Input hints: Display logical keys#2347
Conversation
|
Needs rebase; conflicts due to #2331. Also needs testing on the web platform. |
e6b4629 to
f59fb44
Compare
|
Play this branch at https://play.threadbare.game/branches/endlessm/wjt/rewrite-input-hud-again/. (This launches the game from the start, not directly at the change(s) in this pull request.) |
f59fb44 to
f26ff0e
Compare
f26ff0e to
694c8a5
Compare
694c8a5 to
e8f795b
Compare
Note that as explained in the commit message, the physical-to-logical mapping does not work on the web. So on the web we can only test for regressions. On desktop, one must reconfigure one's keyboard to e.g. QWERTZ, AZERTY, Dvorak. I will attach some screenshots of the latter... |
|
Ah I remember now - in commit c17ffac the keyboard and mouse atlas was edited to remove the solid grey region directly above that mouse glyph. I'm going to see if I can fix it in the shader. Update: I couldn't. It's easier to carry forward the change to the atlas! |
e8f795b to
d95ca0e
Compare
Our keybindings are, correctly, configured to use the physical location of keys on the keyboard. This is because WASD are chosen to be the same shape as the arrow keys, not because "W" means up; and so other bindings such as Z and X must also be physical to avoid clashing. However this means the labels are wrong for QWERTZ and AZERTY keyboards: | Action | QWERTY | QWERTZ | AZERTY | | ------ | ------ | ------ | ------ | | Up | W | W | Z | | Left | A | A | Q | | Down | S | S | S | | Right | D | D | D | | Throw | X | X | X | | Repel | Z | Y | W | We don't actually show WASD in the UI, but we do show Z for Repel (for example). Rather than hardcoding that the glyph for Repel is Z on a keyboard and L1 on a gamepad, instead look these up dynamically. Update to Kenney Input Prompts 1.5, carrying forward the change from commit c17ffac to remove the solid grey region in the top-right of the keyboard-and-mouse atlas. Apply the same change to the joypad spritesheets: the Switch stick glyphs would have the same problem, but we've never tested them! Among other things, this update introduces a glyph for the "Pause" key on a keyboard, which we do actually have a binding for (though it's not the primary so is not shown). Only check in the five atlases sheets that we actually use – keyboard, switch, steamdeck, xbox, playstation – along with the accompanying XML file. Change the button resources to be based on the `Key`/`JoyButton`/etc. enums. Rewrite the importer accordingly to generate just one resource per device type (which contains all the necessary `AtlasTexture`s) rather than hundreds of separate `AtlasTexture` resources. (It was my idea to do the latter; I was wrong!) Then in the input hint controls, determine which key/button the event is bound to, and display that. In theory this will mean that we never have to manually look up the glyphs for a new action again: interact_input.gd will do that for us. On the web, `DisplayServer.keyboard_get_keycode_from_physical()` is not implemented: it is not possible to ask the browser for the logical keycode corresponding to a physical keycode. So on the web platform we always show 'Z' even on an AZERTY keyboard where it should be 'W'. A bit sad but not a regression. There is a web API for this, but it is not widely supported, is blocked in some browsers for anti-fingerprinting reasons, and Godot doesn't (yet) know how to use it. https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap I considered working around this limitation, either by calling `getLayoutMap` directly, or by inferring the keyboard layout based on the events we actually receive: i.e. if we receive an InputEventKey whose keycode is 'z' but physical keycode is 'w', we can infer that the layout is almost certainly AZERTY. This does work – which makes me think that Brave blocking `getLayoutMap()` is probably not improving user privacy! – but I don't think it's worth the complexity. There are some other annoying edge cases to handle, including: - R2 is analogue so is represented in Godot as an axis that can never be negative, but we present it as a button. - The arrow keys want to be treated like a joystick in the UI. - We show mouse + keyboard controls for some actions, so we need to keep the keyboard vs mouse branching. - Sony helpfully relabelled the Start/Select buttons from PS3 (and older) to something else in PS4 and something else again in PS5. I chose the PS5 glyphs because they don't have any text on them. If we wanted to fix this in future, we could ask InputHelper for the "granular" identifier for the joypad and handle the three PlayStation generations separately. Resolves #2185
d95ca0e to
9ce36fd
Compare
manuq
left a comment
There was a problem hiding this comment.
This is impressive, how you managed to overcome the corner cases. I thought this needed more work on the addon side, but I only see nathanhoad/godot_input_helper#88 upstreamed.
Unfortunately I don't have a keyboard to test it, but I can confirm that it doesn't regress in my case. The screen captures you've posted are amazing.
I don't have anything to comment this time on the scripting side.
| # Try to show the logical label for physical mappings; i.e. on AZERTY | ||
| # show W when the physical binding is for Z. As of Godot 4.6, this API | ||
| # is only implemented on X11/Wayland/Mac/Windows; notably it is not | ||
| # available on the Web port. |
| @export var neutral: Texture2D | ||
| @export var negative: Texture2D | ||
| @export var positive: Texture2D |
| ## L2/R2 (aka LT/RT or ZL/ZR) are technically axes, but we treat them as buttons. | ||
| @export var triggers: Dictionary[JoyAxis, Texture2D] |



Our keybindings are, correctly, configured to use the physical location
of keys on the keyboard. This is because WASD are chosen to be the same
shape as the arrow keys, not because "W" means up; and so other bindings
such as Z and X must also be physical to avoid clashing.
However this means the labels are wrong for QWERTZ and AZERTY keyboards:
We don't actually show WASD in the UI, but we do show Z for Repel (for
example).
Rather than hardcoding that the glyph for Repel is Z on a keyboard and
L1 on a gamepad, instead look these up dynamically.
Update to Kenney Input Prompts 1.5, carrying forward the change from
commit c17ffac to remove the solid grey
region in the top-right of the keyboard-and-mouse atlas. Apply the same
change to the joypad spritesheets: the Switch stick glyphs would have
the same problem, but we've never tested them!
Among other things, this update introduces a glyph for the "Pause" key
on a keyboard, which we do actually have a binding for (though it's not
the primary so is not shown). Only check in the five atlases sheets that
we actually use – keyboard, switch, steamdeck, xbox, playstation – along
with the accompanying XML file.
Change the button resources to be based on the
Key/JoyButton/etc.enums. Rewrite the importer accordingly to generate just one resource
per device type (which contains all the necessary
AtlasTextures)rather than hundreds of separate
AtlasTextureresources. (It was myidea to do the latter; I was wrong!)
Then in the input hint controls, determine which key/button the event is
bound to, and display that.
In theory this will mean that we never have to manually look up the
glyphs for a new action again: interact_input.gd will do that for us.
On the web,
DisplayServer.keyboard_get_keycode_from_physical()is notimplemented: it is not possible to ask the browser for the logical
keycode corresponding to a physical keycode. So on the web platform we
always show 'Z' even on an AZERTY keyboard where it should be 'W'. A bit
sad but not a regression.
There is a web API for this, but it is not widely supported, is blocked
in some browsers for anti-fingerprinting reasons, and Godot doesn't
(yet) know how to use it.
https://developer.mozilla.org/en-US/docs/Web/API/Keyboard/getLayoutMap
I considered working around this limitation, either by calling
getLayoutMapdirectly, or by inferring the keyboard layout based onthe events we actually receive: i.e. if we receive an InputEventKey
whose keycode is 'z' but physical keycode is 'w', we can infer that the
layout is almost certainly AZERTY. This does work – which makes me think
that Brave blocking
getLayoutMap()is probably not improving userprivacy! – but I don't think it's worth the complexity.
There are some other annoying edge cases to handle, including:
R2 is analogue so is represented in Godot as an axis that can never be
negative, but we present it as a button.
The arrow keys want to be treated like a joystick in the UI.
We show mouse + keyboard controls for some actions, so we need to keep
the keyboard vs mouse branching.
Sony helpfully relabelled the Start/Select buttons from PS3 (and
older) to something else in PS4 and something else again in PS5. I
chose the PS5 glyphs because they don't have any text on them. If we
wanted to fix this in future, we could ask InputHelper for the
"granular" identifier for the joypad and handle the three PlayStation
generations separately.
Resolves #2185