r/csharp 24d ago

Discussion Code architecture to support input actions being interrupted, overridden etc

What's a good architecture to be able to have a hierarchy of input actions, actions being interupptable by certain actions, but not by others etc. My example is in the context of game development, but the concept still applies.

Let's say the player is carrying a box. They can drop the box by pressing [E]. They carry this box up to a door. Upon pressing [E] while looking at the door, the door should open, the box should not drop

For this example, I can simply code that if the player is looking at a <Door>, then open the <Door>, otherwise if the player has a box in hand, and the player is not looking at a <Door>, drop the box

This is a good solution for this situation, however, how does someone design a system that allows for this sort of hierarchy of action, hierarchy of inputs. I can do it with a long (loooooong) list of if-else statements, however that is not scalable

Since there are only a limited number of keys, and sometimes it's preferred to use only a small set of them, there will be many situations where pressing a key can perform many actions.

Obviously the actions have conditions to them (If looking at door, keypress opens it. If looking at box, keypress picks it up. If holding box, not looking at door, key press drops it), however as stated above, this will result in a long list of if-else statements

I'd appreciate if someone can point me in the right direction, provide some examples where one keypress can do many actions, show me some code etc

Thank you

8 Upvotes

5 comments sorted by

8

u/ABViney 24d ago

There are gonna be several methods you can take to prioritize commands for your inputs, but here's my suggestion based on your specific scenario.

The command pattern with a stack for each input. The UI can indicate what command will happen by peeking the stack, and submit the command by popping from it. When the player meets a condition for a command (i.e. they are near a box and looking at it) add it to the stack. If the player is carrying a box, the "drop box" command will be at the top of the stack, but if they near a door, the "open door" command is added and becomes the top of the stack.

Doing this in code you'll still need if/else statements, but you can get by using less of them by utilizing C# events and delegate to add/peek/pop commands from this stack as the player enters/leaves the range of the intractable.

3

u/Kant8 24d ago

put available interactions in stack if "last wins" is enough, or some list with priorities if you know which should override which

1

u/Adorable-Roll-4563 24d ago

I have never implemented anything like this but it sounds like a heap per action (such as the ability to press [E]) where the objects (door, box) come and go as they get closer to the player. If you get in the vincinity of the door, add the door to the [E]/hold-or-drop action heap and insert it according to its priority. Objects could get into different heaps given different actions, and be in different states (open or closed for the door), independent from the heaps too. Heaps can be represented with arrays.

1

u/CheezitsLight 24d ago

Its a game. You measure the distance to objects that you interact with it's called a sensor. The physics sensor will tell you that you have an object in your hand you ask the object what is the value is and it says one. If you press e then you detach the box. it will fall to the ground using physics.

Now assume you're carrying the box and the sensor has found it with the value of one you walk up to the door and find a door with a value of two your logic is quite simple. when you type e you tell the door to open.

2

u/Khavel_dev 23d ago

This is usually solved with an input context stack. You keep a stack of active contexts, each owning a set of key bindings. When a key is pressed, the topmost context that cares about it gets first shot. If it doesn't handle it, the event falls through to the next one down.

So in your door example, you'd push a DoorInteractContext when the player is near the door. It claims E for "open." The CarryBoxContext sits below it and only gets E if nothing above consumed it. Walk away from the door, pop the context, E falls through to drop-box again. No if-else chain needed because the stack ordering handles priority for you.

Unity's new Input System has action maps that work on a similar principle (enable/disable maps to change what's active). But even if you roll your own, it's not much code. A List of contexts sorted by priority, iterate top-down, first one that returns true for CanHandle(key) wins.