1

When player's computer stutters/he tabs out of game stopping requestAnimationFrame calls, movement system tied to the delta time creates a huge leap, bypassing probable collisions. This allows player to cheat, for example avoiding projectiles by tabbing out and tabbing back in. What are solutions to such a problem, other than pausing the game when delta time is too high?

Attached simple system suffering from this problem - run the code, tab out, wait a while, then tab back in - you shouldn't see the "PEW" in console.

function Box(x, y, w, h, moving) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.moving = !!moving;
    this.colliding = false;
}

Box.prototype.move = function(dx, dy) { this.x += dx; this.y += dy; };

Box.prototype.testCollision = function(target) { return (Math.abs(this.x - target.x) * 2 < (this.w + target.w)) && (Math.abs(this.y - target.y) * 2 < (this.h + target.h)) ; };

var boxes = [ new Box(0, 0, 100, 100, true), new Box(200, 200, 100, 100) ];

function updatePosition(dt) { var time = dt / 1000;

for (var i = boxes.length - 1; i &gt;= 0; i--) {
    if(boxes[i].moving) boxes[i].move(100 * time, 100 * time);
}

}

function updateCollision() { for (var i = boxes.length - 1; i >= 0; i--) { for (var j = boxes.length - 1; j >= 0; j--) { if (i === j) continue;

        if(boxes[i].testCollision(boxes[j])) {
            boxes[i].colliding = true;
            boxes[j].colliding = true;
            console.log("PEW");
        } else {
            boxes[i].colliding = false;
            boxes[j].colliding = false;
        }
    }   
}

}

var canvas = document.getElementsByTagName('canvas')[0]; var ctx = canvas.getContext('2d');

function render() { ctx.clearRect(0, 0, canvas.width, canvas.height);

for (var i = boxes.length - 1; i &gt;= 0; i--) {
    var box = boxes[i];
    if (box.colliding) ctx.fillStyle = '#FF0000';
    else ctx.fillStyle = '#BADA55';

    ctx.fillRect(box.x, box.y, box.w, box.h);
}

}

var lastTime = 0;

function update(time) { var dt = time - lastTime;

updatePosition(dt);
updateCollision();
render();

lastTime = time;
requestAnimationFrame(update);

}

requestAnimationFrame(update);

<canvas width="500" height="500"></canvas>
Misiur
  • 143
  • 5
  • 1
    I found http://gameprogrammingpatterns.com/game-loop.html to provide a clear explanation of the problem. – Niels Aug 08 '16 at 11:28

3 Answers3

2

You're putting graphics drawing and game logic in the same package. They should be separate, so if you tab out the game stops drawing itself, but the logic keeps running in the background (so you don't go through collisionable elements).

  • So, pretty much cut your time into steps/ticks then step through your logic until the number of steps have caught up to the in-game time, then render. – Niels Aug 08 '16 at 11:26
2

When you move the objects, and then test if you collide with something, then obviously you will not register hits if one step is big enough so that you completely pass through the collider.

So don't.

Take the starting point, and the direction of motion, and pre-calculate the next collision point (thats a bit more involved, but possible) and the distance at which it will happen.

If your delta time times velocity is bigger then this distance, there will be a collision, and not only will you detect the collision, but also have a much more precise figure for the point at which the collision actually happened.

Another solution - albeit only a band-aid fix - is to not stop the simulation, as @TheMatrixIsReal suggested. This will not solve the original problem, though, and on slow machines, fast-moving objects might pass through walls during normal gameplay.

Another possibility is to keep the "physics" (which handle the collision detection) on fixed delta-times. This is also commonly used because of the predictability, but also has the aforementioned "Passing through wall" problem for high velocities.

Polygnome
  • 803
  • 5
  • 13
1

I realize you asked 'apart from pausing', but why? Any game that is going to potentially cause the player to fail because they had to stop seeing what is happening is already in a very bad position. Pause the game when they tab out.

Let the user close any other programs they have running then come back to the game.

If their computer is too slow, they should upgrade. If your code is too slow, optimize that.

I would carefully think of every other alternative before encourging the user to tab out to make the game playable.

David
  • 111
  • 3