Previous: Using the physics engine

Sprites. Animated sprites.

So why do we need these sprites, after all? Let's discuss a practical example:

That's a screenshot from a level designed by Jonas Noell during his school internship at Conitec, and I think that it looks pretty good. You can find it on the Gamestudio Download page. Now take a good look at the picture below:

Well, it's the same shot, but some of the magic has disappeared! The fence is gone, the light bulb flares are gone, and even those inscriptions on the left side of the screen are gone now! And yes, you are right; all these level elements were sprites. You can use any painting program to create your sprites; I use Paint Shop Pro because it is pretty cheap and it does its job well, but you can use the more expensive Photoshop, or even the free Gimp. Simply paint your 2D bitmap, save it as a .pcx, .bmp, .tga or .dds file, and then add it to your level.

I feel that the (poor) artist within me wants to burst out, so fire up your painting program and let's create a sprite right now:

- Start with a 256 x 512 pixels image, using a red background for it:

- Draw 2 black lines (RGB = 000) just like in the picture below:

- Now fill the interior with a metallic gradient:

- Fill the red parts with black (RGB = 000):

We will now add an alpha channel to our sprite; that's a special layer that controls the transparency of our sprite. Select the whole image, then choose Selections -> Save to alpha channel, and then save your sprite as a 32 bit TGA file.

I will add this sprite to the level, replacing one of the original flare sprites. If it looks like this...

... you've probably saved the bitmap in PCX, BMP or in a 24 bit TGA format instead of 32 bit - this makes it lose its transparency! Only 32 bit TGA images have that magic alpha channel that lets you see through the light beam. One final touch: let me check the BRIGHT flag in WED (your WED dialog could look a little different):

The BRIGHT flag lets the entity add its brightness to the background, and makes it thus look as if it were illuminated by a light source, which is exactly what we need here. You wouldn't think that a single flag would change our sprite that much, would you? I might quit my job as a programmer and start creating sprites for a living instead! :)

Don't forget that you can create oriented sprites and billboards; read the 9th workshop for more information.

Sprite animation

It is now the time to entertain ourselves with some animated sprites; start Lite-C, and then open and run script20.c:

That blurred "Acknex rules!" text is an animated sprite with 29 frames; take a look at the logo+29.pcx file inside the workshop20 folder if you want to see how it looks. Now let's press the [E] key:

We have triggered an explosion! In fact, we have played the animation for the explosion sprite. Press [E] again if you want to trigger another explosion. There are two types of sprite animations: looped animations and one-shot animations.

An animation is played in a loop if its frames are played more than once. You will want to use a looped animation for a fire effect, or for the signs that hang above the stores in your city, for a monitor that displays all kinds of weird / alien symbols in a loop, and so on. On the other hand, you will want to use a one-shot animation for every sprite that plays its animation frames only once and then stops: an explosion, a teleport effect, and so on. Of course that you can trigger the same one-shot animation several times (for example, to create several explosions) but a looped animation will simply repeat its frames over and over without stopping.

I know, I know... I've talked about these animation frames and you are now wondering what they mean? Let me show you how a bitmap for an animated sprite looks like:

This bitmap has 5 squares = 5 animation frames. The engine will display these bitmaps one by one, starting with the bitmap on the left side of the screen, and moving toward the right side of the screen. If the animation speed is big enough, the player won't notice that he is staring at a bitmapped slide show - he will think that he sees a "real" animation.

So how do you create an animated sprite? Well, you could get some free sprites from Acknex Unlimited, the website that offers free resources for Gamestudio and Lite-C developers, or you could create them in any painting program. Choose the proper size for a frame, and then create several frames and paste them into a single bitmap. If your frame has 128 x 32 pixels and you've got 4 animation frames, the resulting bitmap must have = 512 x 32 pixels, because 512 = 128 x 4, get it? Oh, and don't use those yellow frames that appear in the picture above; I drew them because I wanted you to see where an animation frame begins and where it ends.

You can use as many frames as you like for your animated sprites; please be aware that even though more frames will create better looking effects, they will use more memory. A pcx sprite that has 256 x 128 pixels will need 256 x 128 x 2 bytes = 65,536 bytes = 64 Kb. This means that if your sprite has 16 animation frames, it will use about 64 Kb x 16 = 1Mb of video memory.

Don't forget that you can (and you should) use the same sprite or model over and over whenever it is possible. The memory that is used for it is allocated only once; therefore, it is better to use the same sprite 10 times in your level instead of using 2 different sprites. Want to create a huge explosion? Create 10 identical explosion sprites simultaneously in the level, placing them close to each other!

The last thing to remember for now is the naming convention for sprites. You see, lite-C isn't a mind reader device, so it doesn't know what you plan to do with a certain sprite! So it's nice from you to tell it something like this: "Dear lite-C, I'd like you to treat this sprite as an animated sprite, if it is possible! Can you do that for me?" How do you do that? By choosing a proper name for the animated sprite. Here are a few examples:

fire+5.dds

lightning+32.tga

my_explosion+12.bmp

Do you see what I see? You can choose almost any name for your sprite, but you have to add "+" and the number of frames for that sprite. What happens if I forget to give my animated sprite a correct name? It is treated like a regular, non-animated sprite! See for yourself:

The first picture (WED's side view) shows the same bitmap that was placed in the level using two different names. Once again, I'm using the same bitmap file, but the engine has detected that muzzle+5.pcx is a sprite with 5 animation frames, and has split the bitmap in 5 pieces (5 frames). The second picture shows how these sprites look in the engine; muzzle+5.pcx will behave like an animated sprite, while muzzle.pcx will display all its frames at once. I have added those violet frames in the second picture because I wanted to make the things easier for you; they won't appear if the bitmap doesn't contain them.

You know that we can use .pcx, .bmp, .tga, or .dds images for our sprites. They all look the same in our bitmap painting program, so what's the difference between them? Well, the engine uses the bitmap format in order to decide whether to store the images in low quality (16 bits = 2 bytes) or high quality (24 bits = 3 bytes or 32 bits = 4 bytes with alpha channel). PCX and BMP images are stored in 16 bits. This saves valuable video memory, but it is not recommended for cool transparent effects like explosions or special effects - better use TGA files for them. DDS images are a special kind of bitmaps that normally require a plug-in or a converter for saving in a bitmap painting program, but they are worth the effort because they have a better quality tha PCX / BMP images and use less memory.

Allow me to show you the relevant part of the script20 file:

///////////////////////////////
action acknex_loop()
{
   while (1)
   {
     my.frame += 0.8 * time_step;
     if (my.frame >= 30) my.frame -= 29;
     wait (1);
   }
}
action explosion()
{
   my.ambient = 100;
   my.flags |= BRIGHT;
   while(1)
   {
     while(!key_e) wait (1);
     for (my.frame = 1; my.frame < 12; my.frame += 0.7 * time_step) wait (1);
   }
}
function main()
{
   vec_set(sky_color,vector(1,1,1)); 
// almost black sky
   level_load(""); // load an empty level
   ent_create("logo+29.pcx", vector(300,100,0), acknex_loop);
   ent_create("explo+11.tga", vector(300,-100,0), explosion);
}

I have used 2 animated sprites here: a looping "Acknex rules!" .pcx sprite and a .tga file for the one-shot explosion.

What makes a sprite loop-able? There aren't any restrictions for the programmer, but you will want to make sure that the last frame and the first frame are similar (not identical); otherwise, you will see that your animation loop isn't looking too good.

Let's take a look at the action that is attached to the "Acknex rules!" sprite first:

action acknex_loop()
{
    while (1)
    {
        my.frame += 0.8 * time_step;
        if (my.frame >= 30) my.frame -= 29;
        wait (1);
    }
}

The "Acknex rules!" plays its animation frames in a while (1) loop, which means that the animation starts running as soon as we start the engine, and ends only when we shut down the engine. We increase the animation frame by adding "0.8 * time_step" to the current animation frame all the time; play with 0.8 if you want to increase or decrease the animation speed.

Even if my.frame grows up to 100, we can't see more than 29 frames, because our bitmap hasn't got more frames, right? That's where the next line makes itself useful: it limits my.frame to this range: 1...29, allowing the sprite to loop its frames over and over.

Time to move on to the one-shot animations:

action explosion
{
    my.ambient = 100;
    my.flags |= BRIGHT;
    while(1)
    {
        while (!key_e) wait (1);
        for (my.frame=1; my.frame<12; my.frame += 0.7 * time_step) wait (1);
    }
}

The explosion sprite has an "ambient" value of 100 and its "BRIGHT" flag set; we had to use these settings because an explosion should look bright, right? This time I have created a .tga file that contains an alpha channel, so you can be sure that the explosion effect looks better.

Let's examine the following line of code:

while(1)
{
    while (!key_e) wait (1);
    ...

Two while loops nested in each other! The first while loop just tells the engine "repeat the following lines forever". And the nested while loop is already known to us from the last workshop; it instructs the engine to wait until key [E] is pressed. This means that the following line waits patiently until we press [E]:

for (my.frame=1; my.frame<12; my.frame += 0.7 * time_step) wait (1);

This is a for loop, a convenient way in Lite-C (and in C as well) to repeat several instructions until a certain limit it reached. A for loop has the following structure:

for (initialize counter; compare counter; increase or decrease counter)
{
    // do something
}

At first, the for loop initializes the counter (sets the counter variable to its initial value). Then, it performs a comparison (compare counter), checking if the counter has reached a specified limit or not. If the limit wasn't reached yet, the code from inside the body loop (do something) will be executed. As we've learned, the curly brackets can be omitted if "do something" consists of a single instruction. Finally, the counter is increased or decreased, according to our desire, and then the loop is repeated over and over, until the "compare counter" comparison resolves to false (the counter reaches its limit).

We're using my.frame for our counter variable, which is (as you have guessed), the current animation frame number for the my (aka me) entity. We set my.frame to 1 initially, so the sprite will display its first frame; then, we check if my.frame is still smaller than 12, because our sprite has only 11 animation sprites.

If the answer is affirmative, we "wait (1);" and then we add 0.7 * time_step to my.frame, making the sprite advance through its animation frames. The things repeat over and over until my.frame < 12 resolves to false, which means that my.frame is now equal to or greater than 12. The animation will be played only once, and then it will stop - that's what a one-shot animation is supposed to do! But the animation doesn't magically stop playing at its end; the outer while loop won't let it run again until we press the [E] key. Don't forget to play with 0.7 if you want to increase or decrease the animation speed.

By the way, do we really need these for loops? Can't they be replaced with while loops? Yes, they can, but you'd then need a little bit more code and I hope that you remember it: the programmers are lazy, which is the reason why the for loops were invented in the first place! It's time to prove that just like me, you are a lazy person! Replace the above for loop with a good old while loop.


Solution (don't read it!)

my.frame = 1;
while (my.frame < 12)
{
    my.frame += 0.7 * time_step;
    wait (1);
}

Next: Models. Animated models