-
-
Notifications
You must be signed in to change notification settings - Fork 97
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
Improve local multiplayer support by tracking InputEvent
s and UI focus per player
#10070
Comments
Hi all - I'm very keen on seeing improved support for menu navigation in local multiplayer games. I've looked at a number of issues/PRs related to this topic, and while there are many good ideas/attempts (notably godotengine/godot#29989, #3555, and #4295), I haven't seen a comprehensive proposal yet. I've outlined above what I feel is a pretty complete solution to the problem. Given the scope of this proposal, I felt it was better to open a new issue rather than post this as a comment in #4295. Please let me know if this should be moved into that issue instead. Finally, I'm hopeful that this can help drive discussion towards a concrete proposal that can be implemented; feedback/criticism is more than welcome. If this proposal is approved, I would be happy to work on implementing it if no one else with more experience is interested. |
This proposal seems good. Allowing per-player Control focus is definitely a must. Curious if you can elaborate a bit on what the benefits of the |
Thanks for taking a look! Let me try to better explain the "player layer". Let's start with the problems we're trying to solve:
First, we cannot use device ID to solve For Putting these together, the "player layer" is a bitmask (just like the physics layer!) where each bit represents a specific player in the game. Now, Finally, to make this work seamlessly, Godot needs to know internally how to map from a device type + device ID to a player ID (i.e. one bit of the bitmask), which is why I proposed a new Hopefully that clarifies things somewhat - please let me know if you still have questions. |
Describe the project you are working on
Godot games with support for local multiplayer. I define local multiplayer support as follows:
Describe the problem or limitation you are having in your project
There are two engine limitations that are blocking developers from creating local multiplayer UIs:
Control
node may be focused at a time. As such, developers cannot rely on the built-inControl
node focus feature for multiple players simultaneously.Control
nodes depend on the same UI built-in actions, meaning there's no way for multiple players to separately navigate a UI scene tree (all players would end up triggering the same UI actions).In addition to these limitations, the following is a pain point for local multiplayer games:
InputMap
modifications that must be kept in sync or non-trivial logic to replicate actions as players join/leave. While there are workarounds, this is toil that should be eliminated by the local multiplayer solution.Describe the feature / enhancement and how it helps to overcome the problem or limitation
With the above in mind, I think the following criteria will define a comprehensive solution to the problem:
ui_*
actions as well as custom in-game actions.Input
andInputEvent
should allow for distinguishing between different players.Control
node and (ii) the built-in UI actions used byControl
nodes should be player-aware.Control
should allow for distinguishing between different players.In addition to the above, the following requirements should be met:
Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams
This proposal largely draws on the work @Faless did in godotengine/godot#20091, @reduz's comments in godotengine/godot#20091 (comment), @pouleyKetchoupp's comment in godotengine/godot#29989 (comment), and @redsett's work in godotengine/godot#62421 (along with other comments).
The concept of a "player" layer will be introduced. Implemented as a bitmask, the separate layers identify interactions coming from an independent player. Note that players will usually map 1:1 with devices, but this isn't a hard requirement (consider keyboard sharing or routing both mouse+keyboard and a controller to one player).
The "player" layer will be utilized as follows:
InputEvent
will get a newplayer
property which is the "player" layer that the event is associated with. This can only be a single layer.ProjectSettings
will get new settings which define the mapping from(device type,device index)
to a "player" layer. These values will determine which "player" layer anInputEvent
gets assigned to.In addition to the static mapping, players can also construct events on any layer using
Input.parse_input_event
. To support keyboard sharing, for example, developers could consume events on the keyboard's default "player" layer but then create newInputEventActions
on the desired "player" layers usingInput.parse_input_event
(based on which player the key belongs to).InputMap
actions will now implicitly be defined for each "player" layer. This means that developers do not need to configure actions ahead of time or manually duplicate actions; no changes to theInputMap
UI are required. Instead,Input
will track internally which layers actions were triggered in per-frame (I think we can do this in ActionState?) and developers will provide as an argument the "player" layer(s) they care about when inspecting input action state.All
Input
andInputEvent
methods that deal with actions (e.g.Input.is_action_*
,InputEvent.is_action_*
,Input.action_*
,Input.get_action/axis
) will all accept a new parameter,player_mask
, which is a bitmask of the "player" layers in which to check for input state. This argument will have a default value in GDScript which matches all players and return results consistent with how multiple devices triggering the same action get resolved today; this is how backwards compatibility will be preserved.This change will also allow for checking the state of custom, in-game actions without needing to duplicate actions per-player. Character controllers can simply store which player is driving them and use that as an argument when querying action state via
Input
orInputEvent
s."Focus" as a concept within the engine will now exist on "player" layers. This means that each player's focus will be tracked independently. The
Control.*_focus
methods will be updated with a new parameter, a "player" layer bitmask, that will be used to constrain the set of players that the focus operations apply to. By default this will be all players, preserving backwards compatibility.This aspect of the solution is likely the most complex to implement. I haven't dug deep into what the exact implementation would look like, but it would likely involve maintaining
n
separate references to focusedControl
nodes, wheren
is the maximum number of players, instead of just1
.Finally, to unlock complex, multi-focus UIs,
Control
andViewport
nodes will both be updated to include a "player" layer bitmask filter. This bitmask will be used to filter incoming GUI input events, allowingControl
nodes to ignore GUI input from players that don't match its setting. By defaultControl
andViewport
nodes will listen on (i.e. accept) input from all players, preserving backwards compatibility. However, developers can modify the bitmask to enable portions of the UI (even within the sameViewport
) to only be navigable by specific players. Finally, like mouse events,Control
nodes can be set to inherit this value from their parent, all the way up to theViewport
node (Viewport
nodes will also use this to filter GUI events).I think this change will mainly be implemented within the logic for propagating
InputEvent
s through a scene (is this inViewport
?). Additionally, assuming thatControl
nodes only receive GUI input events for the configured set of players, then theui_*
action handling within_gui_input
methods will work without changes (also assuming the default "all" players mask is used, though we could pass theControl
node's "player" bitmask to be safe).Additional thoughts/commentary
InputEvent
s are filtered at theControl
level, is a more flexible solution thanViewport
-only approaches like Allow multipleSubViewports
within aWindow
to have a focusedControl
godot#79480 and IntroduceWindow
option to contain multipleViewports
with a focusedControl
godot#89772.use_local_focus
property onViewport
s that could be used to prevent focus stealing by otherViewport
s. While this makes sense in the contexts that they were brought up, I feel this will not be required with the solution proposed above. Locking focus within aViewport
is a coarse-grained solution, while developers will now have access to fine-grained solutions viaControl
-level bitmasks, player-specificInputEvent
s, and player-specific focus management.If this enhancement will not be used often, can it be worked around with a few lines of script?
No, there's no way to work around these issues without reimplementing the concept of focus and player-specific actions. Note that player-specific actions can be created for in-game actions, but
Control
nodes use hard-coded, built-in UI actions that cannot be split amongst players.Is there a reason why this should be core and not an add-on in the asset library?
This cannot be implemented without changes to the engine. Additionally, allowing Godot's
Control
nodes to work with local multiplayer is a feature that could benefit a wide variety of games/developers.The text was updated successfully, but these errors were encountered: