1
\$\begingroup\$

I'm currently in the planning phase of a game. The whole thing should be based on the entity-component-system (ECS) pattern. All logic is concentrated inside of the systems, i.e. that the components hold data only. Communication across the systems is performed using asynchronous events.

Using this design, I'm unable to find a clean solution for the simple example task of moving player: While the input system generates key press events, I'm looking for a way to query the key state, since the player will most likely want to move the player for more than one frame.

There are two possible ways I can think of solving this problem:

  • Retrigger the key press event every frame
  • Pass the corresponding systems a reference to the input system

Neither possibility sounds "correct", since they both require the abuse of the design.

Is there a better approach for solving this issue?

\$\endgroup\$
1
  • \$\begingroup\$ There are as many flavours of ECS as there are developers. What are the responsibilities of E, of C and of S? How are they linked? \$\endgroup\$
    – Vaillancourt
    Sep 12, 2017 at 15:56

1 Answer 1

0
\$\begingroup\$

I would opt to tackle input using a hybrid approach.

  1. Cache the current input state in the input system, allowing it to be queried.
  2. Raise appropriate delta input events as necessary.

Lets take an example, pressing both left and mouse buttons imply move forward.

If this use case was designed specifically using only (2), you not only would have to write the handler to detect the mouse button press and release, but maintain state management to track both buttons since its possible one is pressed or released in different frames from the other.

If we used a query approach, the system no longer needs to be bothered with maintaining state for these buttons as the input system does this automatically and exposes a way that a given system could simply poll

if ( input.IsMouseButtonsPressed( Mouse::LEFT || Mouse::RIGHT ) ) {
  // do logic
}

This becomes even more powerful when you have more than one occurrance of the same state check, likely in different systems, allowing it to be centralized and for that state to be managed by what I feel is the most appropriate owner, the input system.

In systems design, I like to take this one step farther and layer another abstraction here on top of what I have described the input system doing. This system is called an action or intent system that generally uses both methods to maintain an additional state map meant to abstract an action with a particular keybinding, allowing those bindings to be customized.

Now rather than your movement system looking for key-press W or up-arrow key for moving forward, it might ask the action system if an action is toggled:

if ( actionSystem.IsActionActive( Action::MOVE_FORWARD ) ) {
  // move forward
}

There will often be a mix of both of these in certain systems as sometimes certain bindings are not mutable and therefore those will be used directly, e.g. the left/right mouse button example above.

\$\endgroup\$
2
  • \$\begingroup\$ This abstraction seems to be what I'm looking for. The remaining question is: How does the intent system know about the input system and how do the other systems know about the intent system? I'm expecting increasing coupling when I just pass references to the respective constructors. Is there an alternative approach? \$\endgroup\$ Sep 16, 2017 at 8:19
  • \$\begingroup\$ You're going to have some coupling between systems. You aren't going to necessarily avoid that. The goal is to minimize that where possible. Some people opt for a message broker concept between systems; however, I find that just obscures dependencies that are meant to exist. \$\endgroup\$
    – Naros
    Sep 18, 2017 at 5:43

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .