0
\$\begingroup\$

I have a game loop that simply iterates through every entity and calls entity.update()

class Engine {
    protected entities: IUpdateable[]

    update() {
        GlobalTime.tick(this)
        for (let entity of this.entities) {
            entity.update()
        }
    }

I need to accept user input, but without requiring the game loop to pause and wait, like the following code would do

    update() {
        getUserInput() // will result in async-await like behavior, pausing until user input
        GlobalTime.tick(this)
        for (let entity of this.entities) {
            entity.update()
        }
    }

What is the standard way to implement optional asynchronous user-input with a continuous game loop? Is it just to have an .events array that is checked every frame, resets every frame, that the frontend can optionally append user input to?

\$\endgroup\$
2
  • \$\begingroup\$ It sounds loke you've already thought of a way to do this. What problem have you identified with that approach that you'd like help solving, or in what way should answers focus on improving it? If you provide details about your tech stack, we may be able to target answers to features available in that language/framework. \$\endgroup\$
    – DMGregory
    Commented Apr 12, 2023 at 13:53
  • \$\begingroup\$ @DMGregory I'm just wondering what the typical approaches are / if my approach is recommended. Trying to familiarize myself with the domain space. \$\endgroup\$ Commented Apr 12, 2023 at 13:56

1 Answer 1

2
\$\begingroup\$

Is it just to have an .events array that is checked every frame, resets every frame, that the frontend can optionally append user input to?

That would be called an "event queue", and it is a pretty common solution for input handling.

Another option is to have a data-structure that holds the current state of all inputs (which buttons are currently held down, which values you have on which analog axis...). Any system that needs to know the current input state would look in that data-structure. An architecture like that can be really convenient in game development, because you often have mechanics where some entity is supposed to behave in a certain way while a button is being held down (for example: "run right while the player holds d-pad right"). In that case, you can write code in an update method like this:

if (inputs.moveRight.ispressed) { 
    position.x += speed * deltaTime; 
}

But because most operating system APIs that deliver user-input are event-based, you usually end up writing that structure from event handlers anyway. If you are working in an multithreaded architecture where input events can be processed at any time and not just at a specific point of the game-loop, then it is usually a good idea to write their changes to a shadow-structure, and replace the actual input structure at the beginning of the update-loop. That ensures that the input state doesn't change during the update-loop, which might otherwise become a source of hard to reproduce bugs.

\$\endgroup\$
6
  • \$\begingroup\$ To confirm, will this require me having separate threads - one for the gameloop, the other for the source of user input? Otherwise, my gameloop will block any later code from running, so it seems like I might need some concurrency. \$\endgroup\$ Commented Apr 17, 2023 at 12:38
  • \$\begingroup\$ @MichaelMoreno That might depend on what API you use to receive user-input. The input APIs commonly used in game development have some form of non-blocking design. They usually modify a global input state, invoke callback functions on input state changes or they post input events to some event queue. But when you for some reason use one that has a blocking design (like reading text from standard input), then you are going to need multithreading. I can't tell you what APIs you have available, because you didn't tag the question with any particular technology. \$\endgroup\$
    – Philipp
    Commented Apr 17, 2023 at 13:01
  • \$\begingroup\$ My game engine will run on a js node server, with the user front-end being a web app. Getting user input on the front end is trivial given the DOMs built-in event system, but injecting those events into the game engine's events queue over on the backend node server is where I'm not so sure. \$\endgroup\$ Commented Apr 17, 2023 at 13:44
  • \$\begingroup\$ @MichaelMoreno You should have mentioned that in the question, because it means that your question isn't actually about input handling. It is about networking. Node.js is not my area of expertise. But I would expect that you have some API endpoint function that gets called when the user connected via websocket sends a message. What you then do during that function call is up to you. \$\endgroup\$
    – Philipp
    Commented Apr 17, 2023 at 13:53
  • 1
    \$\begingroup\$ Again, my knowledge of Node.js is only superficial. If you want a proper answer how to architect a server-sided game loop in node.js, you might want to post a new question with the proper tags. But as far as I know a while(true) loop is an anti-pattern in Node.js. It defines itself as "an asynchronous event-driven JavaScript runtime". So an infinite game loop should be a function that re-enqueues itself as a new event when it's over. This gives the runtime the opportunity to process other events in its queue. \$\endgroup\$
    – Philipp
    Commented Apr 17, 2023 at 14:25

You must log in to answer this question.

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