Remapper les touches
Cliquez une touche, appuyez sur une autre, et l'action déménage. Voler la touche d'une autre action laisse celle-ci sans raccourci, c'est le cas qu'un écran de remappage doit gérer.
L'éditeur ci-dessus fait tourner ce projet. Changez une ligne et il se recharge.
Classes Godot utilisées
InputMapInputEventKeyButtonGridContainer
Le code
scripts/main.gd
extends Node
const ACTIONS: Array[Dictionary] = [
{action = &'rb_left', label = 'Move left', key = KEY_A},
{action = &'rb_right', label = 'Move right', key = KEY_D},
{action = &'rb_jump', label = 'Jump', key = KEY_SPACE},
{action = &'rb_fire', label = 'Fire', key = KEY_J},
]
var buttons: Array[Button] = []
var lights: Array[StyleBoxFlat] = []
var status: Label
var listening: int = -1
func _ready() -> void:
var column: VBoxContainer = _root()
var title: Label = Label.new()
title.text = 'Key rebinding'
title.add_theme_font_size_override('font_size', 28)
column.add_child(title)
var hint: Label = Label.new()
hint.text = 'click a key to rebind it, then press the new one, escape cancels'
hint.add_theme_color_override('font_color', Color('#9aa3b5'))
column.add_child(hint)
var grid: GridContainer = GridContainer.new()
grid.columns = 3
grid.add_theme_constant_override('h_separation', 18)
grid.add_theme_constant_override('v_separation', 12)
column.add_child(grid)
for i in ACTIONS.size():
_register(ACTIONS[i].action, ACTIONS[i].key)
var label: Label = Label.new()
label.text = ACTIONS[i].label
label.custom_minimum_size = Vector2(160, 0)
grid.add_child(label)
var button: Button = Button.new()
button.custom_minimum_size = Vector2(190, 44)
button.pressed.connect(_listen.bind(i))
grid.add_child(button)
buttons.append(button)
grid.add_child(_light())
_refresh(i)
status = Label.new()
status.text = 'holding a bound key lights its dot'
status.add_theme_color_override('font_color', Color('#8eef97'))
column.add_child(status)
print('click a key button and press another key, InputMap keeps the change while the scene runs')
func _process(_delta: float) -> void:
for i in ACTIONS.size():
var held: bool = listening < 0 and Input.is_action_pressed(ACTIONS[i].action)
lights[i].bg_color = Color('#8eef97') if held else Color('#2a2f3d')
func _input(event: InputEvent) -> void:
if listening < 0 or not (event is InputEventKey):
return
var key: InputEventKey = event
if not key.pressed or key.echo:
return
get_viewport().set_input_as_handled()
if key.keycode == KEY_ESCAPE:
status.text = 'rebind cancelled'
else:
_bind(listening, key.keycode)
_refresh(listening)
listening = -1
func _listen(index: int) -> void:
if listening >= 0:
_refresh(listening)
listening = index
buttons[index].text = 'press a key'
buttons[index].release_focus()
status.text = 'waiting for a key for %s' % ACTIONS[index].label
func _bind(index: int, keycode: Key) -> void:
var taken: int = _owner_of(keycode)
var note: String = ''
if taken >= 0 and taken != index:
InputMap.action_erase_events(ACTIONS[taken].action)
_refresh(taken)
note = ', %s is unbound now' % ACTIONS[taken].label
InputMap.action_erase_events(ACTIONS[index].action)
var event: InputEventKey = InputEventKey.new()
event.keycode = keycode
InputMap.action_add_event(ACTIONS[index].action, event)
status.text = '%s is %s%s' % [ACTIONS[index].label, OS.get_keycode_string(keycode), note]
func _owner_of(keycode: Key) -> int:
for i in ACTIONS.size():
for event in InputMap.action_get_events(ACTIONS[i].action):
if event is InputEventKey and (event as InputEventKey).keycode == keycode:
return i
return -1
func _register(action: StringName, keycode: Key) -> void:
if InputMap.has_action(action):
InputMap.erase_action(action)
InputMap.add_action(action)
var event: InputEventKey = InputEventKey.new()
event.keycode = keycode
InputMap.action_add_event(action, event)
func _refresh(index: int) -> void:
buttons[index].text = _key_text(ACTIONS[index].action)
func _key_text(action: StringName) -> String:
for event in InputMap.action_get_events(action):
if event is InputEventKey:
return OS.get_keycode_string((event as InputEventKey).keycode)
return 'unbound'
func _light() -> Panel:
var style: StyleBoxFlat = StyleBoxFlat.new()
style.bg_color = Color('#2a2f3d')
style.set_corner_radius_all(11)
lights.append(style)
var dot: Panel = Panel.new()
dot.custom_minimum_size = Vector2(22, 22)
dot.size_flags_vertical = Control.SIZE_SHRINK_CENTER
dot.add_theme_stylebox_override('panel', style)
dot.mouse_filter = Control.MOUSE_FILTER_IGNORE
return dot
func _root() -> VBoxContainer:
var background: ColorRect = ColorRect.new()
background.color = Color('#12141a')
add_child(background)
background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var center: CenterContainer = CenterContainer.new()
add_child(center)
center.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
var column: VBoxContainer = VBoxContainer.new()
column.add_theme_constant_override('separation', 20)
center.add_child(column)
return column