Best practice for handling input (controller)

Hi there unity fam!
I’m on the fence as to how to implement a ‘best practice’ for capturing input and sending it to objects in the game. Currently I have a ‘player controller’ monobehaviour which does all the action map switching and action enable/disable functions.
Then on objects that need input such as a ‘player’ gameobject in the world, it will subscribe to the events from the ‘player controller’ object (based on it’s state and what controls it requires).

I’m on the fence if this is a ‘best’ way to handle input - I’m kind of tempted just to throw the input directly on the world gameobject - and when any UI is required, simply switch action maps and so on.

If you’re currently using InputSystem - how are you handling input, either for single or local multiplayer?

2 Likes

I have a persistent gameObject which holds a list of children with PlayerInput components. When I switch scenes, I update the Actions resource on all PlayerInput objects. I have a player select scene which creates a PlayerInputManager that I use to spawn children on to the aforementioned gameObject.

I delegate the input events back to respective objects in the currently active scene. Which I think sounds similar to what you’re doing.

I’m not sure if this is good practice, but it works for me.

The only issue I’ve encountered, is where the PlayerInput gameObjects unpair from their devices when switching Action assets.

1 Like

Oh no - that is not a good ‘feature’ lol

And yes, it does sounds pretty similar, and so far working well enough so I think I’ll continue doing so - but keep one eye open on this thread!

I have this concept which I call a “Core stack” which is somewhat similar to your setup. This is for a single player game so the specifics of Input would likely change if multiplayer is required, but the basic idea is the same:

  • I have a Master gameobject which sits in every scene which I call the Core. It has a special tag so any object can find it and get data from it.

  • Each child in the Core - i.e. the Core “stack”, has a separate purpose and holds monobehaviors that pertain to that purpose. So I have one object for the Pause menu, one for the Player UI, one for the Save System, etc. This allows every level in my game to have the same functions under a single prefab. Nowadays nested prefabs help a lot more but back in the old days a single prefab was how things got done, which is why I nested everything under the Core.

  • So somewhere in the Core stack I have some script which refers to Input handling. It has a public variable for the InputActionAsset, which stores all of the maps for the entire game - for UI, gameplay, etc.

  • Now whenever I need to get the input from a certain action, I just keep a reference to that Input handling script in the core (it is automatically updated every time the prefab is updated) and I can retrieve the InputActionAsset, which has a reference to all maps and actions. This is better than having local variables for each object that might need input–they can all grab what they need from this “global” asset variable. This is especially important because now I don’t need to go into my Input handling script to “hand out” data to objects that need it–they can just take what they need from the data it stores. If an object needs to subscribe a function to an action event delegate, it can do so independently without recompiling anything else or adding extra logic to the Core. Clever structure like this is crucial to game development, it prevents wasting time and helps you stay organized.

  • One last step which I performed: I ended up needing extra data for each action, such as if the action was currently pressed or released on the current frame, and that’s data I needed to get per-frame without making callbacks. So I ended up automatically generating a Dictionary of my actions and keeping a “Wrapper” for this extra data and a reference to the action. So now when I need to find if an action is pressed on the current frame I just do a lookup to this custom data wrapper using the Input handling script from the Core stack, and all of this generates on Awake() without needing a recompile any time I change the input maps. If you’re interested in doing something similar I actually posted the code for public use.

Hey! Yeah my setup is very similar in fact, so that’s encouraging to know I’m on the right path :slight_smile:
And thankyou for sharing your code - I’ll be sure to check it out!

1 Like