-Character animation-
Sure, its quite easy to load models and stuff. It takes minimal effort to load an MD2 model, and build switch animation sequences to produce a realistic looking character. But, if you've tried to really use the model interactively, you'll find a lot of problems crop up. For example, take the jump animation. If you simply did what is indicated in the following pseudo code,
whenever the jump key is pressed
Model.AnimationSequence:=jump;
You'll find that as long as the jump key is held, the character will remain in the air, and will not complete the jump, as is to be expected. You'll find yourself writing a lot of special conditions and stuff to handle animations.
What we would need is a method to handle animations and basically let the model itself worry about the other stuff like keeping track of whether to stay crouched, or jumped, etc. Animation sequences of a model, are states. A model enters an animation when a condition is satisfied, and exits from it when another condition is satisfied. So a character animation table would look very much like a state diagram, depicted below.
This is a simple state diagram consisting of three animation states, stand, jump crouch and crouchstand. As you can see, a model in a jumped state cannot stay jumped. The only transition a jumped model can make is back to the stand state. This is rather simplified, and putting more states makes the diagram much more complicated. So, once we agree that we really could do with a state machine for representing character animation, we should see what that involves.
Every state in our diagram, has one or more states to which it can transition. So, our basic state data structure would look sorta like this
    TState=record
                 ID:integer;
                 Name:string;
                 ChangeAllowed:boolean;
                 NextStates:array of integer;
                 Key:word;
                 Sequence:integer;
    end;
    TStateEnterEvent=procedure (State:integer) of object;
The event declared would be the mechanism by which the program would find out that a state change has actually taken place. The state class updation would take place as follows.
nextstate:=FindState(nextstateid);//nextstateid is
                                  //simply requested
                                  //it need not exist
                                  //for example, jump-jump
                                  //is invalid
//perform sanity checks like there are no states, etc
if not CanTransition then exit;//CanTransition indicates whether
                               //a particular state can be
                               //interrupted. For example, you
                               //can interrupt a stand state, but
                               //cannot interrupt a jump state
if states[nextstate].id=currentstate.id then exit;
if nextstate=-1 then //this means the next state requested
                     //was not found. So, we have to go to
                     //the current state's default successor,
                     //which is the 1st item in the next
                     //states array.
begin
     currentstate:=states[currentstate].nextstates[0].id;
     CanTransition:=states[currentstate].nextstates[0].CanTransition;
     //call the OnEnterState event here
     exit;
end;
//if we have come this far it means that
//we've got a valid successor
currentstate:=states[nextstate].id;
CanTransition:=states[currentstate].nextstates[0].CanTransition;
This would be the essence of the Update method of the TStateDiagram class. Here, the input parameter to this method would be only the next requested state. But who translates the keys/commands into state numbers? To better understand a flexible way of maintaining animated characters, take a look at the following hierarchy.
The input subsystem is, for the purposes of this discussion the keyboard, but can be anything, even a set of animation commands from a file. The Keymap, is the part of the hierarchy that converts the keycodes or whatever else into a set of unique values that the state diagram knows (In the given TState structure, its equivalent would be the Key field). The reason to have a Keymap is that, the actual events that trigger a certain value can be altered easily. For example, you could reassign keys that correspond to certain actions without having to change a whole lot of other things. TAnimatedCharacter is the class that would own a TStateDiagram, and is responsible for managing it. I would be better to have TAnimated character simply control mesh objects, who are separate entities. However, one might abandon that level of abstraction, and put the mesh handling code inside TAnimatedCharacter. TAnimatedCharacter, is also responsible for handling the events that originate from TStateDiagram, like OnStateEnter. This might be a bit of a pain to implement initially. But once you are done the results will be worth it, believe me.
The download accompanying this article is not source code as you might expect. This demo uses an older version of Lucifer for its implementation, and the source will be presented after the conversion to the new version of Lucifer is complete. However, I have uploaded an EXE demo, where the state diagrams for the bots are built by script. This gives total customisability to the bot maker, while leaving lesser work for the programmer and greater flexilbility to the framework. The bot demo is only console driven, so commands will have to be typed in. The command list is,
Command Description
LoadBot(<bot filename>); Loads a bot script file and executes it. There are two bots supplied with the example, so you can type LoadBot('bots\was.bot'); or LoadBot('bots\goblin.bot');
About; Displays about information
DrawFPS(<boolean>); Displays/hides the framerate display on the screen.
Quit; quits the application
The bot script files are simply text files, which I suggest you take a look at. I'll post the source code for a character animation shortly as part of Lucifer. The new version will be skeletal, with total customisability.
.>>Download the demo