Hello, and welcome to the sixth in a series of in-depth articles about Kiwi.js v1.1.0, now available for use at www.kiwijs.org. Today we’re talking about time and how it relates to smooth motion. I’m Ben Richards, and today I’ll show you some incredibly useful, incredibly simple animation tools.
Who Needs to Read This Post?
All users. Kiwi.js is built to move sprites around the screen, and these tools make it possible to do this much more smoothly.
Time Properties
We’ve added some new properties to the Game and its components. Together with those already available, these will give you more control over your scene. Key properties include:
1 2 3 4 5 6 7 8 9 |
game.frame game.idealFrame game.frameRate game.time.delta game.time.elapsed game.time.now game.time.rate game.time.master.idealDelta |
Technical Information
This explains exactly how these properties work.
game.frame
This property counts the number of frames that the game has drawn since it loaded up.
This value is potentially inaccurate, because some frames may be dropped by the browser if it is busy. We recommend you use game.idealFrame
for most situations. However, game.frame
is guaranteed to be regular: it increases 1 per frame. It is thus useful if you value even intervals more than smooth time.
You can set the value of game.frame
yourself. This should not be done unless you know what you’re doing.
game.idealFrame
This property counts the number of frames that would have been drawn since the game loaded up, had it maintained a perfect frame rate.
Use this property to drive smooth cyclic animations. For example, consider a tree swaying in the wind. Simply set tree.rotation = 0.1 * Math.sin( game.idealFrame * 0.01 )
every frame, and it will sway smoothly, even if the frame rate slows.
Unlike game.frame
, this property cannot be changed by the user.
game.frameRate
This property controls the number of frames Kiwi.js attempts to render per second.
The maximum value is defined by your browser. It is normally 60. Kiwi.js defaults to 60 frames per second. You do not normally need to change this.
The minimum value is any number larger than 0. You might want a frame rate of 0.016, if you intend to update only once per minute. However, you cannot have a frame rate of 0.
When you set frame rate, it communicates to several behind-the-scenes components and updates some properties. Key among these is game.time.master.idealDelta
, which will be explained later.
game.time.delta
A delta is a unit of change. In this case, it refers to the number of milliseconds since the last frame.
Under normal circumstances, this should be about 16.7. There are 1000 milliseconds per second, and if you have 60 frames in that second, each frame takes (1000 / 60) = 16.7 ms.
This value is not as useful as some others, as we have provided useful derivatives, but it is available for your use.
game.time.elapsed
This measures the time, in milliseconds, since the game loaded up.
We use this value, divided by game.time.master.idealDelta
, to determine game.idealFrame
.
game.time.now
This property tells you the time as measured at the start of this frame. You should use this value instead of Date.now()
to ensure that all game objects are using the same value. It may take a few milliseconds to process the entire scene, and this may throw off certain calculations if you do not use a synced value.
game.time.rate
This property tells you how many ideal frames have passed since the last frame. It is one of the most useful properties in the time toolbox, and we’ll be spending some time explaining its nuances later in this article.
rate
is determined by the delta
divided by the idealDelta
. For example, if you have a framerate of 60, your ideal delta is 16.7ms. If your last frame took 16.7ms (the actual delta), then the rate
is 1.0. But if your last frame took 33.4ms, then your rate
is 2.0. This tells you that you should have had 2 whole frames in that time. This is very useful information for creating smooth animations.
Note that rate
makes no guarantees about the current frame. It cannot tell how long it will take to render. It can only say how long the last frame was. If you are using rate
to govern speed smoothing, and you have a very uneven frame rate, any individual frame may be slightly off from where it should be. However, this is better than the alternative, where any deviation in frame rate will permanently alter the offset. After 1000 frames of half-frame-rate performance, a non-compensated solution will be 500 frames off; but a solution that uses rate
to compensate will only ever be 1 frame off.
game.time.master.idealDelta
This property is calculated from game.frameRate
. It is precisely equal to the number of milliseconds that should be taken per frame. With the default frame rate of 60 frames per second, this is 16.7ms. Ideal delta is used to compute other parameters.
And Now, Physics
You probably don’t consider yourself a physicist. If you do, what are you doing here? We have a dark energy cosmological paradigm to analyse! Shoo, shoo! Back to your laboratories!
However, you know a thing or two about physics. You can’t help it. You know that, when a mushroom man jumps up in the air, he’ll come down again. And you know that he’ll sort of do a curving motion as he goes.
We’re not interested in drawing paths or elegant graphs. All we care about is movement between frames. Once a frame is gone, it’s forgotten forever. This makes it very easy to think about physics.
Some Terms
We need four terms to make elegant motion: time, position, velocity and acceleration.
t = Time
Time is key to everything. It’s a one-dimensional arrow pointing ever forward.
p = Position
Position is simply where you are at this moment. It has no interest in time. Position will not change unless there is velocity.
Position, like velocity and acceleration, can be expressed as a vector of multiple dimensions. These are usually x and y, and sometimes z or other hyperdimensions. Fortunately, we can consider each dimension separately, so we will consider only one.
v = Velocity
Velocity is the change in position over time. In other words, when time passes, position changes. In coding terms, p += v * t
. Velocity will not change unless there is acceleration. If velocity is negative, this simply means you are moving the other way. (This is in contrast with speed, which is always positive. A race car is not going at -100 if it turns around halfway down the track.)
a = Acceleration
Acceleration is the change in velocity over time. In other words, when time passes, velocity changes. In coding terms, v += a * t
. Acceleration represents forces in the world: the thrust of an engine, friction of a surface, or gravity pulling you down.
Let’s Dash
Here’s a mushroom man. He’s off to rescue the President. But he’s on a time limit – he can’t dawdle! So he runs along.
This code will look familiar to anyone who’s completed basic Kiwi.js tutorials:
1 2 3 4 5 6 |
mushroomMan.speed = 3; if( left ) mushroomMan.x += mushroomMan.speed; if( right ) mushroomMan.x -= mushroomMan.speed; |
This seems pretty straightforward.
Let’s Hop
Imagine that our mushroom man decides to jump into the air. Here’s some code to govern his trajectory (please don’t expect this to look good on screen, or function at all in Kiwi.js):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Acceleration points down, which is positive in screen coordinates. var acceleration = 9.8; // Velocity begins heading up mushroomMan.velocity = -100; // Position begins on the ground mushroomMan.y = 0; // Move in a parabolic trajectory while( mushroomMan.y <= 0 ) { mushroomMan.velocity += acceleration; mushroomMan.y += mushroomMan.velocity; } |
This code will send our mushroom man up into the air at his base velocity. But this velocity will gradually change, slowing down, until he hangs at the peak of his jump. Then he will accelerate downwards until he reaches the ground again.
So far, so good.
Temporal Distortion Strikes
The mushroom man has 5 minutes to reach the President. At a normal pace, he’ll get there in 3 minutes. All seems well.
But what happens when the computer running our mushroom man slows down? Say it’s running at half speed. Now the mushroom man will get there in 6 minutes! That’s too slow! (And his animation will be too fast, because it works on time, not frames. He’ll be running in place!) What can we do?
Well, we could change our time limit based on the frame rate, but that would be complicated and it wouldn’t make the game run any faster.
It’s much better to smooth out the mushroom man’s movement. And to do this, we’re going to use the rate
property.
Smooth Dash
This is a nice, simple fix. Remember that p += v * t
. All we need to do is multiply velocity by time. We had previously assumed that time was always 1 (a single regular frame); but because rate
tells us exactly how many frames should have passed since the last frame, we can use that instead.
1 2 3 4 5 6 |
mushroomMan.speed = 3; if( left ) mushroomMan.x += mushroomMan.speed * game.time.rate; if( right ) mushroomMan.x -= mushroomMan.speed * game.time.rate; |
Now, if the game is running at half frame rate, it doesn’t matter. The rate
is 2, so the mushroom man runs twice as far per frame.
Note that we are not altering the actual value of mushroomMan.speed
. This would quickly spiral out of our control: from 3 to 6 to 12 to 24. The mushroom man would hurtle uncontrollably into the distance. Soon he would reach the stars.
Smooth Hop
This one requires a little more thought. Velocity is not constant in a jump. It changes with time. However, this is no more complex than it has to be. When something changes with time, simply multiply by rate
and it will be corrected. The code becomes as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Acceleration points down, which is positive in screen coordinates. var acceleration = 9.8; // Velocity begins heading up mushroomMan.velocity = -100; // Position begins on the ground mushroomMan.y = 0; // Move in a parabolic trajectory while( mushroomMan.y <= 0 ) { mushroomMan.velocity += acceleration * game.time.rate; mushroomMan.y += mushroomMan.velocity * game.time.rate; } |
Now, when the mushroom man jumps in a low frame rate computer, he will move up further every frame, but his velocity will fall off faster every frame as well. Taken together, his trajectory is practically identical to that of a jump on a faster computer.
As you can see, the trajectory has some artifacts because of its disjointed state, but it’s better than slowing down, right? This diagram exaggerates the artifacts; in reality, only extremely fast or forceful interactions would be distorted this much.
Going Slower? Go Faster!
This is the fundamental purpose of rate
: to give you a speed governor, allowing you to move further when you slow down. We recommend you consider using rate
as part of your standard workflow.
We’ve already found rate
to be a useful asset in animating smooth action. We hope you do too!
In Review
When time slows down, you now have the tools to fix it. Just remember: if it changes with time, multiply by rate
.
Benjamin D. Richards
Recent Comments