Twinspire offers a simple straight-forward API for managing animations. Unlike most animation systems which takes two "states", if you like, one for the beginning and another for end state, this animation API gives you full control over your states.

Before we can use animations, we must first initialise it.

import twinspire.Animate.*;

...

animateMax = 100;
animateInit();

All the functions found in the Animate class of Twinspire are prefixed with animate so you can import the functions directly as above for convenience.

animateMax defines the number of animations we expect to use at maximum. This means that when we call animateMax, we will indeed fill 100 default values in the many arrays located in this class. This may sound bad, but remember this is what most games using traditional C and C-like languages will actually do. An assumed maximum number of particles, sound channels, polygons, etc, will be initialised like this and accessed directly by iterating over each. This is actually more efficient than the now standardised method of the Array of Structures approach. We can talk more in detail why this is the case later, but for now let's continue.

We now need to set up a deltaTime as our Animate class needs this to determine passage of time.

Let's do this:

var deltaTime:Float;
var lastTime:Float;

...

function init()
{
    animateMax = 100;
    animateInit();

    lastTime = System.time;
}

...

function render(g2:Graphics)
{
    deltaTime = System.time - lastTime;
    animateTime(deltaTime);

    // our render code

    animateResetTicks();
    lastTime = System.time;
}

You will also see we call animateResetTicks at the end of each frame as well. We need to do this to tell our animation class that any tick values that have reached their final value by the time the frame ends, it will clear to zero so it can restart from the beginning.

Let's take a step back and remember our code from our Creating a Back Buffer tutorial where we discussed using a back buffer to render graphics on-screen. Let's go back to this and animate our red rectangle across the screen.

First, we need to set additional variables:

var animMoveRect:Int;
var rectX:Float;

Followed by some extra initialisation:

rectX = 50;
animMoveRect = animateCreateTick();

animateCreateTick will give us back the index that this animation will refer to when we pass it in across other animate functions. This makes managing our animations a lot easier and we can refer to any animation at any point in our application without restriction.

Once we do this, we can now use this to update our render code:

bG2.begin(true, Color.Black);
bG2.color = Color.Red;
animateTick(animMoveRect, 1.0);
rectX = 50 + (animateGetRatio(animMoveRect) * 500);

bG2.fillRect(rectX, 50, 100, 100);

bG2.end();

animateTick is one of four main animation functions, taking the index of our animation and the number of seconds that should pass until this animation ends. When the animation ends, the animateTick function returns true, but we do not need to do this.

We then use animateGetRatio by passing in the index of our animMoveRect to retrieve a floating-point number representing the percentage between start and finish of the animation. We multiply this by 500 to effectively say: "We want to move the rectangle 500 pixels to the right in one second, starting from 50."

This is our full code:

package;

import twinspire.events.Event;
import twinspire.events.EventType;
import twinspire.Application;
import twinspire.Animate.*;

import kha.graphics2.Graphics;
import kha.System;
import kha.Color;

@:build(twinspire.macros.StaticBuilder.build())
class Game
{

    @:local
    var mouseX:Int;
    var mouseY:Int;
    var mouseDown:Bool;
    var mouseReleased:Bool;

    var deltaTime:Float;
    var lastTime:Float;

    var animMoveRect:Int;

    var rectX:Float;

    @:global
    function init()
    {
        Application.createBackBuffer(1280, 720);

        animateMax = 100;
        animateInit();

        rectX = 50;
        animMoveRect = animateCreateTick();

        lastTime = System.time;
    }

    function handleEvent(e:Event)
    {

    }

    function render(g2:Graphics)
    {
        var bG2 = Application.getGraphics2D();
        deltaTime = System.time - lastTime;
        animateTime(deltaTime);

        bG2.begin(true, Color.Black);
        bG2.color = Color.Red;
        animateTick(animMoveRect, 1.0);
        rectX = 50 + (animateGetRatio(animMoveRect) * 500);

        bG2.fillRect(rectX, 50, 100, 100);

        bG2.end();

		g2.begin(true, Color.Black);
        g2.color = Color.White;
        g2.drawScaledImage(Application.getBufferContext(), 0, 0, System.windowWidth(), System.windowHeight());
		g2.end();

        animateResetTicks();
        lastTime = System.time;
    }

}

Compile and test.

We could always reset the tick so that this animation will continue resetting and playing through.

To do this we need to update the code to the following:

if (animateTick(animMoveRect, 1.0))
{
    animateReset(animMoveRect);
}

Compile and see the results.

You can explore the many other functions of the Animation API currently available.

That's it for tutorials for now. Check back once we have more code to demonstrate!

Previous Tutorial -> Event Handling