So, after much research, I’ve built a custom UnityEngine.UI dll from source and gotten it working with the PlayerInput modules by modifying the EventSystem to support multiple instances, and the BaseEventData to support User Id’s. This seems to get the base UI components working with isolated inputs, and should allow for overridden components to support things like multi-user selection on single buttons in my character select screen. I don’t believe I’ve broken anything crucial in the process, so I can move forward at this stage.
Two questions on this:
By modifying the EventSystem to support rotating through the EventSystem instances, is there something crucial I’m not seeing that I should be aware of?
Is there anything in the plans to support this natively in the future? Everything I’ve seen on the forums either recommends overriding the EventSystem and BaseEventData (which of course will then require overriding basically everything you want to take advantage of it – so buttons and other ui elements), which seems like overkill.
If there’s not plans to replace it in the foreseeable future, I’m happy maintaining it for my projects, just checking before I plan around keeping it updated.
For starters, we are adding an overriden EventSystem (similar to what you describe) to the Input System package:
We have talked to the UGUI devs and agreed that we will have something like this in ugui itself in the future (at which point we may remove the above from the Input System again). In my testing this MultiplayerEventSystem is enough needed to build a simple scene with multiple UI instances controlled separately by multiple controllers - each with it’s own MultiplayerEventSystem and it’s own input module. Why did you need to also change BaseEventData?
Oh cool! The big reason I went the route I did is that I have two separate use cases. The first is multiple inputs (from multiple users) driving separate UI’s on the same screen. For that, the easiest way I could find was multiple event systems, and as long as the Navigation is set to explicit (so that the UI elements don’t link amongst the other panels), it works great.
The second use case, which is my next piece, is a single UI panel which will have multiple users driving it – so similar to a fighting game character select, where you can have multiple users selecting the same item (and needs to visually represent that, in addition to passing who pressed submit). In that case, we need to pass the userID along through the button presses to determine which user pressed the button – hence the custom eventData.
I don’t necessarily like overriding BaseEventData this way, but it seemed a reasonable tradeoff to just have a UserID I can set in my custom input module and pass around to use when needed. I was going off this earlier post in the forums:
For both of these, I am trying to minimize the amount of code written, and keep things really flexible. I’ve written custom managers from the ground up for this before when we only had the legacy input system (usually involving XInput.net and state machines to drive modes), and it’s just a lot of code to keep track of and modify as we iterate on the game design itself, which UnityGUI (with it’s UnityEvent system) could manage in a much simpler and easy to manage fashion.
I found in my experiments that overriding components in the game space meant a lot more work (overridden event data meant overridden UI elements and overridden ExecuteEvents code, which exposed some functionality in the UI library that is internal to the module (the ObjectPool). By overriding it at the module level, I took the entire changeset to about 10 lines of code total, and now I’m back in just choosing elements and hooking things up land :).
Ok. So the first case should be supported with the MultiplayerEventSystem class coming to the input module.
The second use case is not something we’d currently support. But I wonder, does your solution actually work? If you have multiple players interacting with the same UI, then each player needs their own selection. But what if multiple players have the same button selected at the same time? Wouldn’t your buttons and other UI elements change their selected status (and appearance) as soon as one of the players deselect it?
Maybe I’d consider having multiple instances of the same UI layered on top of each other for this? (with the non-selected state being transparent and a base instance rendering the non-selected UI)
I’ve got a class called FlipBook that displays a sprite based on a bitwise operation showing the current users attached to the button. I’m actually currently using the raw userId from the input, but in my full implementation I’ll cache that in the JoinPlayer callback to get a game-based player ID from 1-4 (or whatever we end up going with for player count).
I’d expect OnSubmit to work the same way, and from there I can emit a UnityEvent that contains the player ID for further processing.
A side note, you’re correct that an overlapping system would work here as well. My solution takes a little bit of setup, but provides a slightly nicer interface at runtime (as well as not worrying about introducing Z-fighting), in addition to working nicely with a SpriteAtlas for the player indicator on the button.
The key is that I’m happy to write custom UIElements that take advantage of the user id passed through the events, but I don’t want to reinvent the wheel and rewrite the whole Navigation system and other niceties in UnityEngine.UI. If I can just override the BaseEventData with a class on the Game side and pass it through in my input module, but I believe I then need to override the base UIElements as well to use that custom class (could be just me falling back to my C++ background, but I’d be worried about object slicing truncating my custom data before it gets to my custom elements – it didn’t seem to work when I tried it).
If I’m going about it wrong, happy to update, the modifications to UnityEngine.UI were minimal