this post was submitted on 05 Sep 2023
13 points (100.0% liked)

Godot

5888 readers
54 users here now

Welcome to the programming.dev Godot community!

This is a place where you can discuss about anything relating to the Godot game engine. Feel free to ask questions, post tutorials, show off your godot game, etc.

Make sure to follow the Godot CoC while chatting

We have a matrix room that can be used for chatting with other members of the community here

Links

Other Communities

Rules

We have a four strike system in this community where you get warned the first time you break a rule, then given a week ban, then given a year ban, then a permanent ban. Certain actions may bypass this and go straight to permanent ban if severe enough and done with malicious intent

Wormhole

!roguelikedev@programming.dev

Credits

founded 1 year ago
MODERATORS
 

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])

you are viewing a single comment's thread
view the rest of the comments
[–] ICastFist@programming.dev 2 points 1 year ago* (last edited 1 year 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