Previous: Sprites. Animated sprites

Models. Animated models.

If you plan to create a game, you will want to use models for your vehicles, characters, and so on. Well, you could even spice up your levels by hanging some models on the walls, just like I did with the torch model from the picture below:

You don't have to be a rocket scientist if you plan to use non-animated models in your game; use WED's "Add model" to place them in the level and move them where you want them. However, a torch that doesn't flicker looks dull and boring. How can we use those great looking, animated models? There are two types of animated models:

1) Models that use animation frames (also called vertex animations).

2) Models that use bones for animation.

The first type of models uses animation frames, just like a sprite; don't forget that a model is a 3D object, though. I'll start the free model editor MED; let's take a look at a model that uses animation frames:

The animations can be played in a loop (for standing, walking, running, and so on) or we can have one-shot animations (for jumping, ducking, and so on). You can use many animation frames for your models; the amount of video memory that is used by an animated model is given mostly by the size of its skin; its number of animation frames has little influence until you reach several hundreds of animation frames.

A regular model has several animations, and this is how the frame names look in MED:

Ok, so MED might not show you the frame ranges using different colors (I did that using a painting program) but the point I'm trying to make is this: any animation should have a distinct name. If you have several "death" animations for a model, you could name them this way:

- deatha1... deatha30 (the first "death" animation has 30 frames)

- deathb1... deathb5 (the second "death" animation has 5 frames)

- rip1... rip8 (the third "death" animation has 8 frames)

What I'm trying to say here is that the name of the animation isn't that important, but it has to be a distinct name for every animation. So how do we write the code that uses these animations? Start Lite-C, open workshop21 and then run script21.c:

The guard on the left side of the screen is playing its "walk" animation, but the guard on the right side of the screen doesn't do anything! Ok, let's wait for a few more seconds then...

I see! The guard on the left is performing a looped animation (walk) and the guard on the right has performed a one-shot animation (death)! Let's examine the script file:

///////////////////////////////
var walk_percentage;
var death_percentage;
function main()
{
   video_mode = 7;
   level_load ("work21.wmb");
   wait (2); 
// wait until the level is loaded
   camera.z = 120; // choose a convenient height
   camera.tilt = -15; // and tilt angle for the camera
}
action walking_guard()
{
   while (1)
   {
     ent_animate(my,"walk",walk_percentage, ANM_CYCLE); 
// "walk" animation   loop
     walk_percentage += 3 * time_step; // 3 = animation speed for "walk"
     wait (1);
   }
}
action dead_guard()
{
   wait (-5); // wait 5 seconds
   while (1)
   {
     ent_animate(my, "death", death_percentage, 0); 
// "death" one-shot   animation
     death_percentage += 2 * time_step; // 2 = animation speed for "death"
     wait (1);
   }
}

I liked lite-C from its very beginning because you can do a lot of stuff using only a few lines of code; take a look at the action that animates the walking guard:

action walking_guard()
{
    while (1)
    {
        ent_animate(my, "walk", walk_percentage, ANM_CYCLE); // "walk" animation loop
        walk_percentage += 3 * time_step; // 3 = animation speed for "walk"
        wait (1);
    }
}

We are using a while(1) loop because our "walk" animation must play all the time; let's discuss the following line of code:

ent_animate(my, "walk", walk_percentage, ANM_CYCLE);

This instruction animates the "my" entity (the entity that has the action attached to it) using its "walk" animation loop, with the speed given by the variable named "walk_percentage", and plays the animation in a cycle (in a loop) because of the anm_cycle parameter. Here is the general form of the "ent_animate" instruction:

ent_animate(entity_name, animation_name, animation_percentage, animation_mode);

- "entity_name" is the name of the entity that will be animated. You don't have to use "my" here if you don't want to; define a pointer to an entity first and you will be able to use its name too.

Example:

ENTITY* kung_fu_master;

ent_animate(kung_fu_master, "rofl", masters_speed, ANM_CYCLE);

- "animation_name" is the name of the animation taken from MED (but without the figures associated to the frames). Don't forget to use quotes for the animation name;otherwise, you will get an error message. You can use any name for your animations in MED, but you must use the same name in your script file:

Ex: ent_animate(my, "smelling", nose_speed, ANM_CYCLE);

- "animation_percentage" is the percentage of the animation. This is a simple var that must change its value if you want to animate the model; if animation_percentage is 0, the model displays its first animation frame and if animation_percentage = 100 the model displays its last animation frame.

Our guard model has only 4 animation frames for walking (walk1... walk4) but the animation doesn't look choppy at all in our demo! In fact, if you take a good look at the pictures below, you will notice that the guard appears to have many more animation frames! This feature, named smooth frame interpolation, is built inside Lite-C. The engine computes intermediate animation frames and places them between the physically existing frames, making the animation look much smoother even if the model has only a few frames.

The variable named "walk_percentage" from our example increases its value all the time, and this is why our walking model is always animated; play with "3" if you want to increase or decrease the animation speed.

- "animation_mode" tells the engine to play the animation in a cyclic loop (animation_mode == ANM_CYCLE) or to play a one-shot animation (animation_mode = 0). If the animation_percentage value grows over 100 in a cyclic animation, it will just continue with the first frame again; on the other hand, a one-shot animation will stop at the last frame.

That's all you need to know about "ent_animate" if you plan to animate your models using animation frames. And to make the things even more clear, let's take a look at the action that is attached to the model that plays its one-shot "death" animation:

action dead_guard()
{
    wait (-5); // wait for 5 seconds
    while (1)
    {
        ent_animate(my, "death", death_percentage, 0); // "death" one-shot animation
        death_percentage += 2 * time_step; // 2 = animation speed for "death"
        wait (1);
    }
}

The dead guy will wait for 5 seconds before starting the animation; as you know, when calling wait() with a negative parameter, the engine waits the given number of seconds rather than frame cycles. Use something like wait (10); to wait shorter intervals (10 frames in my example).

The ent_animate line will play the "death" animation using the death_percentage variable. This is a one-shot animation, because "animation_mode" is set to zero. Play with "2" if you want to increase or decrease the animation speed.

I have used a while (1) loop for the dead guard; this loop will continue to run even if the model has played its last "death" animation frame, doing... nothing, but wasting CPU power. A more elegant version of the action would look like this:

action dead_guard()
{
    wait (-5); // wait for 5 seconds
    while (death_percentage <= 100) // death_percentage will only range from 0 to 100 here
    {
        ent_animate(my, "death", death_percentage, 0); // "death" one-shot animation
        death_percentage += 2 * time_step; // 2 = animation speed for "death"
        wait (1);
    }
}

The while loop from above will run for as long as death_percentage is smaller than or equal to 100, stopping as soon as death_percentage grows over 100 (the last "death" frame was played) and allowing the CPU to forget about this (now useless) while loop.

Didn't you miss our little homework? You actually did? Then why don't you try to create a simple action for a model that is animated using its "walk" animation and moves using "c_move" at the same time? Use action walking_guard as a base (it is playing the "walk" animation in a loop already) and then add a single line that moves the entity using "c_move" inside the while(1) loop.

 

Solution: add a line of code to action walking_guard:

action walking_guard()
{
    while (1)
    {
        c_move (me, vector(2 * time_step, 0, 0), nullvector, GLIDE);
        ent_animate(me, "walk", walk_percentage, ANM_CYCLE); // "walk" animation loop
        walk_percentage += 3 * time_step; // 3 = animation speed for "walk"
        wait (1);
    }
}

 

Next: Walking, collision, events