Best practice "context based" input handling architecture

Hi

(First, Im not sure this is the right forum for this, so admin feel free to move the thread if there is a better place).

I’m not sure “context based” is the right word here, but let me explain.

I am building a FPS game in Unity (see link to my Youtube channel in my signature if interested).

In this game the player can of cause move around FPS-like with Mouse and WASD. All good.

The problem arises when some inputs should no longer have an effect or a button have a different meaning in the current context.

In my FPS sample, lets say you move around on WASD, then when pressing tab, an inventory shows on screen and here you select an item WASD too. This also means, that as long as this inventory UI is shown, the WASD input for moving the player shouldn’t be active.
And you can imagine maybe cases like this where input are handling in a “context”.

I’m a professional programmer in my day job, so I like my code and architecture to be as simple and solid as possible, so it’s easy to make changes and hard to break stuff.
The reason I’m mention this, is that I know of way I can create various hacks to accomplice this thing, it just becomes very “hacky” and hard to understand and objects becomes very dependent on each other - which I want to avoid.

Right now I have been trying to accomplice this using a messaging system, already use that in game anyway, and then disable certain components etc., but still haven’t found a good way of doing this. It be comes too complicated way too soon.

So, I’m interested in hearing/seeing how you have solved this problem in a good way.

And just to make it clear, I’m not interested in seeing how I can use CrossPlatformInput.Get(…) to get the input - that’s not the question. The architecture/structure around how to use this input is the topic :slight_smile:

Why not just pipe the input to the correct context?

Like you have input(menu), then input(character) you just pass around the key and the context manage itself. Input just know to pass key and the context just know to receive key.

But what if you remap the key? well just have a mapping per context that translate key to context.

Okay but now who manage how to get in a context? well the easy way, if the design state is fixed and simple is to have a transition call by the input (or remap) that is exit, and exit is a field where you put what is the next context.

Obviously that introduce a bit of coupling, what if we have a complex menu set up where you can call the same menu from many place? we would want to come back the previous state who is now arbitrary. We can just keep track of the old state, or have a stack to pop.

Be what we really want to limit coupling? well any specifics will have coupling somewhere, so let’s have a single structure that handle the coupling and have nobody know who else is. So when input is pressed, the event is intercepted, then the manager look at the state to know which context to call, then pass it the data and let the context manage itself. Once the context return, the manager look at where the state should be (ie that’s where previous state/stack and transition live). That’s kinda like a message system, but input doesn’t subscribe to the manager nor does the context, the manager register them prior, element just emit or receive without knowing each other, they only provide services.

But do we really need a single monolithique manager to handle everything? we remove coupling from elements but now we have a potential big complex chunk. How about breaking that block and promoting it to a higher level? We would have system that manage some specific link, agnostic of who they are managing, and the manager is just setting up system, so when needed we can swap system and each system has single concern.

At least that’s what I would do now, since I have very bad case of coupling deadlocks who killed 2 projects so far.

In this case, I simply disable the component that handles player movement.

But from where do you do that and based on what logic that could be extended to also disable mouse look and WASD when a ingame menu UI is shown etc. Thats the challenge. :slight_smile:

Interesting, I think I’ll read your post a couple of times and see what I solution I get out of it.
I totally agree, at somepoint you will of cause end up with some coupling and dependencies, but I want to minimize this and want to have it in one place, so it’s easy to change and understand. That’s the goal I’m aiming for.

Do you have a pseudo code sample of how this could look?

Anything that needs to pause when a menu opens has a special “pause” component on it which references all of the components that need to be disabled. Whenever the menu system is invoked, it signals all of the “pause” components. Likewise, when the menu system closes it signals all of the pause components to resume.

Edit: Now, if you need to pause rigidbodies in motion- that’s a can of worms that haven’t opened yet, but there’ve been discussions on these forums about this topic. It’s all possible.

I personally like tight coupling for this.

Easiest way is basically have a enum somewhere with different input modes, and have whatever other pieces (your inventory screen or movement controls) check the state and respond appropriately. You could also do it reverse and centralize all the results of state change. Either would work fine.

Event driven could also work fine, here you decentralize.

I don’t think pub/sub is great for this kind of thing but you could bootleg it together to be roughly the same as event driven.

Lots of ways to handle this. In our game we have a lot of features that need input so we also want to reuse easy to use keys based on the context.

We have a UIContext class to handle part of this whole area with an associated UIContextType enum.

For player movement constraints we have this enum:

public enum AgentMoveConstraint
    {
        None,
        Move,
        Rotate,
        MoveAndRotate
    }

The ui context has the context type enum and the movement constraint enum. There is a HasCursor bool and a UiSingleton bool to denote if the ui associated with the context should be the only UI panel active. So we can do things like close all other ui panels if it’s opened, etc…

The ui context doesn’t really have any logic of it’s own. Our UI manager handles that keeping track of which ui context’s are active, setting the move constraints and cursor appropriately, etc…

We also have an InputManager to abstract input handling. Input types have an enum. We use events here. Feature code subscribes to an input type and if more then one tries to subscribe to the same we throw an exception. BUT we have a notion of context input. Code can subscribe to an input as being in a context, passing some object that is used as an identifier for that context. In which case other registered inputs of the same type are temporarily disabled. And the input manager has a ClearContext(object) method that clears all context inputs and re-enables the inputs that were disabled.

We decided to just not allow multiple input contexts that use the same inputs. We might have been able to make that work but it seemed problematic. So the input manager just throws and error if you try to register an input type in a context if that input type has already been registered by another context.

We also use Rewired. Our input type enums actually map directly to Rewired actions. I just prefer enums over the constants Rewired generates.

One other note. Make sure your input handling is based on actions not specific hardware inputs. If you want to support input mapping that is.

2 Likes

Very interesting. I will try and take a look at how I can implement something like this in my game later.

In general, thanks for all your suggestions so far! Keep em coming if more people have good approaches :slight_smile:

What advantage do you see in this being event driven?

I know the answer is ‘loose coupling’ but I don’t see any advantage to using events here to pass around value changes. I imagine you would use events to synchronize a local variable to match your source, but in an environment like Unity that is really built around per frame updates I don’t really see the upside in this.

Why not just update the value per frame based on source, instead of hooking up event driven updates and copying the state locally?

We actually don’t use events for things where you want to query the state per update/fixedupdate, like movement/mouse controls. Forgot to mentiond that. But 90%+ of our user inputs are UI driven and completely detached from update. Our complexity is in the UI and the other misc game states it has to reason about. Frame based input is really simple. We always read it we just sometimes ignore it based on various UI states.

I moved away from event driven UI personally. Most of the time event driven code for ui is used to synchronize multiple copies of some piece of data, in an environment that has such strong support for polling, I tend to favor it over event driven.

To each their own though. Not trying to start a religious debate, was just curious as to the other side of the argument.

I have read all your messages and started on my version yesterday, which were kind of ok, got the mouse movement working in the end.

Anyhow, afterwards I was thinking about if the new input system couldn’t help some of these problems maybe, and looking into that right now and I think I see something I can use. This concept of the Interfaces and enable/disable of the controls might come in useful. Haven’t figured it all out but think I’ll give it a try tonight.
Watching this right now:

Let get back with what solution I come up with, mostly for other people finding this thread in the future.

Another useful link: Quick start guide | Input System | 1.0.2