Ai Poses Redux
by Steve Acaster · 05/07/2013 (6:29 am) · 6 comments
Pose change (stand, crouch, prone, sprint) has always been input driven, based off whatever the player is pressing on the keyboard at any one time (apart from swim and fall obviously).
Originally I had a system to mimick the player input, using the Player Class Trigger movement system and opening this up to the Ai during it's getAiMove() state and setting via script.
The most significant issue with this was that the Ai could fall out of it's scripted pose quite easily by slipping down terrain, falling, swimming, etc - and then reset itself in the default stand root pose. With the scripted event past and it's set triggers changed/cleared, it had no reason to revert to the chosen pose.
To prevent this I decided on a stored variable mAiPose, so that the Ai could always check it's current pose was the correct one set via script rather than an accidental clearing of move triggers.
Because I wanted this to affect ONLY AiPlayer's I started in ShapeBase, so that during the player's UpdateMove() function, I could differentiate between the AiPlayer and a human Player. Player uses input for pose changing, and we don't want our Ai system to affect that.
First up, slap in our new mAiPose variable.
Next up, we split up Player's UpdateMove() function so that this can never affect a player, and so that Ai's ignore their move triggers and use our stored AiPose variable. Replace the standard trigger->desiredPose system with this.
Next for AiPlayer.cpp and adding new functions to control mAiPose.
And there you go, recompile the whole thing. The Ai will now go into different move poses correctly, changing speed and bounds to suit, and they won't "bounce" out of it back into default stand root pose using the new setAiPose() function.
In script it works like this:
Unfortunately ...
Of course this does not help with the minor/major ghosting/Ai objects actually animating correctly. Not being a real programmer I'm currently having to call the pose every tick during pack and unpack to make sure that the Ai animated correctly.
So ... caution ahead for this next piece of code.
And now it will actually play the correct animations to go with the already correctly updated pose state and move speed/bounds/etc.
If anyone has a better solution, feel free to post it.
Originally I had a system to mimick the player input, using the Player Class Trigger movement system and opening this up to the Ai during it's getAiMove() state and setting via script.
The most significant issue with this was that the Ai could fall out of it's scripted pose quite easily by slipping down terrain, falling, swimming, etc - and then reset itself in the default stand root pose. With the scripted event past and it's set triggers changed/cleared, it had no reason to revert to the chosen pose.
//stock code from Player.ccp getMove() else if ( runSurface && move->trigger[sCrouchTrigger] && canCrouch() ) //but if sCrouchTrigger has already been accidentally cleared during movement ... then it's not going to call for crouch ... :(
To prevent this I decided on a stored variable mAiPose, so that the Ai could always check it's current pose was the correct one set via script rather than an accidental clearing of move triggers.
Because I wanted this to affect ONLY AiPlayer's I started in ShapeBase, so that during the player's UpdateMove() function, I could differentiate between the AiPlayer and a human Player. Player uses input for pose changing, and we don't want our Ai system to affect that.
First up, slap in our new mAiPose variable.
//shapeBase.cpp
ShapeBase::ShapeBase()
: mDrag( 0.0f ),
mBuoyancy( 0.0f ),
mWaterCoverage( 0.0f ),
//...
mMoveMotion( false ),
mIsAiControlled( false ),//added comma yorks
mAiPose( 0 )//added yorks
{
// ...//shapeBase.h
//...
/// @name Physical Properties
/// @{
F32 mAiPose; //yorks new
F32 mEnergy; ///< Current enery level.
F32 mRechargeRate; ///< Energy recharge rate (in units/tick).
//...Next up, we split up Player's UpdateMove() function so that this can never affect a player, and so that Ai's ignore their move triggers and use our stored AiPose variable. Replace the standard trigger->desiredPose system with this.
//Player.cpp
//at the end of UpdateMove() function
//...
// Update the PlayerPose
Pose desiredPose = mPose;
if (!mIsAiControlled)//yorks new from here - leave the Player alone!
{
if ( mSwimming )
desiredPose = SwimPose;
else if ( runSurface && move->trigger[sCrouchTrigger] && canCrouch() )
desiredPose = CrouchPose;
else if ( runSurface && move->trigger[sProneTrigger] && canProne() )
desiredPose = PronePose;
else if ( move->trigger[sSprintTrigger] && canSprint() )
desiredPose = SprintPose;
else if ( canStand() )
desiredPose = StandPose;
}
else//yorks select for Ai using mAiPose
{
if ( mSwimming )
desiredPose = SwimPose;
else if ( runSurface && mAiPose == 1 && canCrouch() )
desiredPose = CrouchPose;
else if ( runSurface && mAiPose == 2 && canProne() )
desiredPose = PronePose;
else if ( mAiPose == 3 && canSprint() )
desiredPose = SprintPose;
else if ( canStand() )
desiredPose = StandPose;
}//yorks end of changes
setPose( desiredPose );
}Next for AiPlayer.cpp and adding new functions to control mAiPose.
//AiPlayer.cpp
//below getAiMove() function
//yorks start
void AIPlayer::setAiPose( F32 pose )
{
mAiPose = pose;
}
F32 AIPlayer::getAiPose()
{
return mAiPose;
}
//yorks end
//...
//and somewhere below in the engine defines
//...
//yorks start
DefineEngineMethod( AIPlayer, setAiPose, void, ( F32 pose ),,
"@brief Sets the AiPose for an AI object.nn"
"@param pose StandPose=0,CrouchPose=1,PronePose=2,SprintPose=3. "
"Uses the new AiPose variable from shapebase (as defined in "
"its PlayerData datablock)nn")
{
object->setAiPose(pose);
}
DefineEngineMethod( AIPlayer, getAiPose, F32, (),,
"@brief Get the object's current AiPose.nn"
"@return StandPose=0,CrouchPose=1,PronePose=2,SprintPose=3n")
{
return object->getAiPose();
}
//yorks end//AiPlayer.h
//in public at the bottom
//...
Point3F getMoveDestination() const { return mMoveDestination; }
void stopMove();
void setAiPose( const F32 pose );//yorks
F32 getAiPose();//yorks
};
#endifAnd there you go, recompile the whole thing. The Ai will now go into different move poses correctly, changing speed and bounds to suit, and they won't "bounce" out of it back into default stand root pose using the new setAiPose() function.
In script it works like this:
//0 = stand //1 = crouch //2 = prone ... which has no stock animations //3 = sprint //fall, swim, etc are taken care of automatically by the engine %your_aiplayer_name_or_id.setAiPose(%pose_number);
Unfortunately ...
Of course this does not help with the minor/major ghosting/Ai objects actually animating correctly. Not being a real programmer I'm currently having to call the pose every tick during pack and unpack to make sure that the Ai animated correctly.
So ... caution ahead for this next piece of code.
//player.cpp
//in packUpdate() function
//...
if (stream->writeFlag(mask & MoveMask))
{
stream->writeFlag(mFalling);
stream->writeFlag(mSwimming);//yorks start
//stream->writeFlag(mJetting);
stream->writeInt(mPose, NumPoseBits);//yorks end
stream->writeInt(mState,NumStateBits);
if (stream->writeFlag(mState == RecoverState))
//...//player.cpp
//unPackUpdate() function
//...
// MoveMask
if (stream->readFlag()) {
mPredictionCount = sMaxPredictionTicks;
mFalling = stream->readFlag();
mSwimming = stream->readFlag();//yorks start
mJetting = stream->readFlag();
Pose newPose = (Pose)(stream->readInt(NumPoseBits));
setPose(newPose);//yorks end
ActionState actionState = (ActionState)stream->readInt(NumStateBits);
//...And now it will actually play the correct animations to go with the already correctly updated pose state and move speed/bounds/etc.
If anyone has a better solution, feel free to post it.
About the author
One Bloke ... In His Bedroom ... Making Indie Games ...
#2
05/08/2013 (9:54 am)
very interesting ! Thanks for sharing.
#3
05/08/2013 (7:52 pm)
I see you got more redux....
#4
github.com/SteveYorkshire/Torque3D/tree/york
Not sure about making a pull request though, as I'm uncertain whether the un/packUpdate stuff is the best way of going about fixing the Ai animation problems ... :
edit: 16May2013 updated the link to the new, cleaned up fork, and made pull request
05/09/2013 (8:15 am)
After two days of being thwarted ... I've actually got github to play nice and created a branch.github.com/SteveYorkshire/Torque3D/tree/york
Not sure about making a pull request though, as I'm uncertain whether the un/packUpdate stuff is the best way of going about fixing the Ai animation problems ... :
edit: 16May2013 updated the link to the new, cleaned up fork, and made pull request
#5
05/09/2013 (4:56 pm)
Works with Torque3D 3.0! after I fixed an embarrassing typo ... :S
#6
You started to sound like a real programmer tho. Nevertheless this might be a nice solution. Thanks.
05/15/2013 (1:46 pm)
Quote:Not being a real programmer...
You started to sound like a real programmer tho. Nevertheless this might be a nice solution. Thanks.

Torque Owner David Robert Pemberton
www.deadlyassets.com