Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
483 views
in Technique[技术] by (71.8m points)

javascript - get a smooth animation for a canvas game

How to get a better animation, dinamically, even when browser is busy or idle, for different devices which have different hardware capacity.

I have tried many ways and still cannot find the right way to make the game to display a better animation.

This is what i tried:

var now;
var then = Date.now();
var delta;

window.gamedraw = function(){

   now = Date.now();
   delta = now - then;

   if(delta > 18){
        then = now - (delta % 18);
        game_update();
   }

}

window.gameloop = setInterval(window.gamedraw,1);

18 is the interval value to update the game, but when browser is busy this interval is not good, and it needs to lower. How to get a better animation dinamically, even when browser is idle or busy ?

I suppose that the interval value is the problem, because if interval is lower then game animation is very fast, if this value is 18 then game animation is good but not when browser is busy, and I do not have idea how to change it dinamically.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

To get a smooth animation, you must :
? Synchronise on the screen.
? Compute the time elapsed within your game.
? Animate only using this game time.

Synchronizing with the screen is done by using requestAnimationFrame (rAF).
When you write :

requestAnimationFrame( myCalbBack ) ;  

You are registering myCalbBack to be called once, the next time the screen is available to draw on.
( If you know about double buffering (canvas are always double-buffered), this time is the next time the GPU will swap the draw buffer with the display buffer. )

If, on the other hand, you don't use rAF but a interval/timeout to schedule the draws, what will happen is that the draws won't get actually displayed until next display refresh. Which might happen (on a 60Hz display) any time from right now to 16.6 ms later.

Below with a 20ms interval and a 16 ms screen, you can see that the images actually displayed will be alternatively 16ms away OR 2*16ms away - never 20, for sure-. You just can't know, from the interval callback, when the actual draw will show. Since both rAF and Intervals are not 100% accurate, you can even have a 3 frames delta.

enter image description here

So now that you are on sync with the screen, here's a bad news : the requestAnimationFrame does not tick exactly regularly. For various reasons the elapsed time in between two frames might change of 1ms or 2, or even more. So if you are using a fixed movement each frame, you'll move by the same distance during a different time : the speed is always changing.

(expl : +10 px on each rAF,
16.6 display -->> rAF time of 14, 15, 16 or 17 ms
--> the apparent speed varies from 0.58 to 0.71 px/ms. )

Answer is to measure time... And use it !
Hopefully requestAnimationFrame provides you the current time so you don't even have to use Date.now(). Secondary benefit is that this time will be very accurate on Browsers having an accurate timer (Chrome desktop).
The code below shows how you could know the time elapsed since last frame, and compute an application time :

function animate(time) {
  // register to be called again on next frame.
  requestAnimationFrame(animate);
  // compute time elapsed since last frame.
  var dt = time-lastTime;
  if (dt<10) return;
  if (dt >100) dt=16;  // consider only 1 frame elapsed if unfocused.
  lastTime=time;
  applicationTime+=dt;
  //
  update(dt);  
  draw();
}

var lastTime = 0;
var applicationTime = 0;
requestAnimationFrame(animate);

Now last step is to always use time inside all you formulas. So instead of doing

x += xSpeed ;

you'll have to do :

x += dt * xSpeed ;

And now not only the small variations in between frames will be taken into account, but your game will run at the same apparent speed whatever the device's screen (20Hz, 50Hz, 60 Hz, ...).

I did a small demo where you can choose both the sync and if using fixed time, you'll be able to judge of the differences :

http://jsbin.com/wesaremune/1/

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...