Ai Poses Reduxed - Finally A Fully Working Version
by Steve Acaster · 12/02/2013 (11:56 am) · 18 comments
After much iteration, I finally managed to iron out all of the bugs and previous hacks to get a properly working version of having the AiPlayer use standard player poses/stances.
First up, add the mAiPose attribute to shapeBase.cpp and shapeBase.h files.
Next, we want to prevent the player code from intefering with Ai poses. At the bottom of UpdateMove() function.
And make sure that poses are being networked in PackUpdate() and UnPackUpdate(). Might as well throw swimming and jetting in here too or the AiPlayer will be in their state - but will not play the animation.
And now for AiPlayer.cpp and AiPlayer.h, we add the poses to getAiMove where the Player code cannot overwrite them.
And finally add functions which allow us to control the pose via script.
And the whole thing is controlled like this:
And there you go, a proper working AiPlayer pose system without hacks. Without the interference of Player updateMove() the AiPlayer's poses do not time-out or get overwritten at low fps.

A polycount of 86 million ... and no "bobbing", jumping or losing the crouch pose which was set via script.
And that's the end of my 50th resource post. :)
First up, add the mAiPose attribute to shapeBase.cpp and shapeBase.h files.
//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, we want to prevent the player code from intefering with Ai poses. At the bottom of UpdateMove() function.
//Player.cpp
//at the end of UpdateMove() function
//...
// Update the PlayerPose
Pose desiredPose = mPose;
if (!mIsAiControlled)//yorks new - leave the AiPlayer 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;
setPose( desiredPose );
}//yorks new
}And make sure that poses are being networked in PackUpdate() and UnPackUpdate(). Might as well throw swimming and jetting in here too or the AiPlayer will be in their state - but will not play the animation.
//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
//in unPackUpdate() function
//...
// MoveMask
if (stream->readFlag()) {
mPredictionCount = sMaxPredictionTicks;
mFalling = stream->readFlag();
//calls to update animations correctly
mSwimming = stream->readFlag();//yorks start
mJetting = stream->readFlag();
mPose = (Pose)(stream->readInt(NumPoseBits));
//yorks end
ActionState actionState = (ActionState)stream->readInt(NumStateBits);
if (stream->readFlag()) {
mRecoverTicks = stream->readInt(PlayerData::RecoverDelayBits);
setState(actionState, mRecoverTicks);
}
else
setState(actionState);
Point3F pos,rot;
//...And now for AiPlayer.cpp and AiPlayer.h, we add the poses to getAiMove where the Player code cannot overwrite them.
//AiPlayer.cpp
//near the end of getAiMove() function
//...
else
if (!mTargetInLOS) {
throwCallback( "onTargetEnterLOS" );
mTargetInLOS = true;
}
}
Pose desiredPose = mPose;//yorks in
//yorks select for Ai using mAiPose
if ( mSwimming )
desiredPose = SwimPose;
else if ( mAiPose == 1 && canCrouch() )
desiredPose = CrouchPose;
else if ( mAiPose == 2 && canProne() )
desiredPose = PronePose;
else if ( mAiPose == 3 && canSprint() )
desiredPose = SprintPose;
else if ( canStand() )
desiredPose = StandPose;
setPose( desiredPose );//yorks end
// Replicate the trigger state into the move so that
// triggers can be controlled from scripts.
for( int i = 0; i < MaxTriggerKeys; i++ )
movePtr->trigger[i] = getImageTriggerState(i);
//...And finally add functions which allow us to control the pose via script.
//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 the whole thing is controlled 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); bob.setAiPose(1);//sets AiPlayer Object named 'bob' to crouch pose
And there you go, a proper working AiPlayer pose system without hacks. Without the interference of Player updateMove() the AiPlayer's poses do not time-out or get overwritten at low fps.

A polycount of 86 million ... and no "bobbing", jumping or losing the crouch pose which was set via script.
And that's the end of my 50th resource post. :)
About the author
One Bloke ... In His Bedroom ... Making Indie Games ...
#2
12/02/2013 (6:23 pm)
Thanks Steve!
#3
12/02/2013 (7:55 pm)
You are awesome as usual, thank you.
#4
12/03/2013 (9:04 am)
gratz to the 50. resource post :) Like always,it is very usefully. Thanks for sharing.
#6
12/03/2013 (8:30 pm)
I owe you another drink Steve!
#7
I owe you too.
12/04/2013 (3:38 am)
Thanks Steve, saved me a lot of time.Quote:I owe you another drink Steve!
I owe you too.
#8
Steve
Yorkshire
UK
What could possibly go wrong? ;)
12/04/2013 (6:04 am)
Stick a stamp on a glass of beer and send it to:Steve
Yorkshire
UK
What could possibly go wrong? ;)
#9
I did what you asked and stuck a stamp in a glass of beer and put in in a envelope addressed to you. Though I had some difficulties getting the stamp in the envelope, but not to worry... managed somehow! Will bring it to the Chinese post office here tomorrow, hopefully they don't charge me extra for exporting alcoholic products.
12/04/2013 (6:42 am)
@Steve,I did what you asked and stuck a stamp in a glass of beer and put in in a envelope addressed to you. Though I had some difficulties getting the stamp in the envelope, but not to worry... managed somehow! Will bring it to the Chinese post office here tomorrow, hopefully they don't charge me extra for exporting alcoholic products.
#11
I get a server connection error, sumfink about ghosts...
What could it be?
12/05/2013 (4:53 am)
@SteveI get a server connection error, sumfink about ghosts...
What could it be?
#12
Great job man.
12/05/2013 (9:39 am)
Finally someone has hunted this mythical bug. We should report to the press.Great job man.
#13
Just tried it on a clean build and no errors here (release and debug) - though I am server and client. Haven't tested it on multiplayer because I only have one box.
Check you've not missed any of the bits that say "yorks".
12/05/2013 (9:55 am)
Nils,Just tried it on a clean build and no errors here (release and debug) - though I am server and client. Haven't tested it on multiplayer because I only have one box.
Check you've not missed any of the bits that say "yorks".
#14
12/05/2013 (6:19 pm)
Thanks Steve. Building goes error free. I used it on my own stuff; single player levels with UAISK home-grown bots running around. player.cpp and aiplayer.cpp aren't very original, so the chance of conflicting sumfink is there. I'll try to figure it out some time later. Thanks again!
#15
01/17/2014 (1:59 pm)
Steve, any pointers on how id get my ai to crouch when fired at or go prone after explosion occures close by, im using UAISK kit for my ai?? Any help would be great and thanks in advance....
#16
In the same way Ai on Ai would send the crouch function to the target from the attacker. Bit more difficult with Player vs Ai, though I think Bryce came up with a solution using triggers mounted on Ai to detect incoming shots.
01/17/2014 (4:36 pm)
Explosions should be easy, put a radius or distance check in the explosion function and any Ai within x range you can get their ID and tell them to prone.In the same way Ai on Ai would send the crouch function to the target from the attacker. Bit more difficult with Player vs Ai, though I think Bryce came up with a solution using triggers mounted on Ai to detect incoming shots.
#17
01/18/2014 (9:14 am)
Thank you for the great advice, if i get it worked out ill post it...
#18
01/23/2014 (8:58 pm)
using uaisk how would i get my aiplayers id number or how would i use it?? 
J0linar