Time Delta Calculations in HTML5 Canvas

I am new to game development. I am currently making a game for the js13kgames competition, so the game needs to be small and therefore I am not using any of the modern popular frameworks.

While designing my endless game loop, I found several articles and tips for implementing it. Now it looks like this:

self.gameLoop = function () {
        self.dt = 0;

        var now;
        var lastTime = timestamp();
        var fpsmeter = new FPSMeter({decimals: 0, graph: true, theme: 'dark', left: '5px'});

        function frame () {
            fpsmeter.tickStart();
            now = window.performance.now();

            // first variant - delta is increasing..
            self.dt = self.dt + Math.min(1, (now-lastTime)/1000);

            // second variant - delta is stable.. 
            self.dt = (now - lastTime)/16;
            self.dt = (self.dt > 10) ? 10 : self.dt;

            self.clearRect();

            self.createWeapons();
            self.createTargets();

            self.update('weapons');
            self.render('weapons');

            self.update('targets');
            self.render('targets');

            self.ticks++;

            lastTime = now;
            fpsmeter.tick();
            requestAnimationFrame(frame);
        }

        requestAnimationFrame(frame);
};

      

So the problem is self.dt

In the end I found out that the first option is not suitable for my game, because it increases forever and the speed of the weapon increases with it (for example, this.position.x += (Math.cos(this.angle) * this.speed) * self.dt;

..

The second option looks more appropriate, but is this kind of loop ( http://codeincomplete.com/posts/2013/12/4/javascript_game_foundations_the_game_loop/ ) appropriate ?

+3


source to share


6 answers


A great solution for your game engine would be to think about objects and objects. You can think of everything in your world as objects and objects. Next, you want to create a game object manager that will have a list of all of your game objects. Next, you want to create a generic communication method in the engine so that game objects can trigger events. Entities in your game, such as a player, would not need to be assigned anything to be able to render to the screen or detect collisions. You would just do generic methods on the entity that the game engine is looking for. Then let the game engine handle the entity the way it wants. Objects in your game can be created or destroyed at any time in the game, so you shouldn't hardcode any objects in the game loop.

You want other objects in your game engine to respond to event triggers received by that engine. This can be done using methods on the entity, which the game engine will check to see if the method is available and whether the events will pass events to the entity. Do not code any game logic into the engine, it spoils portability and limits your options for further development of the game.

The problem with your code is that you are calling different objects first and the updates are not in the correct order. You need to name ALL your updates and then name ALL your renders in that order. Another is that your method of hardcoding objects in a loop will give you a lot of problems when you want one of the objects to no longer be in the game, or if you want to add more objects to the game later.

Your game objects will have update()

and render()

your game engine will look for that function in the object / entity and call it every frame. You can get very fancy and make the engine work in such a way as to check if the game / object has functions before calling them. for example, maybe you want an object to have a function update()

but never display anything on the screen. You can make the game object functions optional by checking the engine before calling them. It is also good to have a function init()

for all game objects. When the game engine starts the scene and creates objects, it starts by calling game objects the init()

first time the object is created, then every frame that callsupdate()

this way you can have a function that you only run once on creation. and another that starts every frame.

delta time is not really required as it window.requestAnimationFrame(frame);

will give you ~ 60fps. Therefore, if you are tracking the number of frames, you can tell how much time has passed. The various objects in your game can then (based on the setpoint in the game and the number of frames) determine how long it took to do something based on the new number of frames.



window.requestAnimationFrame = window.requestAnimationFrame || function(callback){window.setTimeout(callback,16)};
gameEngine = function () {
        this.frameCount=0;
        self=this;

        this.update = function(){
           //loop over your objects and run each objects update function
        }

        this.render = function(){
          //loop over your objects and run each objects render function
        }

        this.frame = function() {
            self.update();
            self.render();
            self.frameCount++;
            window.requestAnimationFrame(frame);
        }
        this.frame();
};

      

I have created a complete game engine located at https://github.com/Patrick-W-McMahon/Jinx-Engine/ if you are looking at the code at https://github.com/Patrick-W-McMahon/Jinx- Engine / blob / master / JinxEngine.js you will see a fully functional game engine built with 100% JavaScript. It includes event handlers and allows calls to actions between objects, which are passed to the engine using the event call stack. check out some examples https://github.com/Patrick-W-McMahon/Jinx-Engine/tree/master/examples,where you will see how it works. The engine can run about 100,000 objects, which are rendered and executed per frame at 60 frames per second. This has been tested on an i5 core. different equipment may differ. mouse and keyboard events are built into the engine. the objects passed to the engine just have to listen to the event passed by the engine. Scene control and support for multiple scenes are currently built in for more complex games. The engine also supports high pixel density screens.

Examining my source code should help you create a more fully functional game engine.

I would also like to point out that you should call requestAnimationFrame()

when you are ready to redraw, not earlier (otherwise at the end of the game loop). One good example of why you shouldn't call requestAnimationFrame()

at the start of a loop is if you are using a canvas buffer. If you call first requestAnimationFrame()

and then start drawing to the canvas buffer, you can end up making it draw half of the new frame and the other half the old frame. This will happen on every frame depending on the time it takes for the buffer to complete relative to the redraw cycle (60 frames per second). But at the same time, you end up overlapping each frame so the buffer gets more and more confusing as it loops on its own. This is why you must callrequestAnimationFrame()

when the buffer is completely ready for drawing on the canvas. having at the end requestAnimationFrame()

you can skip the redraw if the buffer is not ready to be drawn and therefore each is redrawn as expected. The position requestAnimationFrame()

in the game loop makes a big difference.

+6


source


Here's an implementation of an HTML5 rendering system using a fixed time step with a variable render time:

http://jsbin.com/ditad/10/edit?js,output

It is based on this article:

http://gameprogrammingpatterns.com/game-loop.html

Here's the game loop:

    //Set the frame rate
var fps = 60,
    //Get the start time
    start = Date.now(),
    //Set the frame duration in milliseconds
    frameDuration = 1000 / fps,
    //Initialize the lag offset
    lag = 0;

//Start the game loop
gameLoop();

function gameLoop() {
  requestAnimationFrame(gameLoop, canvas);

  //Calcuate the time that has elapsed since the last frame
  var current = Date.now(),
      elapsed = current - start;
  start = current;
  //Add the elapsed time to the lag counter
  lag += elapsed;

  //Update the frame if the lag counter is greater than or
  //equal to the frame duration
  while (lag >= frameDuration){  
    //Update the logic
    update();
    //Reduce the lag counter by the frame duration
    lag -= frameDuration;
  }
  //Calculate the lag offset and use it to render the sprites
  var lagOffset = lag / frameDuration;
  render(lagOffset);
}

      

The function render

calls a method render

for each sprite with a reference to lagOffset

function render(lagOffset) {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  sprites.forEach(function(sprite){
    ctx.save();
    //Call the sprite `render` method and feed it the
    //canvas context and lagOffset
    sprite.render(ctx, lagOffset);
    ctx.restore();
  });
}

      



Here's a sprite rendering technique that uses a delay offset to interpolate the rendering position of the sprite on the canvas.

o.render = function(ctx, lagOffset) {
    //Use the `lagOffset` and previous x/y positions to
    //calculate the render positions
    o.renderX = (o.x - o.oldX) * lagOffset + o.oldX;
    o.renderY = (o.y - o.oldY) * lagOffset + o.oldY;

    //Render the sprite
    ctx.strokeStyle = o.strokeStyle;
    ctx.lineWidth = o.lineWidth;
    ctx.fillStyle = o.fillStyle;
    ctx.translate(
      o.renderX + (o.width / 2),
      o.renderY + (o.height / 2)
     );
    ctx.beginPath();
    ctx.rect(-o.width / 2, -o.height / 2, o.width, o.height);
    ctx.stroke();
    ctx.fill();

    //Capture the sprite current positions to use as 
    //the previous position on the next frame
    o.oldX = o.x;
    o.oldY = o.y;
  };

      

The important part is the bit of code that uses lagOffset and the difference in rendering the sprite between frames to determine its new current canvas position:

o.renderX = (o.x - o.oldX) * lagOffset + o.oldX;
o.renderY = (o.y - o.oldY) * lagOffset + o.oldY;

      

Note that the values oldX

and are oldY

recalculated every frame at the end of the method, so they can be used in the next frame to help figure out the difference.

o.oldX = o.x;
o.oldY = o.y;

      

I'm really not sure if this interpolation is completely correct, or if this is the best way to do it. If anyone out there reads this, it knows it is wrong, please let us know :)

+4


source


The modern version of requestAnimationFrame now posts in a timestamp that you can use to calculate the elapsed time. After the specified time interval has elapsed, you can update, create and execute tasks.

Here's some sample code:

var lastTime;
var requiredElapsed=1000/100; // desired interval is 10fps

requestAnimationFrame(loop);

function loop(now){
    requestAnimationFrame(loop);

    if(!lastTime){lastTime=now;}
    var elapsed=lastTime-now;

    if(elapsed>requiredElapsed){
        // do stuff
        lastTime=now;
    }

}

      

+3


source


This is not an answer to your question, and without knowing more about a particular game, I cannot say for sure if it will help you, but do you really need to know dt

(and FPS)?

In my limited forays into JS game development, I've found that you often don't need to compute any dt

, as you can usually get a reasonable default based on the expected frame rate and do something based on timing (like reloading a weapon ) just work based on the number of ticks (i.e. the bow can take 60 ticks to reload (~ 1 second @ ~ 60FPS)).

I usually use window.setTimeout()

, and not window.requestAnimationFrame()

which I have found tends to provide a more stable frame rate that will allow you to define a reasonable default instead dt

. On the game side, the game will be more of a resource swamp and less workable on slower machines (or if the user has a lot of other things), but depending on your use case, this might not be a real issue.

Now, this is purely anecdotal advice, so you should take it with a lot of salt, but I've liked it a lot in the past. It all depends on whether you play the game slower on older / less powerful machines and how efficient your game loop is. If it's something simple that doesn't need to be displayed in real time, you can skip it entirely dt

.

+1


source


I haven't tested the math logic in your code .. however, here's what works for me:

GameBox = function()
{
    this.lastFrameTime = Date.now();
    this.currentFrameTime = Date.now();
    this.timeElapsed = 0;
    this.updateInterval = 2000;       //in ms
}

GameBox.prototype.gameLoop = function()
{
   window.requestAnimationFrame(this.gameLoop.bind(this));
   this.lastFrameTime = this.currentFrameTime;
   this.currentFrameTime = Date.now();
   this.timeElapsed +=  this.currentFrameTime - this.lastFrameTime ;
   if(this.timeElapsed >= this.updateInterval)
   {
      this.timeElapsed = 0;
      this.update(); //modify data which is used to render
   }
   this.render();
}

      

This implementation is independent of processor speed (ticks). Hope you can use this!

0


source


At some point, you will want to think about decoupling your physics from your rendering. Otherwise, your players may have inconsistent physics. For example, someone with a meaty machine taking 300 frames per second will have very accelerated physics compared to someone who has a problem at 30 frames per second. This might show up with the first player traveling around the world in a mario scrolling game at super speed, and another player crawling halfway (assuming you've done all your trials at 60 frames per second). The way to fix it is to introduce delta time steps. The idea is that you find the time between each frame and use that as part of your physical computation.It ensures consistent gameplay regardless of frame rate. Here's a good article to get you started:http://gafferongames.com/game-physics/fix-your-timestep/

requestAnimationFrame will not fix this inconsistency, but it is still useful sometimes because it has battery saving benefits. Here is the source for more information http://www.chandlerprall.com/2012/06/requestanimationframe-is-not-your-logics-friend/

0


source







All Articles