Game Development Community

dev|Pro Game Development Curriculum

Getting Ai to Use Poses

by Steve Acaster · 08/04/2011 (9:42 am) · 12 comments

Disclaimer: WestCoastCollege was instrumental in helping me work this out as I was sticking my code in the wrong place - so it's not all my own work.

2nd Disclaimer: I thought this was already a resource ... but apparently it isn't.

First up, let's expose the stock getPose function to script so we have an easy way to find out what pose the Ai is already in. At the end of player.cpp add:

[edit 17 oct 12] Apparently this defineEngineMethod is now in stock 1.2
//yorks
DefineEngineMethod( Player, getPose, S32, (),,
	"0=standPose, 1=crouchPose, 2=pronePose, 3=swimPose" )
{  
   return object->getPose(); 
}

Now open up AiPlayer.cpp, and near the end of AIPlayer::getAIMove function:

/...
   // 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);

//yorks in start
	switch (mPose) 
	{
	   case StandPose:
		     movePtr->trigger[3] = false; 
			 movePtr->trigger[4] = false;
		break;
		case CrouchPose:				
			movePtr->trigger[3] = true; 
			movePtr->trigger[4] = false;
		 break;
		case PronePose:
			movePtr->trigger[3] = false;
			movePtr->trigger[4] = true;  
		 break;
	}
//yorks in end

   }

   mLastLocation = location;
   
   return true;
}

//yorks - new function!
void AIPlayer::changePose(S32 poseNumber)
{
	Pose Pose = StandPose;
	if(poseNumber == 1) 
	{      
		Pose = CrouchPose;
	}
	else if(poseNumber == 2)
	{
		Pose = PronePose;
	}
	setPose(Pose);
}
//yorks

Expose this new function to script:
//yorks
ConsoleMethod( AIPlayer, setPose, void, 3, 3, "()"
			  "(int pose) StandPose=0,CrouchPose=1,PronePose=2")
{	
   object->changePose(dAtoi(argv[2]));
}
Anyone want to change my ConsoleMethod to a DefineEngineMethod feel free

And finally declare the new function at the end AiPlayer.h:
//...
   void stopMove();
   void changePose(S32 poseNumber);//yorks
};

#endif

So, you can now set the pose of an Ai by using:

//set the pose!
%myBotID.setPose(%pose);

//and return the pose
%myBotID.getPose();


[edit 17 oct 12] www.garagegames.com/community/forums/viewthread/130955/1#comment-831144 should fix the issues listed below

Now let's talk about issues with it not working as desired:

The main one is that there seems to be some sort of hiccup in the passing of the actual animation, so whilst the the boundingbox resizes correctly, the animation requires a "bump". This has been reported and confirmed as a bug.

But in the meantime, we'll use a workaround and "jumpstart" the animation.
function AiPlayer::doStand(%this)
{
   echo(%this.getname() @ " AiStand");
   if(%this.getPose() != 0)
   {
      %this.setPose(0);
      %this.schedule(50, "kickStartPose", 0);
   }
}

function AiPlayer::doCrouch(%this)
{
   echo(%this.getname() @ " AiCrouch");
   if(%this.getPose() != 1)
   {
      %this.setPose(1);
      %this.schedule(50, "kickStartPose", 1);
   }
}

function AiPlayer::doProne(%this)
{
   echo(%this.getname() @ " AiProne");
	
   if(%this.getPose() != 2)
   {
      %this.setPose(2);
      %this.schedule(50, "kickStartPose", 2);
   }
}

//and our kickstart bugfix workaround solution
function AiPlayer::kickStartPose(%this, %pose)
{
   echo("kickstart pose " @ %pose);
   if(%pose == 0)
      %this.setPose(0);
		
   if(%pose == 1)
      %this.setPose(1);

   if(%pose == 2)
      %this.setPose(2);
		
   %this.setTransform(%this.getTransform());
}

It's the setTransform at the end which helps to "jumpstart" the animation into the new pose, and it also helps if there is a slight delay - hence the schedule.

And there you go, you can now change the Ai's pose (as long as the model is set up with the correct animation0 by using any of the following:

%myBotId.doStand();
%myBotId.doCrouch();
%myBotId.doProne();

This also helps prevent previous issues where having a "prone-able" Ai could see them default to prone instead of standing.

#1
08/04/2011 (9:45 am)
that was quick. :)
#2
08/04/2011 (12:48 pm)
Nice work Steve, works great.
#3
08/05/2011 (12:35 am)
@Steve the account I used to reply to your other post:

http://www.garagegames.com/community/forums/viewthread/115567

was deleted and all of the post made with it were also deleted so you wont find it by searching, or all the hundred other things I posted :(

#4
08/05/2011 (7:08 am)
That'll explain why when I found that thread it looked like I was talking to myself ... which confused me slightly ...

Anyhow, accreditation where it belongs unlike LA Noire ...
#5
08/07/2011 (6:22 am)
...you know me, Steve, I have to ask: does this work across the network? Single Player is 'fine', MultiPlayer is FTW! Can I be on a dedicated server and have this work?

Thanks!

Quote:%myBotId.doStand);
.....missing opening parenthesis of 'doStand' function.


NB: I read at over 500 wpm....lol.....
#6
08/07/2011 (7:57 am)
It should work over a network as it's updating inside the Ai Class getAiMove function - plus the Ai are going to be on the server and not the client.

I suppose a foolproof way would be to call the pose change as a serverCommand.
commandToServer('AiStand', %botID);

function serverCmdGoStand(%client, %ai)
{
   %ai.AiStand();
}

Easiest way to find out is to test! I've only got the one box ...

And typo fixed.
#7
08/07/2011 (10:58 pm)
Nice work Steve =) Handy resource!
#8
08/09/2011 (9:53 am)
Great tutorial on AI Steve. :)
#9
11/22/2011 (11:25 am)
Steve @ Player::getPose is already a defined engine method in T3D 1.2 (fyi) ... testing out the rest of this resource.

Works great and yes, it works fine with UAISK ... for those using it.

I just had my AI "crouch" after getting hit, if they can't find cover.
#10
10/16/2012 (6:20 pm)
www.garagegames.com/community/forums/viewthread/130955/1#comment-831144
Note: Guy Allard's update warp fix means that you no longer have to call via serverCommand nor use the setTransform() workaround.
#11
02/28/2013 (9:38 am)
function AiPlayer::doProne(%this)
{
   echo(%this.getname() @ " AiProne");
	
   if(%this.getPose() !$= "Prone")
   {
      %this.setPose(2);
      //%this.schedule(50, "kickStartPose", 2);
	    %this.setTransform(%this.getTransform());  
   }
}
without schedule it is working fine.

" it also helps if there is a slight delay - hence the schedule."

steve,
just for curiosity,in which cases there would be delay?
#12
02/28/2013 (6:35 pm)
with script side fix it was working fine in t3d 1.2

but it failed to work with t3d 2.0
after adding "Guy Allard's update warp fix" it is now working.

so to make it work with t3d 2.0 we have to use that fix instead of script side fix.