Wiki

Clone wiki

agtools / Tutorial 5c - Xenon's End

Outline

ts5c.png

This last step in tutorial 5 just adds some finishing touches which help underline methods we've seen so far.

Includes

For the first time, we pull in the random number generator. This is a 'mersenne twister' algorithm which produces good distribution, at some cost. Use it to make tables.

// random number source
#include "agtsys/rnd.h"

We define the generator here, and the random table we'll build with it. The table will be used for explosion offsets.

mersenne_twister rnd;

static s16 explode_randtab[512];

Setup

We then populate our random table:

    for (int i = 0; i < 512; i++)
    {
        explode_randtab[i] = (s16)(rnd.genU32() >> 5) >> (16-5);
    }

The first shift >> 5 is to get rid of the lower bits - notorious for poor random distribution with some generators, if not necessarily this one. The last shift by (16-5) leaves a 5-bit signed random value (-32 -> +31) which is fine for our explosion 'spread'.

We also load the big explosion asset we forgot to load last time.

Entity Setup

Next we assign a different countdown to each letter. We use the general purpose counter field for this. The rest remains as before.

    penew = EntitySpawn(EntityType_LETTER_X, g_viewport_worldx-250, g_viewport_worldy+c_xenon_yoffset);
    EntityMakeRelative(penew, g_viewport_worldx, g_viewport_worldy);
    penew->counter = 450;
    penew->wz = index++;

Mainloop

Some logic has been added to the mainloop to control the scrolling in a slightly more complex way - it accelerates and decelerates according to the current time window (ticks elapsed).

This simple tweak shows that the 'viewport' can later catch up with entities which left the view some time ago, and they continue to do their thing uninterrupted when out of view. It also shows that the playfield system can adapt to different scroll speeds without CPU spikes.

Tick Function

The letter entities have also been modified to implement a countdown to explosion. Each letter has a different initial counter value, so they explode one after the other in sequence.

When its counter elapses, each entity swaps its tick function for another one called letter_death_fntick. This second tick function spawns explosion objects at random offsets, of two different kinds (large and small). The sprite is also strobed using the 'hitflash' field in the entity (this is a modifier which causes the sprite to turn solid colour when incremented). After a short time, the letter deletes itself, leaving the explosions to burn out.

void letter_death_fntick(entity_t *_pself)
{
    if ((_pself->counter++ & 3) == 0)
    {
        // strobe the sprite
        _pself->draw_hitflash += 3;

        // spawn some flames

        s16 rand_index = (_pself->counter << 1) & 511;

        s16 xo = explode_randtab[rand_index + 0];
        s16 yo = explode_randtab[rand_index + 1];

        if (xo & 1)
            EntitySpawnRel(EntityType_SMALL_EXP, _pself, xo, yo);
        else
            EntitySpawnRel(EntityType_LARGE_EXP, _pself, xo, yo);

        // hide the sprite after a while
        if (_pself->counter > 20)
        {
            // turn invisible
            _pself->drawtype = EntityDraw_NONE;

            // after a bit longer, kill it off
            if (_pself->counter > 60)
            {
                EntityRemove(_pself);
            }
        }
    }
}

The default tick function performs the countdown and switch with the code fragment below. Note that swapping a tick function is a much more efficient route than spawning a new object of the same type, while destroying the old one. It's usually only worth spawning a replacement if the object really is of a different kind.

    // after some individually predetermined time...
    if (_pself->counter-- < 0)
    {
        _pself->counter = 0;

        // switch to cloud of explosions 
        _pself->fntick = &letter_death_fntick;
    }

Summary

So by this point we already have some powerful tools for displaying & controlling content in the game world. It's just a case of coming up with creative ways to use it. The basics are more or less covered (and I do mean basics - there are more things to play with here another time...).

We're still missing something though - the objects can't interact. We'll begin to deal with interactions in tutorial 6.

Updated