-
-
Notifications
You must be signed in to change notification settings - Fork 21.9k
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
Support Input and UI for multiple players #102412
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 |
I know about the plugin you mentioned, but honestly I didn't look into it, so I'm not sure about the differences. This PR is solely based on the proposal mentioned in the top comment. Anyway, as I explainied better in the last edits, this PR addresses both having a new If you want examples you can check out the test project i linked in the top PR comment 😄 If you could test this PR it would be awesome, as there are a lot of stuff I may be missing here and there. |
Great question! At a high level, this proposal/PR aims to achieve two things:
In summary, this goes beyond what an addon can achieve by trying to achieve a cohesive, native solution for local multiplayer. You can see this in action in the PR description, but work is still in progress so don't have examples ready at this time. Edit: Oops - I didn't see @GreenCrowDev already responded! |
b5d97a8
to
c5eff31
Compare
The PR is almost complete, but there are a couple of missing pieces:
Sorry if this is not intended behaviour, but I'm marking this "ready for review" so that someone can start to take a look at this PR and give some starting feedbacks. |
ceea85a
to
ffb4894
Compare
I opened a PR for the documentation in case this one will be accepted, so that we can also discuss there how to best present the new features to the devs. It will also serve as a test to see if the new improvements are easy to explain and understand. |
ffb4894
to
8eb91c9
Compare
e81714a
to
d0d74ed
Compare
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 |
d0d74ed
to
0078ae2
Compare
0078ae2
to
e0d13f0
Compare
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