addon for remapping inputs
| -rw-r--r-- | Test.tscn | 25 | ||||
| -rw-r--r-- | addons/remap/ActionLabel.gd | 41 | ||||
| -rw-r--r-- | addons/remap/RemapButton.gd | 33 | ||||
| -rw-r--r-- | addons/remap/private/ActionIcon.gd | 21 | ||||
| -rw-r--r-- | addons/remap/private/ActionIcons.gd | 54 | ||||
| -rw-r--r-- | addons/remap/private/IconMap.gd | 35 | ||||
| -rw-r--r-- | project.godot | 14 |
7 files changed, 153 insertions, 70 deletions
@@ -12,28 +12,33 @@ grow_horizontal = 2 grow_vertical = 2 theme = ExtResource("1_cy8qq") -[node name="v" type="VBoxContainer" parent="."] +[node name="v" type="GridContainer" parent="."] layout_mode = 2 -offset_left = 576.0 -offset_top = 322.0 -offset_right = 576.0 -offset_bottom = 326.0 +offset_left = 474.0 +offset_top = 274.0 +offset_right = 678.0 +offset_bottom = 374.0 +columns = 5 [node name="actionlabel" type="HBoxContainer" parent="v"] +custom_minimum_size = Vector2(100, 100) layout_mode = 2 +offset_right = 100.0 +offset_bottom = 100.0 script = ExtResource("4") _name = "left" -action = "ui_left" +action = "input" icon_size = Vector2(80, 80) font_size = 50 -continuous_updating = true [node name="remapbutton" type="HBoxContainer" parent="v"] +custom_minimum_size = Vector2(100, 100) layout_mode = 2 -offset_top = 4.0 -offset_bottom = 4.0 +offset_left = 104.0 +offset_right = 204.0 +offset_bottom = 100.0 script = ExtResource("2_dj1bu") _name = "left" -action = "ui_left" +action = "input" icon_size = Vector2(80, 80) font_size = 50 diff --git a/addons/remap/ActionLabel.gd b/addons/remap/ActionLabel.gd index b4b9fa5..2d7ebe4 100644 --- a/addons/remap/ActionLabel.gd +++ b/addons/remap/ActionLabel.gd @@ -19,7 +19,7 @@ const SaveLoadUtils := preload("./private/SaveLoadUtils.gd") ## The font to use. Only change if your font has the [url=https://github.com/Shinmera/promptfont/blob/c27797b49dee560e3ea3eaa40e87f9a7f35e8913/glyphs.json]necessary glyphs[/url]. @export var font: Font = preload("./PromptFont.ttf") -## The font size +## The font size. See also [member font] @export var font_size: int = 16 ## Wether to update continuously. Usefull if you have RemapButtons on this action. @@ -37,28 +37,25 @@ var name_label_bg := PanelContainer.new() func _ready() -> void: - assert(font != null) - set_process(continuous_updating) - DirAccess.make_dir_absolute(SaveLoadUtils.dir) - SaveLoadUtils.load_action_to_inputmap(action) - add_child(name_label_bg) - name_label_bg.add_child(name_label) - name_label.text = _name - name_label.custom_minimum_size = icon_size - name_label.vertical_alignment = ALIGNMENT_CENTER - name_label.horizontal_alignment = ALIGNMENT_CENTER - var spacer := Control.new() - spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL - icons = ActionIcons.new(action, icon_size, font, font_size) - add_child(spacer) - add_child(icons) - if not continuous_updating: - update() + assert(font != null) + DirAccess.make_dir_absolute(SaveLoadUtils.dir) + SaveLoadUtils.load_action_to_inputmap(action) + add_child(name_label_bg) + name_label_bg.add_child(name_label) + name_label.text = _name + name_label.custom_minimum_size = icon_size + name_label.vertical_alignment = ALIGNMENT_CENTER + name_label.horizontal_alignment = ALIGNMENT_CENTER + var spacer := Control.new() + spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL + icons = ActionIcons.new(action, icon_size, font, font_size, continuous_updating) + add_child(spacer) + add_child(icons) -func _process(_delta: float) -> void: - update() - ## Updates the icon visuals. func update() -> void: - icons.update()
\ No newline at end of file + if continuous_updating: + push_error("Continuous updating set, manually calling update() pointless.") + return + icons.update(true) diff --git a/addons/remap/RemapButton.gd b/addons/remap/RemapButton.gd index 688cd81..1d75e3f 100644 --- a/addons/remap/RemapButton.gd +++ b/addons/remap/RemapButton.gd @@ -25,18 +25,27 @@ const SaveLoadUtils := preload("./private/SaveLoadUtils.gd") ## The minimum ActionIcon size. @export var icon_size := Vector2(20, 20) -## The font size -@export var font_size: int = 16 - ## The font to use. Only change if your font has the [url=https://github.com/Shinmera/promptfont/blob/c27797b49dee560e3ea3eaa40e87f9a7f35e8913/glyphs.json]necessary glyphs[/url]. @export var font: Font = preload("./PromptFont.ttf") +## The font size. See also [member font] +@export var font_size: int = 16 + ## Wether to update continuously. ## Usefull if you have multple RemapButtons following this action. ## If you need this for a different reason, eg resetting all inputs to the default ([code]InputMap.load_from_project_settings[/code]), ## it is more efficient to manually call [method update] and [method save]. @export var continuous_updating := false + +## The device the input is mapped to. Set to -2 to disable. +## -1 = all devices = player1 maps button a to jump. player2 presses a and player1 jumps. (good for singleplayer) +## 0 = device 0. player1 maps button a to jump, player0 presses a and player0 jumps. (good when you explicitly state which player its for) +## -2 = player1 maps button a to jump, player1 presses jump and player1 jumps. +## Make sure that you set your inputs in the editor `InputMap` correctly, or players will be able to have two icons of the same type on one action. +@export var device := -2 + + ## The internal ActionIcons object. This is a required internal node. var icons: ActionIcons = null @@ -48,7 +57,6 @@ var clear: Button func _ready() -> void: assert(font != null) - set_process(continuous_updating) set_process_input(false) custom_minimum_size = icon_size DirAccess.make_dir_absolute(SaveLoadUtils.dir) @@ -69,12 +77,10 @@ func _ready() -> void: button.pressed.connect(_pressed) var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL - icons = ActionIcons.new(action, icon_size, font, font_size) + icons = ActionIcons.new(action, icon_size, font, font_size, continuous_updating) add_child(button) add_child(spacer) add_child(icons) - if not continuous_updating: - update() func _pressed(): button.text = prompt_text @@ -99,6 +105,8 @@ func _input(event: InputEvent) -> void: if event is InputEventJoypadMotion: event.axis_value = sign(event.axis_value) get_viewport().set_input_as_handled() + if device != -2: + event.device = device RemapUtilities.add_action(action, event) if not continuous_updating: update() @@ -109,13 +117,11 @@ func _input(event: InputEvent) -> void: ## Clears the mappings for this action. func clear_mappings(): RemapUtilities.clear_mappings(action) - icons.update() + if not continuous_updating: + icons.update() SaveLoadUtils.action_to_file(action) clear.hide() -func _process(_delta: float) -> void: - update() - ## Saves the rebind data to a file. Only necessary if manually changing the [InputMap]. func save(): SaveLoadUtils.action_to_file(action) @@ -123,4 +129,7 @@ func save(): ## Updates the icon visuals. func update() -> void: - icons.update() + if continuous_updating: + push_error("Continuous updating set, manually calling update() pointless.") + return + icons.update(true) diff --git a/addons/remap/private/ActionIcon.gd b/addons/remap/private/ActionIcon.gd index 057ff12..2761aff 100644 --- a/addons/remap/private/ActionIcon.gd +++ b/addons/remap/private/ActionIcon.gd @@ -6,11 +6,16 @@ extends PanelContainer ## The inner label. var label := Label.new() -func _init(text: String, min_size: Vector2, font: Font, font_size: int) -> void: - label.horizontal_alignment = BoxContainer.ALIGNMENT_CENTER - label.vertical_alignment = BoxContainer.ALIGNMENT_CENTER - add_child(label) - label.custom_minimum_size = min_size - label.text = text - label.add_theme_font_override("font", font) - label.add_theme_font_size_override("font_size", font_size) + +func _init(min_size: Vector2, font: Font, font_size: int) -> void: + label.horizontal_alignment = BoxContainer.ALIGNMENT_CENTER + label.vertical_alignment = BoxContainer.ALIGNMENT_CENTER + add_child(label) + label.custom_minimum_size = min_size + label.add_theme_font_override("font", font) + label.add_theme_font_size_override("font_size", font_size) + + +## Sets the text of the inner label. +func set_text(text: String): + label.text = text diff --git a/addons/remap/private/ActionIcons.gd b/addons/remap/private/ActionIcons.gd index 03ba1a2..32c5f11 100644 --- a/addons/remap/private/ActionIcons.gd +++ b/addons/remap/private/ActionIcons.gd @@ -12,20 +12,58 @@ var action: String ## The minimum icon size. var min_size := Vector2(20, 20) -## The font to use +## The font to use. var font: Font + +## Font size. var font_size: int -func _init(p_action: String, p_min_size: Vector2, p_font: Font, p_font_size: int) -> void: +## The last result of [method Input.action_get_events]. +var last_acts: Array[InputEvent] + +## Updating continuously. +var continuous_updating: bool + +## Cache the currently connected joypads +var current_connected_joypads_type := IconMap.get_connected_joypads_type() + +func _init(p_action: String, p_min_size: Vector2, p_font: Font, p_font_size: int, p_continuous_updating) -> void: action = p_action min_size = p_min_size font = p_font font_size = p_font_size + continuous_updating = p_continuous_updating + if continuous_updating: + return + set_process(false) + Input.joy_connection_changed.connect( + func(_device: int, _connected: bool) -> void: + current_connected_joypads_type = IconMap.get_connected_joypads_type() + update(true) + ) + update() + + +func _process(_delta: float) -> void: + update() ## Updates the icons to the latest inputs. -func update(): - for i in get_children(): - i.queue_free() - for e in InputMap.action_get_events(action): - var icon := IconMap.get_icon(e) - add_child(ActionIcon.new(icon, min_size, font, font_size)) +func update(force := false): + var acts := InputMap.action_get_events(action) + if force or acts != last_acts: + last_acts = acts + + # make sure we had enough children + var child_diff := acts.size() - get_child_count() + while child_diff < 0: + # kill our children if we had too many + get_child(child_diff).queue_free() + child_diff += 1 + while child_diff > 0: + # bear new children if we dont have enough + add_child(ActionIcon.new(min_size, font, font_size)) + child_diff -= 1 + + # set the children + for i in acts.size(): + get_child(i).set_text(IconMap.get_icon(acts[i], current_connected_joypads_type)) diff --git a/addons/remap/private/IconMap.gd b/addons/remap/private/IconMap.gd index dc561d4..b25ab29 100644 --- a/addons/remap/private/IconMap.gd +++ b/addons/remap/private/IconMap.gd @@ -77,9 +77,9 @@ const GENERIC_BUTTON_MAP := { JOY_BUTTON_B: "↦", JOY_BUTTON_X: "↤", JOY_BUTTON_Y: "↥", - JOY_BUTTON_BACK: "❓", - JOY_BUTTON_GUIDE: "❓", - JOY_BUTTON_START: "❓", + JOY_BUTTON_BACK: "back", + JOY_BUTTON_GUIDE: "guide", + JOY_BUTTON_START: "start", } ## Xbox button map. @@ -176,15 +176,40 @@ static func get_device(raw_name: String) -> int: return PADS.GENERIC +## Puts all the connected joypads into a array. ifInput.get_connected_joypads() == [0, 1, 2], this will return [[constant GENERIC], [constant XBOX], [constant XBOX]]. +## See also [method get_connected_joypads_type]. +static func get_connected_joypads() -> PackedInt32Array: + var arr: PackedInt32Array = [] + for device in Input.get_connected_joypads(): + var raw_name := Input.get_joy_name(device) + if raw_name == "HTIX5288:00 0911:5288 Touchpad": + continue + arr.append(get_device(raw_name)) + return arr + + +## Looks at all the connected joypads, and checks if their all the same type. +## If so, returns that type. +## Otherwise, returns PADS.[constant GENERIC]. +static func get_connected_joypads_type() -> int: + var pads := get_connected_joypads() + if pads.size() == 0: + return PADS.GENERIC + for pad in pads: + if pad != pads[0]: + return PADS.GENERIC + return pads[0] + + ## Gets the icon for a input[param e]vent. Returns [code]?[/code] on failure. -static func get_icon(e: InputEvent) -> String: +static func get_icon(e: InputEvent, connected_joypads_type := get_connected_joypads_type()) -> String: if e is InputEventKey: var res := check_key(e.keycode, e.physical_keycode) if res: return res elif e is InputEventJoypadButton: if e.button_index < 7: - match get_device(Input.get_joy_name(e.device)): + match connected_joypads_type: PADS.XBOX: return XBOX_BUTTON_MAP[e.button_index] PADS.PLAYSTATION: diff --git a/project.godot b/project.godot index 99e5261..e8a4ed5 100644 --- a/project.godot +++ b/project.godot @@ -31,12 +31,16 @@ window/size/test_height=720 [input] -ui_left={ +input={ "deadzone": 0.5, -"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":3,"axis_value":-1.0,"script":null) -, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"unicode":0,"echo":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":66,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":true,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":65,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194317,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194319,"physical_keycode":0,"unicode":0,"echo":false,"script":null) +, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":7,"pressure":0.0,"pressed":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":false,"script":null) ] } |