Design Review Request: Event-driven player input

We’re creating a local co-op game with several mini-games. At the start, players can join by pressing one of the pre-bound “accept” buttons on the keyboard or a controller.

.

We just designed a rudimentary version of the player select back-end and it already feels poorly designed. We’re looking for suggestions on how to clean it up before using this as the core of our controls for the game and fleshing out the UI.

.

Current Layout

.

IPlayerControls

  • Stores a button layout
  • Current implementations: PlayerKeyboardControls and PlayerJoystickControls
  • Provides methods such as GetMovementHorizontal(), GetUpKey(), GetJoinGameKeyDown(), etc.

.

Player

  • Grabs a snapshot of its assigned IPlayerControls on Awake()

.

GameControlsManager (singleton)

  • Set default keyboard button layouts / read keyboard button layouts from PlayerPrefabs
  • Holds dictionary of PlayerNumber → IPlayerControls which assigns a player a controls config.
  • Frequently checks for available, unclaimed IPlayerControls configs in case a user connects a joystick, etc.
  • bool CheckPlayerClaimedControls(): loop through the remaining available configs, determines if the “join game” key has been pressed on any of them, and assigns to the next available player if one exists.
  • Provides a snapshot of player control layouts (e.g. to a Player).

.

PlayerSelect

  • Runs loop of GameControlsManager.Instance.CheckPlayerClaimedControls() and updates UI

.

Issues

.

So from our POV there are at least two main glaring issues with this approach:

.

  1. GameControlsManager is doing a lot and could probably be split up (but we need a singleton).
  2. CheckPlayerClaimedControls() and similar upcoming methods should probably be UnityEvents instead.

.

We’re sure there are more and we’d love to hear them.

.

Followup

.

With all that in mind, here are the requirements for our system:

  • Save a set of keyboard configs, set defaults where these aren’t already set
  • Allow players to assign themselves a config at the player select screen based on key press.
  • Allow Players (the class) to retrieve these configs at runtime
  • Allow scenes / classes to listen in on generic events (a player has pressed a “join game” key, a player has pressed “up” – useful for menu navigation)

.

Very curious to hear all your thoughts on a better design for this. I’ll be spending a better part of the day continuing to research and draft a better solution, so I’ll update if I think I have a semi-working solution :slight_smile:

Here’s my attempt at a slightly better solution and to better illustrate the relationships between the classes.

.

Changes:

  • GameControlsManager → GameInputManager
  • PlayerPrefs read/write moved out of GameInputManager into GameKeyboardInputStore
  • GameInputEventManager created to handle global player events, e.g. any player presses “join game” button (for player select) or any player presses “up button” (for ui navigation).

.

GameInputManager’s role now is to assign an IPlayerControls to a player, pass it along when requested, and provide ways to signal when any player performs a global action.

.

We on Update() in GameInputManager to keep AvailablePlayerControls up-to-date. If any of the AvailablePlayerControls buttons for “join game” is pressed, we register the controls to PlayerControlsAssignments where possible and we trigger an event to the GameInputEventManager, which then triggers an update in the UI.

.

I still feel like this is a pretty poor design, but maybe we’re getting closer?

.