-
-
Notifications
You must be signed in to change notification settings - Fork 22.4k
Support Input and UI for multiple players #102412
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Support Input and UI for multiple players #102412
Conversation
@Sauermann can I ask why the "breaks compat" flag was added? We want to implement this feature without breaking compatibility. Is there anything you noted that breaks compat and we need to address? |
I can't see any compatibility breakgage from the code, default arguments are applied and it seems the behavior is equivalent with these |
@AThousandShips Thanks for the explanation. My understanding was, that changing exposed API (even when default parameters are added) is considered |
Thanks for taking this on, @GreenCrowDev! I've left a few comments, but overall this is a great start to the implementation. Are there any parts of this that you would like help with? One other small, low-priority thing that comes to mind: we may want to add an |
Thanks for the comments @coffeebeats! |
dbbc61b
to
e7e8c2f
Compare
e7e8c2f
to
e9d381f
Compare
@coffeebeats sorry to bother you, just a little heads-up since I need some help. In the meantime I'm working on the Also, how would you cleanly approach giving other players control of just some keys of the keyboard (that is generally player 1)? I'm using something like this (seems rough): func _unhandled_key_input(event: InputEvent):
if event is InputEventKey:
# print("Device: %s; Player: %s;" % [event.device, event.get_player()])
manage_ijkl(event)
else:
print(event)
func _input(event: InputEvent):
if event.player == Input.PLAYER_2:
print("Player 2 Input.")
pass
elif event is InputEventAction:
print(event)
pass
func manage_ijkl(event: InputEventKey) -> void:
# Key I pressed.
if event.player != Input.PLAYER_2 && event.keycode == KEY_I && event.is_pressed():
get_viewport().set_input_as_handled()
var p2_event = InputEventAction.new()
p2_event.player = Input.PLAYER_2
p2_event.action = "ui_up"
p2_event.pressed = true
Input.parse_input_event(p2_event) EDIT: I improved the code a little bit to support 4 players on the same keyboard. It's in the test project repo: https://github.com/GreenCrowDev/godot_input_ui_multiplayer_support_test/blob/b8dabd0fffaaab6cf1cf2ddc717ecc93f411b156/input_test.gd |
5cd289c
to
5d77afa
Compare
Is there a way to distinguish focus on different controls, so that different focus outline colors can be used for each player? Also, can controls query which player(s) is focusing them from a script? Additionally, if multiple players focus on the same control, the focus outlines should be modified to display this somehow. This could be done by extending the size of one of the focus outlines, so it surrounds the other outline, or by using dashed lines with alternating colors. This may be difficult to implement though, especially for the dashed lines since StyleBoxFlat doesn't support them currently. |
5d77afa
to
9dca687
Compare
Sorry for the delay in responding - I haven't had much time recently to devote to this. First off, great work so far! The demo video you shared is super exciting!
The code snippet you shared is exactly how I'd approach it. I don't think it's feasible or necessary to allow configuring keyboard sharing ahead of time. Creating A few minor details:
These (minor) points aside, the implementation looks great to me!
I'm thinking through how the device -> player mapping should work right now. I should have some thoughts to share soon! |
I think we'll definitely want to allow Likewise, Unfortunately, as I'm writing this now, I'm seeing some complexity that I missed in the proposal:
|
I yet have to read carefully every point, I'll do that soon, but:
I already implemented this part. In the video it's not apparent, but if you try to focus a control out of the 3x3 buttons grid of each player, it will not be allowed. Not sure if you meant something more advanced. EDIT: Do you mean the case where a |
The core functionalities have been implemented, so it should be ready to review now! 🎉 Testing needed! The latest change was about having the triggering of |
0078ae2
to
e0d13f0
Compare
e0d13f0
to
6f207b5
Compare
Rebased. |
Not sure why yet, but I got a reproduceable crash on my game using it, it seems to be specifically on code from this PR:
The line in question is this one, in calculate_ancestral_player_mask() in control.cpp:
|
I will take a look at it later or tomorrow, but I need to know how to reproduce the error. I don't have a steamdeck, but I don't see why it shouldn't work. There should be a mapping issue somewhere. |
I'll try to find some time to make a MRP. It might take a while as I have a busy week this week. |
b4d1ea4
to
934a1e1
Compare
@DanielSnd I think the crash error should be fixed now. Tomorrow I'll work on the GDExtension compatibility breakage following the docs and fix the CI error. |
32fe928
to
149dcb8
Compare
149dcb8
to
2e520a4
Compare
I implemented compatibility stuff, now CI is fine! |
Hello @Faless, sorry for the ping. |
Hi @adamscott @Repiteo, just wanted to know how we should follow up on the discussion from the Core meeting. Thanks! |
The discussion will happen in the Godot developers chat, on the #input channel. Here's the link: https://chat.godotengine.org/channel/input |
Support Input and UI for multiple players from PR godotengine#102412
Support Input and UI for multiple players from PR godotengine#102412
Support Input and UI for multiple players from PR godotengine#102412
Closes godotengine/godot-proposals#10070.
Goal
The goal of this PR is to improve the engine support for local multiplayer games.
Also, the intent is to implement the new features without breaking compatibility with previous version.
The new logic will be implemented with default arguments when appropriate.
Content
This PR is based on @coffeebeats proposal Improve local multiplayer support by tracking InputEvents and UI focus per player.
The author stated in the discussion:
The two key points that will be addressed are:
player
layer between the device and game logic;player
layer in the UI, allowing multiple players have their ownControl
node in focus.The max amount of players allowed is 8.
Changes
Input
PlayerId
enum class andPlayerMask
enum for bitfield masking purposes.PlayerId
is used when you only want to specify one single player, whilePlayerMask
is used as a bitfield when you want to check conditions for multiple players.Input::is_action_pressed()
and similar now have a new optionalPlayerId
argument that allow to specify for which player you want to check the condition;InputMap
action events binded to joypad devices are now set toALL_DEVICES
by default. This is because now the action itself is going to be triggered by all devices, and will be later filtered by the player id. The possibility of choosing the device id for the event is left incact to allow for custom behaviour, but it is not intended as the normal use case;P1
throughkeyboard_player_id_override
,mouse_player_id_override
,touch_player_id_override
;Joypad
struct inInput
, adding aPlayerId
property in it. This strong coupling seems to work well, since it avoids the complexities of mantaining a synchronization between another map and the number of connected devices, that are very dynamic and real-time defined. Since the device id starts from 0, by default theplayer_id
is assigned to the corresponding device idint
, and seems to follow the common behaviour of newly connected controllers;Input.set_joy_player_id(0, PLAYER_ID_P2)
;UI
Control
has aBitField<PlayerMask> player_mask = PLAYER_ALL;
property, allowing to control which player can focus on it;Add
PROPERTY_HINT_LAYER_PLAYER_MASK
forControl
player_mask
UI editor;Methods like
Control::grab_focus()
and similar now have a new optionalPlayerId
argument that allow to specify for which player you want to apply the function;The method
Control::calculate_ancestral_player_mask()
calculates aBitField<PlayerMask>
composed by the union (& operator) of the masks of the node up to its root parent. This has been introduced so that given a parentContainer
withplayer_mask = PLAYER_2
, all of its children will "indirectly inherit" the player 2 status even if they have theirplayer_mask = PLAYER_ALL
, since the internal logic traverses the parent tree to calculate the combined bitmask. This simplifies the creation of split screen UIs with less properties to modify, and allowing a more advanced control if the dev wants;Specific logic has been added in the
Control
class to calculatefocus_neighbor[]
,focus_next
, andfocus_prev
, considering now there areControl
s assigned to other players that you can't focus. Changing focus with arrow keys and TAB/SHIFT+TAB is preserved and works as expected;You can query which players are focusing a
Control
withControl::get_focused_players_id()
;Test Project
https://github.com/GreenCrowDev/godot_input_ui_multiplayer_support_test
Documentation PR
godotengine/godot-docs#10704
Examples
ui_multiplayer_001.mp4
ui_mp_next_prev.mp4