I’ve been looking into some tutorials, mainly this one - https://www.gotut.net/custom-key-bindings-in-godot-4/

My particular problem is that this and every other tutorial of key rebind only deals with 1 action = 1 keybind, while I want my actions to have 3 keybinds: 2 from the keyboard, 1 from a controller. Because of that, I can’t simply empty the InputMap of a given action and add the newly pressed key.

After quite some time thinking and testing, I’ve managed to cobble together this code (based on the one from the link above) that works as I want: it removes the previous keybind and adds the new one, without messing any of the previous keys.

func _input(event):
	if !current_button:
		return
	if event is InputEventKey and $keypress.visible:
		var action: String
		match current_button.name:
			"rebind_jump":
				action = "game_jump"
		#Match repeat for each button/action, also "duplicated" for each of the alternative keys/buttons, as "alt_game_jump", etc
		#The following lines happen after the match defines the action 
		rebind(event,action, current_button.text)
		current_button.text = event.as_text()
		current_button = null


## The rebind function is called by sending the event, a string of the action proper and the button's current text as the "oldkey"
## rebind(event, "game_jump", $rebind_jump.text)
## After the event call, the button's text is set like this: $rebind_jump.text = event.as_text()

func rebind(event: InputEvent, action: String, oldkey:String):
	var existing : Dictionary = {}
	var key: String
	for ia in InputMap.action_get_events(action):
		#The split(" ") below happens because physical key presses are sent as "E (Phyisical)" or "Ctrl (Physical)"
		if ia.as_text().split(" ")[0] != "Joypad":
			key = ia.as_text().split(" ")[0]
		else:
			key = ia.as_text()
		existing[key] = ia
	for acts in InputMap.get_actions():
		#Removes the key from any other events 
		InputMap.action_erase_event(acts, event)
	InputMap.action_erase_event(action, existing[oldkey])
	InputMap.action_add_event(action, event)

While it works as is for keyboard rebind, I haven’t tested it with a controller, as I don’t have one around to test.

But my main question is: is this “ideal”? Is there a better/easier way to do this, rebind an action substituting only one key?

EDIT: Did a few tweaks on the code. One thing that I haven’t figured out yet is a way to update a key that was already bound to another button. For example: if “Ctrl” is bound to jump, and I bind it to attack, it automatically unbinds from jump. If I then try to bind anything to that specific jump button, I’ll get a Invalid Index error at the InputMap.action_erase_event(action, existing[oldkey])

  • I Cast FistOP
    link
    English
    2
    edit-2
    10 months ago

    Instead of another edit, here’s the “final” code i’ve managed to make. If a different button has that keybind, it gets erased. Haven’t managed to cause any errors:

    func rebind(event: InputEvent, action: String, oldkey:String):
    	var existing : Dictionary = {}
    	var key: String
    	for ia in InputMap.action_get_events(action):
    		if ia.as_text().split(" ")[0] != "Joypad":
    			key = ia.as_text().split(" ")[0]
    		else:
    			key = ia.as_text()
    		existing[key] = ia
    	for acts in InputMap.get_actions():
    		#Removes the key from any other events 
    		InputMap.action_erase_event(acts, event)
    	InputMap.action_add_event(action, event)
    	check_existing(event.as_text()) #Removes the keybind if it's present in any other key
    	current_button.text = event.as_text()
    
    func check_existing(event):
    	#This is the Array with all the buttons of "default" keybinds
    	for aa in df_keybinds:
    		if aa.text == event:
    			aa.text = ""
    	#This is the Array with all the buttons of "alternative" keybinds
    	for alt in alt_keybinds:
    		if alt.text == event:
    			alt.text = ""
    
    func _input(event):
    	if !current_button:
    		return
    	if event is InputEventKey and $keypress.visible:
    		var action = ""
    		match current_button.name:
    			"rebind_jump":
    				action = "game_jump"
    			#Other matches...
    		rebind(event,action, current_button.text)
    		$keypress.hide()
    		current_button = null