0
\$\begingroup\$

I'm developing a game which uses a standard Entity-Component-System.

Every frame, the PhysicsSystem runs, which, among other things, updates the position of entities based on their velocity. Something like this:

execute(delta: number, elapsed: number) {
  for (let entity of this.scene.query(CT.Transform, CT.Physics)) {      
    let transform = entity.get(CT.Transform);
    let physics = entity.get(CT.Physics);

    let movementIncrement = physics.direction.multiply(delta * physics.friction * physics.speed);

    physics.velocity = physics.velocity.multiply(1 - delta * physics.friction).add(movementIncrement);
    transform.pos = transform.pos.add(physics.velocity.multiply(delta));
  }
}

This works fine, except there's an issue. When the user switches to another tab, waits some time (let's say 5 seconds), and then tabs back in, sometimes the entities instantly fly away so far that they fly off the map and out of bounds.

I'm not sure why this would happen, as it never happens during normal gameplay. Something to do with the tab becoming inactive for a time, and then active, seems to cause this issue.

I suppose a possible solution is to "pause" the game if the tab loses focus, but I rather the game keep running in the background unless the user manually pauses by hitting escape.

Does anyone know why this is happening? The rest of the game continues functioning normally, it's just the entity positions fly away for some reason.

\$\endgroup\$
2
  • \$\begingroup\$ Does the game actually runs in the background? What is the actual framerate of the game when you're out of focus? (I think I remember reading that it's much less than usual, or it does not run at all.) \$\endgroup\$ – Vaillancourt Mar 24 at 18:18
  • \$\begingroup\$ I'm not sure how to check that. I'm on windows and using chrome. When I tab out but can still "see" the window, because I just have a smaller window in front, and I can still "see" the game, then the game runs normally. The only time this happens is when I switch tabs so the game becomes the inactive tab. I'll try console logging the FPS maybe. \$\endgroup\$ – Ryan Peschel Mar 24 at 18:23
1
\$\begingroup\$

The browser suspends your page to save CPU power during that time.

When tabbing back in the physics engine sees 1 frame with a 5 seconds duration so it moves the objects for that entire 5 seconds before processing friction and what not.

The first thing is your velocity * friction calculation should be a power function, not a linear factor of time.

For example, with delta = 2.0 and a friction = 0.25 your formula multiplies by 0.5 (1 - 2.0 * 0.25) but if we were to calculate two 1 second frames the result would be 0.5625 (1 - 1.0 * 0.25) * (1 - 1.0 * 0.25)

It should be calculated as velocity * pow(1-friction, delta).

The other issue is that 1 - delta * physics.friction possibly gives you a negative factor when (delta * friction) is larger than 1.

\$\endgroup\$
2
  • \$\begingroup\$ Comments are not for extended discussion; this conversation has been moved to chat. \$\endgroup\$ – Vaillancourt Mar 24 at 18:50
  • \$\begingroup\$ Thanks for all the help. I ended up moving to a fixed physics update timestep which fixed everything. See the chat discussion for more details. \$\endgroup\$ – Ryan Peschel Mar 24 at 19:28
1
\$\begingroup\$

Assuming the browser suspends the execution of the code of the page, when the browser gets back go your game you should only run the equivalent of one normal frame.

Running a 5 second frame does not make sense when your game is made to run frames 60 times per second. Imagine all the collisions that will be missed.

Instead of using a delta-time based like you have now, pre-define your delta time to be 1/60, and limit your framerate to that.

This way, everything you run your physics, whether the browser has suspended your game or not, you'll never experience odd behaviour like this.

Another option is to clamp your frame time to a maximum when it gets to your window; before you run the frame, make sure it is at most e.g. 1/30, and use it as your delta time before passing it to your game functions.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.