addon for remapping inputs
-rw-r--r--Test.tscn25
-rw-r--r--addons/remap/ActionLabel.gd41
-rw-r--r--addons/remap/RemapButton.gd33
-rw-r--r--addons/remap/private/ActionIcon.gd21
-rw-r--r--addons/remap/private/ActionIcons.gd54
-rw-r--r--addons/remap/private/IconMap.gd35
-rw-r--r--project.godot14
7 files changed, 153 insertions, 70 deletions
diff --git a/Test.tscn b/Test.tscn
index 21b81b3..8d9fb14 100644
--- a/Test.tscn
+++ b/Test.tscn
@@ -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)
]
}