godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.08k stars 69 forks source link

Add `Input.get_joy_type()` to get controller type #8519

Open timothyqiu opened 7 months ago

timothyqiu commented 7 months ago

Describe the project you are working on

Platformer.

Describe the problem or limitation you are having in your project

When interaction is available, an interaction key icon is shown above the player.

I want it to display A when Switch controller is used, display B when XBox controller is used, and display Circle when PlayStation controller is used. Otherwise defaults to a generic icon (highlighted east key among the four keys).

The only way to distinguish between these controller types seems to be Input.get_joy_name().

Describe the feature / enhancement and how it helps to overcome the problem or limitation

Add Input.get_joy_type() that returns either:

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Use something like this mapping: https://github.com/libsdl-org/SDL/blob/main/src/joystick/controller_list.h

But I'm not sure what the CONTROLLER_ID is in Godot.

If this enhancement will not be used often, can it be worked around with a few lines of script?

Partially. Using good old way of parsing Input.get_joy_name() can detect some controllers.

Controller icon libraries using this method already exist in AssetLib. But how they parse the name differ and are quite basic (e.g. not able to detect common non-official controllers).

Is there a reason why this should be core and not an add-on in the asset library?

Parsing names are not reliable. Probably needs information that is not exposed.

KoBeWi commented 7 months ago

I want it to display A when Switch controller is used, display B when XBox controller is used, and display Circle when PlayStation controller is used.

These controllers have easily recognizable names when used with get_joy_name(). I don't know how else the function you propose would be implemented.

Which means that you can use this code: https://github.com/KoBeWi/Godot-Action-Icon/blob/13406c2e23eb8f1f4637f6b6d5e93398d0676533/addons/ActionIcon/ActionIcon.gd#L353-L364

timothyqiu commented 7 months ago

I don't think it's that easy.

Here is another plugin that tries to recognize controller type by name: https://github.com/rsubtil/controller_icons/blob/38d66dd664840c7b7148533dcc67e336da886073/addons/controller_icons/Mapper.gd#L35

Godot recognizes 405[^1] controllers. You guys disagree with each other for at least 45 of them if unknown result returned from the latter is treated as XBox. Otherwise, you only reach an agreement for 11% of these controllers.

And both of these two plugins recognize my "8BitDo SN30 Pro", which is in the list of controllers recognized by Godot, as an XBox controller, but it's in fact a Nintendo controller.

No matter whether name parsing is the only way to implement this:

[^1]: As listed in gamecontrollerdb.txt and godotcontrollerdb.txt.

Calinou commented 7 months ago

Indeed, many 3rd-party controller brands like SCUF feature layouts that are 100% compatible with a standard controller type (Xbox in SCUF's case). However, I don't think gamecontrollerdb.txt stores exact information on this.

Also, it begs the question of how accurate the controller type should be. Should it report the precise generation (e.g. distinguish PS4 and PS5[^1], Xbox 360 and Xbox One/Series[^2])? These should preferably have different button prompt icons to best match the actual controller.

[^1]: Buttons are colored on PS4, not on PS5. [^2]: ABXY button colors are different.

stephanbogner commented 6 months ago

I came to the same conclusion (@timothyqiu) that it's not as trivial. A while ago I opened a discussion on a related topic.

A few remarks:

1.) Interplay between InputMap, Input and Action

Another argument why this should be core is that InputMap, Input (Device) and Action need to work together here:

  1. Show an image of the correct controller (requires the player's current input device)
  2. Show the correct input prompt to the user (requires the player's current input device, the required action and the input map configuration)

2.) joy_type also for joypads, not just input:

get_joy_type() should exist as well for joypads → Allows you to show an appropriate icon / logo for a connected joypad

3.) Additonals details, not just type

The returned value could also be a class that contains more details. Something like:

→ If you only want to differentiate between Playstation, Xbox and Nintendo the family is enough. If you want to be more precise, you can use the other values. (Related to what @Calinou wrote)

4.) All lowercase vs. common writing

I am not sure yet but it might be good to store values how they are normally/officially written: Joy-Con vs. joycon → Would allow you to display information to the player in how they commonly read it

5.) Get the button name

Some functionality should exist to get the button's name. It could be based on Input but more likely it would be should be based on InputMap by joy_type/platform.

Note: Actions can be bound to multiple buttons so this would likely be an array, so take it as a simplification

→ This would allow to display correct input prompts even without images

I currently have it the other way round where I have images called lower_action_button.png which are then different for each platform.

6.) Sticks

It gets even more complicated when it comes to sticks because 1 stick is normally split into up, down, left, right. This makes sense because you can bind it how you want (especially on PC and regarding a11y) but makes things more complicated when it comes to showing input prompts.

Final thoughts

Rubonnek commented 2 months ago

But I'm not sure what the CONTROLLER_ID is in Godot.

Turns out it's currently possible to extract the vendor id (nVID) and the product id (nPID) but only in Linux with Godot:

    var dict : Dictionary = Input.get_joy_info(0)
    print("nVID: 0x%04x" % dict["vendor_id"])
    print("nPID: 0x%04x" % dict["product_id"])

In Windows this is not currently exposed, but I believe those values are already extracted in joypad_windows.cpp. Seems like it's a matter of passing those values through the joy_connection_changed call through a dictionary for consistency.