InputEventKey.as_text_key_label() returns (Unset)

Godot Version

4.4 rc1

Question

I want to implement a menu for input remapping and I want to display the assigned keys in a readable format.

Why does the code in the ready method print (Unset) for every action? When I press the same buttons that are assigned to the attack action, the input function prints them correctly.

Is this a bug?

func _input(event: InputEvent) -> void:
	if event is InputEventKey:
		print(event.as_text_key_label())


func _ready() -> void:
	InputMap.load_from_project_settings()
	
	for action: String in InputMap.get_actions():
		if action.begins_with("ui"):
			continue
		
		for event: InputEvent in InputMap.action_get_events(action):
			if event is InputEventKey:
				print(event.as_text_key_label())

Looking at your code the problem is that InputMap.action_get_events(action) is returning events of different types than what you’re checking for. Your condition if event is InputEventKey: is only handling keyboard events, but the events in your action mapping arent all keyboard events.

In the UI screenshot, I can see that your “attack” action has physical inputs like “A”, “Apostrophe”, etc., which suggests they’re likely InputEventJoypadButton or other non-keyboard input types rather than InputEventKey instances.

Try modifying your code to check for the specific event types or to handle all event types:

func _ready():
    InputMap.load_from_project_settings()
    
    for action: String in InputMap.get_actions():
        if action.begins_with("ui"):
            continue
            
        print("Action: ", action)
        for event: InputEvent in InputMap.action_get_events(action):
            print("  Event type: ", event.get_class())
            if event is InputEventKey:
                print("  Key: ", event.as_text_key_label())
            elif event is InputEventJoypadButton:
                print("  Joypad Button: ", event.button_index)

Thanks a lot for the suggestions. The events are in fact InputEventKey, I’ve added on of your lines to check.

print("Event type: ", event.get_class()) # prints Event type: InputEventKey

We can also just remove the type check and use the code below. It still prints (Unset), so the problem is elsewhere.

func _ready() -> void:
	InputMap.load_from_project_settings()
	
	for action: String in InputMap.get_actions():
		if action.begins_with("ui"):
			continue
		
		for event: InputEvent in InputMap.action_get_events(action):
			print(event.as_text_key_label())

There are three ‘types’ of inputs (Keycode (latin), physical keyboard and unicode). You can specify them in the dropdown at the bottom when specifying the input in the InputMap.
You specified in your InputMap the input as physical key (on a US keyboard) but as_text_key_label returns the Keycode (first type) text.

You could instead

  • Use InputEvent.as_text. This returns the string representation with the ‘type’ information (e.g. Space (Physical))
  • Use InputEventKey.as_text_physical_keycode. Similar to as_text_key_label but instead of the keycode it returns the physical keycode (the one that you have set)
  • Use DisplayServer.keyboard_get_label_from_physical to get the localized label associated with the physical key.
  • Instead of specifying the input on a physics keyboard you could change the type to just Keycode. But then the keys change for different keyboard layouts.

Edit: Added DisplayServer.keyboard_get_label_from_physical

1 Like

Thanks a ton! I did not know about this.

I think this is the solution to my problem. I hope I’m not missing anything.

  1. Configure the input event as Key Label (Unicode, Case-Insensitive)
  2. Use OS.get_keycode_string(event.key_label) to get the correct text

The code below prints this, just as required.

Ü
Y
Ä
func _ready() -> void:
	InputMap.load_from_project_settings()
	
	for action: String in InputMap.get_actions():
		if action.begins_with("ui"):
			continue
		
		for event: InputEvent in InputMap.action_get_events(action):
			if event is InputEventKey:
				print(OS.get_keycode_string(event.key_label)) 

Yes, this looks good. Now the event is associated with the ‘unicode’ character send by the OS. Keep in mind that different keyboard-layouts have the characters at different position.

Btw. I checked the documentation again and OS.get_keycode_string(event.key_label) and event.as_text_key_label() seem to be the same thing (I updated my original answer).

One more thing. DisplayServer has a function to convert a physical-keycode to the label of the current layout: keyboard_get_label_from_physical.
This way you can set the input-event to physical-keyboard (i.e., the key position does not change based on the layout), but you can print the actual label that’s associated with the users keyboard-layout.