A simple translation animation

Translation is an overly fancy word for moving an object along one or more axis. Here’s an example where we draw a single point traveling across the screen:

love.load()
    -- note that the lack of a "local" keyword at the beginning makes
    -- this a global variable by default in lua, which is why we can 
    -- reference it from the other callbacks.
    point = {
        x = 10,
        y = 10
}
end

love.update(dt)
    point.x = point.x + 1
end

love.draw()
    love.graphics.points(point.x, point.y)
end

If you were to copy this code into main.lua and run LÖVE right now, it would work; it would draw a single tiny point across the screen, one point to the right every time it draws a frame. But you’ll notice that I’ve not actually used the dt value from love.update(). So Jim’s potato computer from the 90s moves really slowly, while Leslie’s 480Hz gaming PC moves so fast she just thinks she’s looking at a blank screen. To make sure that it runs the same on everyone’s machines, we need to do math with dt.

Time-based smooth animations

In order to understand how to use time, you have to understand dt or whatever your framework’s concept of delta time is. Almost universally, the symbol for delta time will represent a fraction of a second.

If you’re good at algebra, you know all the properties of numbers that will make everything make sense immediately. But I’m an idiot, so I will explain it in idiot terms.

Since dt is a fraction of a second, if you sum up every update callback’s dt, when that sum reaches 1, one second will have passed. So let’s imagine that we ahave a screen that refreshes 10 frames per second. So our delta time will be one tenth of a second, or in absolute terms, 0.1.

The easiest way to think of translation animations is going to be in terms of pixels per second. Let’s say we want that pixel to move 100 pixels per second. You would use this line instead:

    point.x = point.x + 100 * dt

For another way to visualize what’s happening in our hypothetical you can imagine you’re doing 100 * 1 / 10 - which will give us 10 pixels. Since we are moving 10 frames per second, we can multiply that by 10 to get where we will be after one second and we will get 100 - the number we multiplied by dt.

Of course, our target devices will not be running 10 frames per second. They will be running any number of different frequencies, and our game code will take time to run, too, so dt could be any number. And that means that the value of point.x could be fractional even. But don’t worry, pretty much every framework will handle this.

This math doesn’t just work for translations. You can use them for any number smooth frame-by-frame animations, such as scale, rotation, shearing, etc. Of course, we can also apply limits very easily

    while point.x < 500 do
        point.x = point.x + 100 * dt
    end

Though this solution has an obvious flaw: we can overshoot where we want this to go! A potential better solution is as thus:

    if point.x >= 500 then
        point.x = 500
    else
        point.x = point.x + 100 * dt
    end

That way when it reaches or exceeds our destination, it will just make sure that it’s at that specific point.

Frame-triggered animations

So now that you understand how you use time math to make animations stay the same speed, but what do you do if you have time-based animation? Say, for instance, a sprite’s idle animation. You’re not going to want fractional pixel differences, you’re going to want to have animation frames switch out at specific times. If I give you a minute you might figure out how to do it yourself.

Go ahead. I’ll wait. Come back and we can compare answers.

….

You done? Good.

You realized you have to keep track of the time, right?

Here’s a basic frame animation example

love.load()
    frames = {
        -- frames go here
    }
    currentTime = 0
    finishTime = 2  -- 2 seconds, that is
    frameIndex = 1  -- Lua tables are 1-indexed, so we start here
end

love.update(dt)
    currentTime = currentTime + dt
    if currentTime >= finishTime then
        -- reset currentTime
        currentTime = 0
        --increment frameIndex
        frameIndex = frameIndex + 1
        -- if we passed the number of frames, we loop to the beginning
        -- "#" is the operator that returns the length of a table in lua
        if frameIndex > #frames then
            frameIndex = 1
        end
    end
end

love.draw()
    love.graphics.draw(frames[frameIndex], 20, 20)
end

I think the code speaks for itself (or it better with all those damn comments). But you might notice something really unideal if you know about how Lua scopes variables by default. All of the variables we defined in love.load() are global! Between the one object we are animating, the background we’ll need, and the UI, the code will really be spaghetti before we even have a game ready! With that in mind, you should already figured out that code organization will be extremely important when making a game engine. It’s such a critical issue that I’d honestly say it’s the single most important part of this book.

Next: Objects as Objects