Animation Agent Script
|
The Animation Agent Script (.AAS file) controls a model’s physical behavior (the way that animations are applied to character movement). Animation sequences are executed based upon a variety of model inputs such as BaseMode, BasePosture, etc. Behaviors may be further refined through user-defined inputs.
Channels
A character will have one or more named animation channels. A channel masks off various bones (defined in Maya or 3ds Max) that it affects. All characters must have at least the one predefined channel named "AnimAllBody", that includes all bones by default.
The "AnimAllBody" channel must always have an animation playing, or the character will revert to root pose. The other channels, if any, may or may not have an animation playing as necessary.
Each channel's animation overrides the channel above it in the list based on the blend factors specified for each bone. So at 100%, the animation completely overrides. At 50%, it is a 50/50 crossfade.
- Note on the actual skeleton rig exported from 3dsMax or Maya, 0-1 values are used for 0 - 100%. for example .5 = 50% and 1 = 100%
Any animation sequence can be played on any channel.
From the HJ Sunok creature script we have:
channels { AnimAllBody WeaponRightHand Impact }
In this example, we have three channels. The required "AnimAllBody" channel, and two others. The "WeaponRightHand" channel masks off just the bones of the hand and is perfect for controlling that hand's open and closed state by running a suitable single frame animation on that channel.
Inputs
Then there are the inputs to control behaviors. An input is essentially a named enumeration of values. A given input can be exactly one of the values specified at any given time.
The character controller often has very specific inputs that it expects. For example, the PlayerBehavior2 controller (default for Hero's Journey), expects inputs of BaseMode and BasePosture and others.
An input can only be changed externally (via a behave command or the character controller) unless it is marked as variable
. In that case an input can be modified by the change
command in the AAS itself.
The following code sample from monkeytok.aas shows the standard input definitions followed by user-defined variables. For instance, the variable Location was defined to allow behavioral adjustment dependent on whether the creature is flying or walking.
inputs { BaseMode = Normal, Combat BasePosture = Idle, Turning, Moving IdleType = Normal MoveDirection = Forward, Backward TurnDirection = None, Right, Left TurnAmount = None, T45Degrees, T90Degrees, T135Degrees, T180Degrees variable AlreadyTurning = true, false variable Attacking = Laser, Swipe, FlySlash, None variable CurrentMotion = Idle, TurningLeft, TurningRight, Forward, Backingup variable Special = None, Backflip, Howl, MonkeyBusiness, Heal, Fly, Land, Flips, Laser, Swipe, FlySlash variable ImpactHit = None, Blam variable Location = OnGround, Hovering WeaponHand = Closed, Opened }
Input values can be adjusted directly in the animation agent script:
change AlreadyTurning to False
Note that this only works if the channel is marked as variable
, otherwise it is a run-time error.
Or through HSL scripts using the Behave command mechanisms:
For the server:
$BEHAVE._SendBehave(aCharacter, "input WeaponHand Opened")
On the client, you can manipulate the behave property directly:
creatureNode["Behave"] = "input WeaponHand Opened"
or use the _doBehave() method which allows a user-defined HE_DoBehave() method to override it:
$BEHAVE._doBehave(creatureNode, "input WeaponHand Opened")
Predefined inputs expected (required) by the PlayerBehavior2 character controller include:
Input Name | Recognized Values |
BaseMode | Normal, Combat |
BasePosture | Idle, Turning, Moving, Dead, Prone, Kneeling |
IdleType | Normal |
MoveDirection | Forward, Backward |
SideDirection | Left, Right |
TurnDirection | None, Left, Right |
TurnAmount | None, T45Degrees, T90Degrees, T135Degrees, T180Degrees |
WeaponHand | Opened, Closed |
Examples
The following examples show behave commands for the Humans and Suwari races in Hero's Journey:
Human/Suwari
Behave | Description |
input IdleType Normal | Normal idle based on gender |
input IdleType Sitting | Character is seated, legs draped over ledge... useful only for screenshots right now. Turn off snap and place by hand. |
input IdleType Conversation | Femaleish "chatting with friends" idle |
input IdleType Muted | Special very-limited motion for use in character manager (varies by gender) |
input IdleType Sneak | Sneaking idle. |
input IdleType Hammer | Hammer pounding animation. |
input IdleType Trowel | Trowel animation |
Input Attacking AttackName | Valid AttackName: None, DownSlashRight, StepDownSlashRight, SideSlash, SpellCastLeft, SpellCastCrossarm, ComboFlyingSpinKick, SlashSpinSlash, JumpSlashBackSlash, SpellCastHealGroup, SpellCastSuperSpell, SpellCastSweep, SpellCastBuff, SpellCastGroundImpact, Impact, BowShoot, BowSlam, JumpSlashBackFlip, BackJumpSlashBackFlip, BackSlashSpinSlash, SoloFlyingSpinKick, BowShootLong, BowSnipeNock, BowSnipeRelease, BowShootHiLow, BowShootLongHiLow |
input Special Option | Valid Options: VictoryCheer, VictoryYes, Hammer, Trowel, Cower, Cry, Wave, Hug |
input IdleType Sneak | Rogueish still posture, listening around a corner |
Kneel | Character assumes a crouched/kneeling position. |
Transitions
When an input changes value, a specific block of Animation Agent Script (an Action) can be designated to execute. This is done by specifying transitions of an input from one value to another and what named code block to execute.
Transitions allow the override of defined behaviors to handle special character states like stunned, paralyzed or dead. Transition blocks are defined with the keyword transition followed by name of the transitional input. The various state changes determine which Action will be executed.
Transition statement format:
from state1 to state2 = action
A full transition block:
transition BasePosture { from Idle to Dead = Die from Turning to Dead = Die from Moving to Dead = Die }
Actions
Actions are executable named code blocks. This is the action block for the death transition above:
action Die { force(1) AnimAllBody { anim "combat_death" hold 1 blend 0.1 align false looping false change AlreadyTurning to False change CurrentMotion to Idle change Attacking to None } }
The main body of the animation agent falls under the Default action. This is a partial listing for the wood sprite.
action Default { // Basic behavior for the whole body set(1) AnimAllBody { when BaseMode { is Combat: when ImpactHit { is Blam: if (BasePosture = Idle) { anim "impact" blend 0.1 hold 1 looping false align false change ImpactHit to None stop } } when BasePosture { is Idle: anim "idle" align false blend 0.25 hold 0.25 seconds recycle looping true change AlreadyTurning to False change CurrentMotion to Idle } is Normal: when Special { is IdleCrouch: anim "idle_crouch" looping true hold 1 align true blend 0 change Special to None change AlreadyTurning to False change CurrentMotion to Idle stop // AAS continues further...
AAS Keywords
ALIGN
The ALIGN keyword specifies if the Animation Sequence specified by the ANIM command will be "aligned" with the end of the currently playing animation on the channel (specified in the containing SET/FORCE construct).
The syntax is:
ALIGN [true|false]
So if you want the Animation Sequence to start from the beginning when the currently playing sequence ends, you'd use:
ALIGN TRUE
This is important when you have animations that are designed to match up at their "cut" point. So the ending of one leads precisely into the beginning of the next. Normally an Animation Sequence will play based on the global animation clock. So it might, for example, be half-way through the animation by "wall clock" when it starts. If you want this to occur, use:
ALIGN FALSE
Keep in mind that the specified Animation Sequence by the ANIM keyword may not start immediately, it depends on the cross-fade you want specified by the BLEND keyword.
This keyword is only valid within a SET/FORCE construct, and will only do something if an ANIM keyword is also encountered.
ANIM
- See also: Character Behave Commands#ANIM
Play the specified Animation Sequence. The Animation Sequence must be defined in the ANIMATIONSET.DAT file.
Syntax:
ANIM “animation_sequence_name”
Example:
ANIM “walkrun”
This keyword is only valid within a SET/FORCE construct. The SET/FORCE construct establishes which channel the Animation Sequence will operate on.
It is valid to specify an Animation Sequence of "" which means to play no sequence, or to clear the playing animation on a channel. For example:
ANIM "" // Play nothing on this channel
This is valid for anything but the AnimAllBody
channel which must always have some Animation Sequence playing on it. If not, the character will revert to "root pose" and an error message will be displayed.
Be sure to specify how long to "hold" the playing of the Animation Sequence with the HOLD keyword.
CALL
Used to invoke another Action in the agent script. After that action completes, execution continues with the next statement after the call (unless a stop is encountered along the way).
Syntax:
CALL {animation_actionblock_name}
Example:
CALL Die
CHANGE
Change a variable input value.
Syntax:
CHANGE variable TO value
Example:
Where the input CurrentMotion is defined as
variable CurrentMotion = Idle, TurningLeft, TurningRight, Forward, Backward
use
CHANGE CurrentMotion TO TurningLeft
To change the CurrentMotion
input value to TurningLeft
.
Note that only inputs marked as variable
can be modified with the CHANGE command. Without the variable
keyword, the inputs can only be changed externally (via the character behavior system, script, etc.).
HOLD
The HOLD keyword specifies the amount of time to "hold" the priority of the playing animation sequence on the current channel. After this amount of time has elapsed, the priority on the channel reverts to 0.
The syntax is:
HOLD percentage
This format lets you specify the hold time as a percentage of the duration of the animation sequence about to play (via the ANIM keyword). So that:
HOLD 1.0
Means to hold for the full duration of the Animation Sequence, or 100%. Likewise:
HOLD .5
Would mean to hold for half (or 50%) of the duration of the Animation Sequence.
You can also specify a precise amount of seconds to hold with this format:
HOLD .25 seconds
This means to hold for precisely one quarter of a second regardless of the length of the Animation Sequence.
HOLD .25 seconds recycle
Recycle modifies a looping sequence such that it is forced to reevaluate any decisions it made (such as evaluation of a multiplexer) to determine what sequence (animation) will ultimately be played. This is useful if you have a multiplexer animation for idles that occasionally adds in a flavor idle (such as the character stretching) that you do not want to happen repeatedly (in general).
This command is only valid within a SET/FORCE construct, and will only do something if an ANIM keyword is also encountered.
BLEND
The BLEND keyword specifies the amount of "Cross-Fade" to perform between the prior animation sequence on the current channel and a new sequence specified with an ANIM keyword.
The syntax is:
BLEND percentage
This format lets you specify the blend time as a percentage of the duration of the animation sequence about to play (via the ANIM keyword). So that:
BLEND 1.0
Means to blend for the full duration of the Animation Sequence, or 100%. Likewise:
BLEND .5
Would mean to blend for half (or 50%) of the duration of the Animation Sequence.
You can also specify a precise amount of seconds to blend with this format:
BLEND .25 seconds
This means to blend for precisely one quarter of a second regardless of the length of the Animation Sequence.
This command is only valid within a SET/FORCE construct, and will only do something if an ANIM keyword is also encountered.
IF/ELSE
Standard conditional logic structure.
Syntax:
IF (conditional phrase) { Executable statement block } ELSE IF (conditional phrase) { Executable statement block } ELSE { Executable statements block }
Example:
if (AlreadyTurning = true) { align true }
SET/FORCE
The keywords SET and FORCE begin a code block for a particular specified channel. The code block is only executed if the specified priority is greater than (in the case of SET) or greater-than-or-equal to (in the case of FORCE) the priority of the animation currently playing on the animation channel. This also sets the priority for the channel once an animation is specified by the ANIM keyword.
Syntax:
set('''priority''') '''ChannelName''' { } force('''priority''') '''ChannelName''' { }
So the structure:
set(5) AnimAllBody { ... }
Will only execute if the current priority of the channel named "AnimAllBody" is less than 5. If FORCE(5) were used instead of SET(5) in this example, then the code block would execute if the priority was 5 or less.
- Remark: This text first uses "greater than" terminology then switches to "less than" terminology. Which is correct?
This also establishes the priority for this channel at 5 (in this example) once an ANIM keyword is executed. The priority can be overriden with the PRIORITY keyword.
All commands related to an animation, such as ANIM, HOLD, LOOPING, etc. refer to the specified channel for this code block.
Managing the priorities of channels is the key to the AAS system. Using SET() and FORCE() establish a channel to be acted upon, but only if the priority of the currently running animation is low enough. Note that the priority on a channel automatically drops to 0 once it's hold time expires (see the HOLD keyword).
STOP
The stop
keyword causes the execution of the script to halt at that point.
Syntax:
stop
WHEN/IS
Standard switch statement for case evaluation. If there is no case match, logic drops through to the case named DEFAULT.
Syntax:
WHEN switch_variable { IS switch_value1: Executable statement block IS switch_value2: Executable statement block DEFAULT: Executable statement block }
Example:
when TurnDirection { is Right: when TurnAmount { is T45Degrees: anim "turn_right_45" is T90Degrees: anim "turn_right_90" is T135Degrees: anim "turn_right_135" is T180Degrees: anim "turn_180" } change CurrentMotion to TurningRight is Left: when TurnAmount { is T45Degrees: anim "turn_left_45" is T90Degrees: anim "turn_left_90" is T135Degrees: anim "turn_left_135" is T180Degrees: anim "turn_180" } change CurrentMotion to TurningLeft }
Creating a sequence name from an input
The Animation Agent Script (AAS) language also is the ability to create a sequence name from an input. Here is an example from CyberStrike Redux:
First, ensure that all animation sequences that are part of a logical group have the same prefix. In CSR, emotes are a logical group so they were all named like this:
- emote_bashful.asq
- emote_curtsey.asq
- emote_flip.asq
- ... etc ...
The reason is that in the Animation Agent script, you can cut down a lot of code by mapping inputs to the ASQ name:
inputs { variable EmoteType = None, Playing, bashful, curtsey, flip, jog_in_place, moonwalk, spin, wave }
then later on...
if (EmoteType != None) { anim str("emote_",EmoteType) blend 0.1 hold 1 align false looping false change EmoteType to Playing stop }
Notice how the str("emote_",EmoteType) did a string concatenation to achieve the different animation sequence names, such as: "emote_bashful"
This makes building your animation agent script easier because you then don't need a seperate WHEN/IS clause for every emote, for example. As your ASQs grow in number, this can be very helpful!