Reader small image

You're reading from  Learning Game AI Programming with Lua

Product typeBook
Published inNov 2014
Reading LevelBeginner
PublisherPackt
ISBN-139781783281336
Edition1st Edition
Languages
Right arrow
Author (1)
David Young
David Young
Right arrow

Chapter 3. Character Animations

In this chapter, we will cover the following topics:

  • Loading animated meshes in the sandbox

  • Attaching meshes to animating bones

  • The sandbox soldier and weapon animations

  • Blending multiple animations

  • Creating a Lua animation state machine

  • Building a soldier and weapon animation state machine

With a basic sandbox under our feet and a primitive agent representation, we can now move on to handling animations so that our agents can finally begin to look human. While we could assign a basic human mesh to our agents so far, they could never really look human without animations.

Animations play a key role in not only the visual aspect of the AI, but also the functionality of the AI. This might seem counter-intuitive, but the sandbox AI can only perform behaviors that the AI has animations for. If an animation takes 5 seconds to play out and can't be interrupted, our AI needs to take this into account before it decides to execute an animation; otherwise, our AI loses all reactiveness...

Skeletons and meshes


First, we need to cover some basics on how animations are represented and used by the sandbox.

Mesh skeletons

So far, we've learned how to create mesh representations within the sandbox. We're now going to deal with an additional asset called a skeleton. Ogre, which is the underlying renderer for the sandbox, stores the skeleton and animations within a single .skeleton file. Any animated Ogre mesh within the sandbox references its animations from the media/animations folder, and in the case of our soldier, animations are located at media/animations/futuristic_soldier/futuristic_soldier.skeleton.

Loading an animated mesh

Loading an animated mesh is exactly the same as loading a normal mesh within the sandbox. Simply create an animated mesh with the Core.CreateMesh function:

Sandbox.lua:

function Sandbox_Initialize(sandbox)
    local soldier = Core.CreateMesh(
        sandbox,
        "models/futuristic_soldier/" ..
        "futuristic_soldier_dark_anim.mesh");

Showing a skeleton...

Attaching meshes to bones


Attaching a mesh to a bone is exactly what it sounds like. Each time the animation updates the skeleton, the attached mesh is repositioned to stay in sync with the bone it is attached to. The sandbox provides an Animation.AttachToBone function that attaches a mesh to a bone and allows for a position and rotation offset from the bone.

Tip

Once a mesh has been attached to the bone, you should discard the variable to the mesh, as it is no longer valid. It is best to assign the variable to nil in order to avoid errors.

Attaching a weapon to our soldier

Attaching a weapon to our soldier is very easy with the Animation.AttachToBone function. Given the correct offset values, we can position a sniper rifle grip into the soldier's hand:

Sandbox.lua:

function Sandbox_Initialize(sandbox)

    ...

    local weapon = Core.CreateMesh(
        sandbox,
        "models/futuristic_soldier/" .. 
        "soldier_weapon.mesh");

    Animation.AttachToBone(
        soldier,
        "b_RightHand...

Animation clips


Now that we have a mesh with a skeleton, we can start talking about how skeletons become animated. Ogre's animation system is based on animation clips and forward kinematics. What this all boils down to is the use of a hierarchical setup where each child bone is a position and orientation offset to its parent bone.

Each animation clip stores a number of key frames that pose the entire skeleton at a specific point in time. In between these key frames, Ogre will interpolate each bone position in order to create an animated skeleton.

Playing an animation on our soldier

Playing an animation on our soldier requires three important parts. First, we need to acquire the animation based on its name; then, we need to enable the animation; and then, we need to update the animation during our normal sandbox update loop.

Note

It is the sandbox's responsibility to step the animation based on the delta time the sandbox updates with.

Obtaining an animation is performed by the Animation.GetAnimation...

Soldier poses


Typically, animation clips are authored based on a set number of poses that an agent can be in. For example, our melee animations are only authored to be blended together with our soldier's standing idle aim pose.

Poses are very important when we lay out which animations our agent has at its disposal at any given time. Otherwise, we'll need to transition our agent into another pose first before playing our desired animation.

Our soldier model has five basic poses we need to be aware of: his standing idle aim pose, his running aim pose, his running pose, his crouch aim pose, and lastly, his crouch walk pose.

Almost all of the soldiers animations can be played quickly from the standing idle pose as the pose has no deviations in the movement during the playback.

The standing idle aim pose

In contrast, playing animations from the run pose will require some compensation for the animations' movement and possible foot positions.

The standing forward run aim pose

The standing forward run...

Manipulating animations


A number of API calls allow you to manipulate the underlying animations backed by Ogre. While Ogre maintains the actual animation state, the sandbox is responsible for manipulating and updating these animation states.

Enabling and disabling animations

The enabling and disabling animations determine whether the animation will contribute to the skeleton's current pose. If multiple animations are enabled, they will combine in an additive manner to determine the skeleton's final pose.

Note

As none of our soldier's animations were authored to be played simultaneously with one another, additional steps are required to blend multiple animations together.

To determine if an animation is already enabled, we can use the Animation.IsEnabled function:

local enabled = Animation.IsEnabled(animation);
Animation.SetEnabled(animation, true);

Looping animations

Animation clips have an additional attribute, which is known as looping. Non-looping animations will freeze at their last keyframe...

Animation blending


Animation blending is the process of playing multiple animations on a single mesh at the same time. Some key things to note about animation blending are that the associated playing animations must be carefully balanced against one another; otherwise, this will result in artifacts such as clipping or impossible skeletal orientations.

We use animation blending to prevent what is commonly known as animation pops. These animation pops happen when the position of a bone becomes discontinuous as the bone moves. Typically, this shows up as an abrupt change in pose in a time span that is unbelievable to an observer.

Blending helps alleviate pops by smoothing out abrupt skeletal changes as well as allowing our soldier to blend together two animations that were not authored to be played back to back.

Animation weights

Animation blending is done through the manipulation of how much each animation contributes to the skeleton. As animation weights must be between 0 and 1, the animation...

Animation state machine (ASM)


Currently, our animation blending is a bit verbose and requires a lot of manual handling from Lua. Now, we're going to create a system that will manage animations and blends for us based on the concept of a finite state machine (FSM). We'll call this system an animation state machine but in essence, it's an FSM where states are animations and transitions between states represent the blend window, blend duration, and animation offsets.

States

Animation states in Lua will be represented by a table with a few key pieces of information. First, we need to store the animation, state name, whether the state is looping, and at what rate the animation will be played. The ASM will contain one animation state for each animation we support for our soldier.

Note

You can find the book's implementation of an animation state in the src/demo_framework/script/AnimationState.lua file.

Laying out the AnimationState class will create an initialization function which assigns default values...

Building a weapon animation state machine


Now that we've built an entire animation state machine system, it's time to build the ASMs our soldier will use. As the soldier's weapon is the simplest ASM, we'll start there first. With only five different animations, this is a relatively easy ASM to build.

First, we add each of the states with their corresponding animations and specify whether the animation is looping or non-looping. Next, we connect each state via transitions. Lastly, we request a state within the ASM to start playing a looping animation; in this case, this is the sniper_idle state.

The sniper/submachine gun animation state machine

Note

The reload animation for the weapon only works when the weapon is in its sniper pose. The ASM must reflect this; otherwise, you will see a noticeable pop on the sniper pose before the reload animation plays.

Creating the weapon ASM primarily consists of adding each state, the states corresponding to the animation to play, and creating transitions between...

Building a soldier animation state machine


Building the soldier's ASM is very similar to the weapon ASM, except that we have a lot more animations to think about. In this case, we're only going to flush out the standing idle and standing run poses of the soldier. A spoke-wheel-designed ASM can easily accomplish each of the animation states with their corresponding transitions.

One large restriction to this simple design is that whenever an animation plays, we must return to the idle stand animation before being able to play another animation. In a fully connected ASM, you can add additional transition animations to go from reload to fire immediately, for example:

The soldier animation state machine

You should pay attention to where transitions exist as well as their blend durations, as this will ultimately affect how reactive your agent will appear. As this design requires going back to the idle_aim state for a minimum of the blend duration, there will always be a slight delay before an agent...

Updating animation state machines


Now that we've created both the soldier and weapon ASMs, we need to add an update call within the sandbox update loop. This allows for both ASMs to update whatever animations they are playing:

Sandbox.lua:

function Sandbox_Update(sandbox, deltaTimeInMillis)
    soliderAsm:Update(
        deltaTimeInMillis, Sandbox.GetTimeInMillis(sandbox));
    weaponAsm:Update(
        deltaTimeInMillis, Sandbox.GetTimeInMillis(sandbox));
end

Playing with states


We can now start playing with animation states within our ASMs. In this case, we have two ASMs that need to be kept in sync, so we create a global weaponState variable that will check whether our weapon is in the sniper rifle pose or the SMG pose.

Next, we can bind each of the number pad keys to request specific states within our soldier and weapon ASMs. Special case handling is required for the transform states as well as the reload state:

Sandbox.lua:

local weaponState = "sniper";

local function IsNumKey(key, numKey)
    -- Match both numpad keys and numeric keys.
    return string.find(
        key, string.format("^[numpad_]*%d_key$", numKey));
end

function Sandbox_HandleEvent(sandbox, event)
    if (event.source == "keyboard" and event.pressed) then
        if (event.key == "f1_key") then
            Sandbox.SetCameraPosition(
                sandbox, Vector.new(0, 1, -3));
            Sandbox.SetCameraForward(
                sandbox, Vector.new(0, 0, -1));
    ...

Summary


From playing a simple animation clip to creating a full-fledged animation state machine, we've begun to bring life to characters. At the end of the day, animations play a very important role, not just in how our AIs will look, but what our AIs can do at any given moment.

Now that we have a stateful representation of when and what our agents will look like while executing specific behaviors, we can move forward to the next chapter and start binding the visual look of the agent with the agent's decision making.

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Learning Game AI Programming with Lua
Published in: Nov 2014Publisher: PacktISBN-13: 9781783281336
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)