nathanhoad/godot_input_helper

set_action_key_or_button does not work between gamepad/keyboard

Salvakiya opened this issue · 4 comments

using set_action_key_or_button does not properly swap out joypad/keyboard inputs. Adding exposing "swap_if_taken" to set_action_key_or_button and additionally adding the proper logic to handle the swap between joypad/keyboard would be a possible solution.

Possibly might be better to have a separate optional parameter like "swap_if_taken" called "swap_key_and_button_if_taken" which defaults to false as to not introduce breaking changes.

Godot version 4.1.x

Edit: To clarify, the joy buttons will swap with itself just fine.. but swapping buttons and keyboard inputs dont properly talk to eachother... so setting a joypad button on a spot with a keyboard input wont move the keyboard input to the joypads old location.

after viewing the code... it may be a neat solution to combine the update functions all into one.. since their code is very similar.

here is an untested "unified" version of those _update functions. Also partially supporting mouse/joypad motion as "buttons".

its only a partial solution and may not be the one you want.

# Snap vector2 to 8 directions
func vectorTo8(vec2:Vector2) -> float:
	# 0.0 = E
	# 1.0 = NE
	# 2.0 = N
	# and so on
	var e_dir = atan2(vec2.x,vec2.y)
	return fmod(roundf(8.0 * e_dir / (2.0*PI) + 8.0), 8.0)

func _update_action(
		target_action: StringName,
		actual_event: InputEvent,
		swap_if_taken: bool,
		only_accept_one: bool,
		only_replace_key = "",
		only_replace_button = JOY_BUTTON_INVALID,
		only_replace_mouse = MOUSE_BUTTON_NONE
		) -> Error:
	var clashing_action = ""
	var clashing_event
	if swap_if_taken:
		for action in InputMap.get_actions():
			if action == target_action: continue

			for event in InputMap.action_get_events(action):
				if event.get_class() == actual_event.get_class():
					var _clash = false
					match event.get_class():
						"InputEventKey": _clash = (event.physical_keycode == actual_event.physical_keycode)
						"InputEventMouseButton": _clash = (event.button_index == actual_event.button_index)
						"InputEventMouseMotion": _clash = (vectorTo8(event.velocity) == vectorTo8(actual_event.velocity))
						"InputEventJoypadButton": _clash = (event.button_index == actual_event.button_index)
						"InputEventJoypadMotion": _clash = (event.axis == actual_event.axis) and (sign(event.axis_value) == sign(actual_event.axis_value))
						
					if _clash:
						clashing_action = action
						clashing_event = event
	
	# Find the key based event for the target action
	for event in InputMap.action_get_events(target_action):
		match event.get_class():
			"InputEventKey":
				if only_replace_key != "" and only_replace_key != OS.get_keycode_string(event.physical_keycode) and only_replace_key != event.as_text():
					continue
					
				# Add the current mapping to the clashing action
				if clashing_action:
					InputMap.action_erase_event(clashing_action, clashing_event)
					InputMap.action_add_event(clashing_action, event)
					action_key_changed.emit(clashing_action, OS.get_keycode_string(event.physical_keycode))
				# Remove the current mapping
				InputMap.action_erase_event(target_action, event)

			"InputEventJoyButton":
				if only_replace_button != JOY_BUTTON_INVALID and only_replace_button != actual_event.button_index:
					continue
				
				# Add the current mapping to the clashing action
				if clashing_action:
					InputMap.action_erase_event(clashing_action, clashing_event)
					InputMap.action_add_event(clashing_action, event)
					action_button_changed.emit(clashing_action, event.button_index)
				# Remove the current mapping
				InputMap.action_erase_event(target_action, event)

			"InputEventMouseButton":
				# Add the current mapping to the clashing action
				if clashing_action:
					InputMap.action_erase_event(clashing_action, clashing_event)
					InputMap.action_add_event(clashing_action, event)
					action_mouse_button_changed.emit(clashing_action, event.button_index)
				# Remove the current mapping
				InputMap.action_erase_event(target_action, event)
				

	match actual_event.get_class():
		"InputEventKey":
			# Add the new event to the target action
			var next_event = InputEventKey.new()
			next_event.physical_keycode = OS.find_keycode_from_string(actual_event.physical_keycode)
			InputMap.action_add_event(target_action, next_event)
			action_key_changed.emit(target_action, OS.get_keycode_string(next_event.physical_keycode))
		"InputEventJoyButton":
			var next_event = InputEventJoypadButton.new()
			next_event.button_index = actual_event.button_index
			InputMap.action_add_event(target_action, next_event)
			action_button_changed.emit(target_action, next_event.button_index)
		"InputEventMouseButton":
			var next_event = InputEventMouseButton.new()
			next_event.button_index = actual_event.button_index
			InputMap.action_add_event(target_action, next_event)
			action_mouse_button_changed.emit(target_action, next_event.button_index)

	return OK

That's working as expected. Replacing a joypad button should only be done with another joypad button and same for keyboard. If you're trying to set up something for multiplayer then this addon isn't really meant for that yet.

That's working as expected. Replacing a joypad button should only be done with another joypad button and same for keyboard. If you're trying to set up something for multiplayer then this addon isn't really meant for that yet.

this is not related to multiplayer. only that the function set_action_key_or_button exists and does not behave the same as set_action_key and set_action_button when it comes to swapping inputs around.