Game Development Community

dev|Pro Game Development Curriculum

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.

//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
};

#endif

And 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.

www.yorkshirerifles.com/random/ai_pose_fixed.jpg
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. :)

#1
12/02/2013 (12:45 pm)
great one, Steve
#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.
#5
12/03/2013 (4:58 pm)
This needs adding to the MIT version...

Nice one Steve :-D
#6
12/03/2013 (8:30 pm)
I owe you another drink Steve!
#7
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
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
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.

i-dropper.net/torque/beer_envelope.jpg
#11
12/05/2013 (4:53 am)
@Steve

I get a server connection error, sumfink about ghosts...
What could it be?
#12
12/05/2013 (9:39 am)
Finally someone has hunted this mythical bug. We should report to the press.

Great job man.
#13
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
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??