I'm designing an input system for a game where the same key can perform different actions depending on the current context. For example, pressing E might drop a box the player is carrying, but if the player is looking at a door, it should open the door instead and leave the box alone. Other situations could include picking up items, using equipped objects, or triggering a default action. What architecture would support action priorities, conditions, and interruption rules without turning the input code into a long, unmaintainable chain of if-else statements?
4 Answers
You don't necessarily need a completely generic system. Define a small number of gameplay layers and evaluate them in a fixed order. For example: first the object being looked at, then the held object, then the equipped object, and finally global or default controls. Each layer checks whether it has a valid action for the input. Once one layer handles the input, the remaining layers are skipped. This keeps the ordering explicit and makes the system easy to reason about while still allowing each object to define its own actions and conditions.
Represent each action as a command with three main properties: a condition that says whether it can run, a priority, and an interruption policy. On each input, gather matching commands, discard the ones whose conditions fail, and execute the highest-priority remaining command. For actions that take time, the interruption policy can specify which newer actions are allowed to cancel them. This lets you add new actions by registering commands instead of editing a central chain of branches, while still keeping interruption behavior explicit.
A useful approach is to turn the keypress into an abstract intent, such as Interact, rather than handling the physical E key directly. Then query the current context for possible targets: the object being looked at, the player's hands, equipped items, and finally any default controls. Sort those candidates by priority and offer the intent to them in order. The first candidate that accepts it handles the action and stops propagation. In the example, the door gets the Interact event before the hands do, so the door opens and the box is not dropped. If no door is targeted, the event reaches the hands and the box can be dropped. Each handler can return whether it claimed the event.
That makes sense. It feels like a more structured version of the conditional logic, but with the priority and handling rules separated from one large input function.
Another option is to build the available action list from the object currently receiving the player's attention. Use a raycast or similar query to find the first object in front of the player, then collect its currently valid actions. Try those actions from highest to lowest priority until one matches the input. Components can contribute actions independently: a door component might offer Open only while closed, while a lock component can add Unlock or prevent Open until the lock is dealt with. When the player is carrying a box, the box can become the focused object, so its actions take precedence over a door behind it.

It is also worth being careful about overloading one button. Using the same key for opening doors and dropping objects can create frustrating mistakes, especially when the player is carrying something near an interactable object.