Hi!
I’ve been pounding my head against the keyboard for a day or so trying to get the MultiplayerEventSystem to work in my network multiplayer game with couch co-op, but I’m coming up with nothing. I’m hoping I’ll solve my problem as soon as I post this, typical me ;), but I thought I’d write this up anyway since I’ve been spinning my wheels for several hours now…
Here’s the situation:
My networked game supports up to 10 clients connected to a server, each client supporting 8 local players (for 80 players total.) I’ve got a top-down Fat Princess: Adventures style game with a single camera (no split-screen) with all 8 couch co-op players sharing a single camera. The client connection flow looks a little something like this (for completeness, hopefully the details here don’t matter too much):
- Client joins the server and gets a MultiplayerNetworkController created and assigned to it.
- When the MultiplayerNetworkController spawns on the Client, it enables the InputManager which waits for player input to join a local player.
- When a player presses a button, a LocalPlayerController object is created in the PlayerJoin callback (the prefab setup in the InputManager)
- After a LocalPlayerController is created, the MultiplayerNetworkController requests a separate NetworkPlayerController object for that local player from the server (so 80 of these potentially get created on the server) for networking logic.
- When the NetworkPlayerController is spawned (on the owning client only), I show a Character Selection UI in game (to enable quick join-in-progress for couch co-op) that is intended to let a player choose their character and request that character be spawned and controlled through the MultiplayerNetworkController to the server.
My issue: No matter what I do, I can’t for the life of me get UI input working in Step 5 there using the MultiplayerEventSystem. No mouse clicks, no navigation events happen (using either keyboard or gamepad), no submit or cancel, nothing.
Here’s the setup of the LocalPlayerController:
The ActorInputProvider being the bridge between the NetworkPlayerController and LocalPlayerController.
There’s a bit more to the story…
In Step 5, after the client gets its NetworkPlayerController spawned in, the LocalPlayerController is “hooked up” to it and a few things happen:
- The PlayerInput.MainCamera is set to the camera that gets spawned in with the client MultiplayerNetworkController in Step 2.
- Some manual action binding happens for Actor controls (not UI controls) like so:
// Map initial input actions to handler wrappers
moveAction = playerInput.actions[moveActionName];
rotateCameraLeftAction = playerInput.actions[rotateCameraLeftActionName];
rotateCameraLeftAction.performed += HandleRotateCameraLeftActionPerformed;
rotateCameraRightAction = playerInput.actions[rotateCameraRightActionName];
rotateCameraRightAction.performed += HandleRotateCameraRightActionPerformed;
- The PlayerInput’s ActionMap and Behavior are changed (based on the InputMode I intend, controlling an actor or cycling through UI) like so:
case InputMode.UI:
playerInput.SwitchCurrentActionMap(uiActionMapName);
playerInput.notificationBehavior = PlayerNotifications.SendMessages;
break;
case InputMode.Actor:
playerInput.SwitchCurrentActionMap(actorActionMapName);
playerInput.notificationBehavior = PlayerNotifications.InvokeCSharpEvents;
break;
NOTE: This is a bit funky but I have some very specific input requirements for when a player is controlling an actor in the world and manually binding (and using InvokeCSharpEvents) was the only way I could get the input to work exactly as I needed it to. For the UI, using either SendMessages or BroadcastMessages is fine.
- The MultiplayerHUD (the main canvas under the client’s MainCamera) is instantiated from a prefab and has 8 PlayerPanelGUI sub-panels, one for each player (mainly used for showing “Press Button to Join”, Character Selection GUI, and Player Status HUD). The PlayerPanelGUI is set as the event system root and the (any) button is set as the FirstSelectedGameObject like so:
// Set the player panel to the event system player root
var panel = controller.MultiplayerController.HUD.GetPlayerPanel(controller.PlayerIndex.Value);
panel.SwitchToMode(PlayerPanelGUI.Mode.CharacterSelect);
eventSystem.playerRoot = panel.gameObject;
eventSystem.firstSelectedGameObject = panel.GetComponentInChildren<Button>().gameObject;
GUI Layout looks like so:
- MultiplayerHUDCanvas (Canvas - Screen Space Overlay)
– TopVerticalLayout (HorizontalLayoutGroup)
— 4 PlayerPanels (With Canvas, Canvas Group, or both in various combinations. This is what the MultiplayerEventSystem.playerRoot is set to when a player joins.)
I thought that was all that was needed, but I’m missing something silly.
I’ve verified that the UI input is happening correctly (by hooking up a dummy OnSubmit() handler in ActorInputProvider), there’s just something messed up with the setup with the MultiplayerEventSystem. I’ve tried manually setting the MultiplayerEventSystem.playerRoot AFTER a player connects, but neither the MultiplayerHUDCanvas nor any PlayerPanel objects seem to receive input events correctly.
I have the MainMenuUI (in a different scene) setup to use a single EventSystem with the same input map and everything works as expected (all local players can control the main menu. Chaos, but fun ^_^)
Does anybody have experience getting anything even remotely like this working from script? My next step is to try to manually pipe navigation events through the ActorInputProvider to the “current HUD”, but I thought I’d ask here first (since that will turn into a LOT of work for me down the road, potentially.)
Any and all thoughts appreciated on this one