skull

Robert Tamayo

R
B
blog
skull

Separating Physics and Rendering Framerates

Ghosts may be able to walk through walls, but they shouldn't be able to glitch through them. While playtesting Balloon Ghost and finishing the game up one level at a time, my PC started slowing down and the framerate dropped in the game. This caused Balloon Ghost to fall through the ground and onto some spikes. That may be normal for ghosts, but it shouldn't be normal in a game.

To fix this, I finally got around to separating the framerate for physics from the framerate for rendering. It's such a simple solution that I'm considering making a simple update for Robot Ops with it.

float delta = gameDelta;
float threshold = .034f; // ~30 FPS - if delta is greater than threshold this frame (framerate lower than 30fps), break up the collision test into multiple runs
if (delta > threshold) {
    int framesSkipped = 0;
    boolean collision = false;
    while (delta > threshold) {
        delta -= threshold;
        framesSkipped++;
        if (runCollisions(shaper, threshold)) {
            collision = true;
            break;
        }
    }
    if (!collision) {
        runCollisions(shaper, delta);
    }
    System.out.println("Frames Skipped: " + framesSkipped);
} else {
    runCollisions(shaper, delta);
}

Each frame, the game does a check to see how much time has passed since the last frame. It uses the difference, or delta, in all of the calculations to see how far a character has moved since the last frame. The collision detection is supposed to stop a character from moving through a wall by detecting whether the character bounds have crossed into the wall's or ground's bounds.

When a game stalls for whatever reason, this can cause the framerate to drop for a few frames. The time between the current and last frame can become so large that the game thinks a character has moved 8 times farther than normal. This can be drastic enough to cause a character to be placed inside of a wall before the collision detection takes place. Once a character is inside a wall, it can be hard to figure out what to do with them, so usually bad things happen.

To fix this, developers set a threshold for how large the difference between each frame can be between each collision test. If more time has passed than the threshold, then the test is performed using the threshold instead of the actual delta. Then the threshold is subtracted from the actual delta. If the delta is still greater than the threshold, then the process repeats. If not, then a final check is done using the remaining delta. If an actual collision is detected at any point in time during this process, the tests stop.

The reason this works is that it keeps the characters moving along at their expected paces. If a character normally walks at a pace of 1 foot per frame at 60 frames per second, and 1 full second passed between one frame and the next, that character would be 60 feet farther along the path than expected. He might literally appear to have walked straight through a house or into a mountainside. The above code works by ensuring that at the collisions are always tested 1 foot at a time. If a full second were dropped, then 30 individual collision tests would be run, with each test moving the character forward 2 feet at a time.

I could probably add a max frame count for how many framed could be dropped. Hopefully, though, no frames are dropped. And as of now, at least they won't be forgotten if they are.
Comments:
Leave a Comment
Submit