Search code examples
javascripthtmlcanvasgame-loop

HTML5/JS - choppy game loop


I've been trying to make a simple game in HTML5/JS. I've managed to get a basic skeleton ready for the state machine and game loop, but even making a simple rectangle move around results in some 'tangible' choppiness.

What's more, after leaving it idle for some time (tabbing out), it seems to tick extremely fast, making a simple key-press move the rectangle far more than it should.

I'm using setTimeout for the game's updates. During each 'tick', I call the update function for the current state, which is

State.prototype.update = function(ms) {
    this.ticks += ms;

    var updates = 0;
    while(this.ticks >= State.DELTA_TIME && updates < State.MAX_UPDATES) {
          this.updateState();

          this.updateFrameTicks += State.DELTA_TIME;
          this.updateFrames++;

          if(this.updateFrameTicks >= 1000) {
              this.ups = this.updateFrames;
              this.updateFrames = 0;
              this.updateFrameTicks -= 1000;
          }

        this.ticks -= State.DELTA_TIME;
        updates++;
    }   

    if(updates > 0) {
          this.renderFrameTicks += updates*State.DELTA_TIME;
          this.renderFrames++;

          if(this.renderFrameTicks >= 1000) {
              this.rps = this.renderFrames;
              this.renderFrames = 0;
              this.renderFrameTicks -= 1000;
          }

          this.renderState(updates*State.DELTA_TIME);
    }

};

The idea is to call Game.update as frequently as possible using setTimeout, and then pass the elapsed time to State.update. State.update will update the state, only if the time accumulated by the state is greater than or equal to the fixed update time-step. If the state is actually updated, State.update will also render/redraw the current state, ensuring that the state presentation matches the state simulation.

Now, I know that requestAnimationFrame works better than setTimeout, but theoretically the current version should work, unless I've made a fundamental mistake.

This is what I have so far: http://jsbin.com/ogicec/1 (Edit)

You can clearly observe that it has fits of choppiness, and if you tab out for a long period and come back in, it seems to be running 'faster' than normal.

I can't really pinpoint what the problem is, so help would be really appreciated!

EDIT: I separated my update and render parts for the state, using setTimeout for update and requestAnimationFrame for render. It took care of the tabbing out problem, and the whole thing feels more consistent. But, at the same time, the performance is still choppy and I'd like to ensure it is smooth enough before going on to add more game related code.

Here is the updated JSBin: http://jsbin.com/eyarod/1 (Edit)


Solution

  • On my system, the JSBin test actually runs with UPS: 60 and FPS: 60 pretty much all the time (note I'm running it in chrome 25.0.1349.2 dev on an i7-2600k, and I have VSync turned on, so that's why the framerate would be limited). When I switch to a different tab and back the FPS goes haywire for a bit: I suspect that's related to the way browsers throttle setTimeout calls when the tab isn't active.

    More generally, you seem to be trying to shoehorn a message-polling busy wait loop into JavaScript, which is an event-driven language. I suspect (but do not know for sure), that adding an event handler that updates a shared object with the current x and y, and then having a separate loop to update the screen at a rate fixed to the monitor refresh rate would perform much better.