godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.17k stars 98 forks source link

Dealing with local multiplayer input mapping #10887

Open groud opened 1 month ago

groud commented 1 month ago

Describe the project you are working on

Godot

Describe the problem or limitation you are having in your project

Dealing with local multiplayer is a bit complex in Godot right now. The common approach is to define several input actions, a set for each player, with a suffix for each player ID. Then you need to assign each actions a dedicated event with a different device ID (if you have several gamepad). Here is an example action list:

# Player 1
jump_0 -> Keyboard space 
crouch_0 -> Keyboard ctrl
up_0 -> Keyboard up key
....

# Player 2
jump_1 -> Gamepad with device ID 0 - X button
crouch_1 ->  -> Gamepad with device ID 0 - B button
up_1  -> Gamepad with device ID 0 - button
....

# Player 3
jump_2 -> Gamepad with device ID 1 - X button
crouch_2 ->  -> Gamepad with device ID 1 - B button
up_2  -> Gamepad with device ID 1 - button
....

It is quite complex to setup, makes it hard to update / remap action for different players.

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

To solve the problem, I am thinking about grouping actions and their mapped events per player. So, in the editor UI, you would have a dropdown menu at the top to switch the current user (by default 0). You could then assign for each player different events.

All action-related function (taking an action name as a parameter), should expose an additional, optional argument to select a player different from the player 0. Functions like:

is_action_pressed("action_name", 1) # fetch action for player 1
is_action_just_pressed(...)
is_action_just_released(...)

While that solve the problem of having to suffix all action with a player ID, it's still a struggle to copy-paste mappings with a different device ID (for each gamepad), and it would still be complicated to reassign a set of mapping from one player to another. That's why I would like to introduce an ActionInputMap resource (name TBD). This resource could be created directly out of the editor, by saving a player's mapping to a file. Then the API / editor could also load such a mapping from a file. Lastly, we should have both an API and and a button in the editor to update an ActionInputMap with a specific device ID for gamepad event.

This is an example API for what would be mapping a player as a gamepad:

# Player 2 wants to play with gamepad with ID 1
var mappings_gamepad = load("gamepad_mapping.tres") # Loads an ActionInputMap
mappings_gamepad.set_gamepad_events_device_id(1) # Modify the loaded resources so that all gamepad events match device ID 1
InputMap.load_mappings(2, mappings_gamepad) # New API to replace all player's 2 mapping with the given ActionInputMap

# Player 2 wants to play with keyboard
var mappings_keyboard = load("keyboard_mapping.tres") # Loads another ActionInputMap, no device change needed
InputMap.load_mappings(2, mappings_keyboard) # New API to replace all player's 2 mapping with the given ActionInputMap

# And so on, for all players.

Here is an editor mockup. I called "sheet" a set of action mapped to a player: Copie d'écran_20241003_183326

I believe that, probably, all sheet will display all defined actions (event if some have no event assigned I guess). For copy paste, I think that it would be nice to select actions individually, to have freedom to copy past event from one sheet to another. Also, the dropdown could be tabs too, that would work.

Also, I wonder if we could also define a sheet as "a copy of another one but with an automatic remap". This would allow only modifying one sheet for multiple players easily.

Related proposals / PRs:

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

Yes

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

It's core

yosoyfreeman commented 1 month ago

I just want to add that there is people who is able to play PC games by using two controllers, a controller and a keyboard etc to be able to play the game because of physical limitations. As per most games are single player, i would suggest the player 0 to mean "whatever this input comes from" (Like right now) and use the per character approach when set to something else than 0.