Game Development Community

dev|Pro Game Development Curriculum

Smooth, configurable rotation for AIPlayers

by Guy Allard · 08/17/2011 (3:01 am) · 12 comments

Here's the code I usually use to give AIPlayers smooth yaw:
As a bonus, you also get an AIPlayerData namespace for your AI scripting.

*** NOTE ***
For all engine builds up to and including T3D 1.1final, there is a bug in the player interpolation and networking code that affects the rotation of AIPlayers. The bug is discussed here, and you will need to apply Ivan's fixes for this resource to work correctly.


Now, lets hack away at the engine:

in AIPlayer.h
before:
class AIPlayer : public Player {

add:
class AIPlayerData : public PlayerData {
   typedef PlayerData Parent;

public:
   AIPlayerData(); // CTor

   // maximum yaw rate
   F32 maxTurnRate;
   
   // standard datablock stuff
   DECLARE_CONOBJECT(AIPlayerData);
   static void initPersistFields();
   virtual void packData(BitStream* stream);
   virtual void unpackData(BitStream* stream);
};

then in AIPlayer.cpp
before
IMPLEMENT_CO_NETOBJECT_V1(AIPlayer);

add
#include "core/stream/bitStream.h"

// AIPlayerData datablock functions
IMPLEMENT_CO_DATABLOCK_V1(AIPlayerData);

AIPlayerData::AIPlayerData()
{
   maxTurnRate = M_2PI_F; // default to 1 full circle per second
}

void AIPlayerData::initPersistFields()
{
   Parent::initPersistFields();
   addField("maxTurnRate",    TypeF32, Offset(maxTurnRate,     AIPlayerData));
}

void AIPlayerData::packData(BitStream* stream)
{
   Parent::packData(stream);
   stream->write(maxTurnRate);
} 

void AIPlayerData::unpackData(BitStream* stream)
{
   Parent::unpackData(stream);
   stream->read(&maxTurnRate);
}


now we need to make the AIPlayer class actually use the AIPlayerData datablock
in AIPlayer.h,
after
typedef Player Parent

add
AIPlayerData *mDataBlock;        // our new datablock type

still in AIPlayer.h,
after
~AIPlayer();

add
bool onNewDataBlock(GameBaseData* dptr, bool reload);  // new datablock assigned

then in AIPlayer.cpp,
in the AIPlayer constructor, after
mAimOffset = Point3F(0.0f, 0.0f, 0.0f);

add
mDataBlock = 0;

then at the end of AIPlayer.cpp, add the function to handle the new datablock assignment
bool AIPlayer::onNewDataBlock(GameBaseData* dptr, bool reload)
{
   mDataBlock = dynamic_cast<AIPlayerData*>(dptr);
   if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload))
      return false;
   
   scriptOnNewDataBlock();
   return true;
}

Then, the code that actually controls the rate of turn
in AIPlayer.cpp, in the getAIMove function, replace
movePtr->yaw = yawDiff;

with
// control the rotation of the AIPlayer
   
         // get the maximum amount we can yaw this tick
         F32 maxYaw = mDataBlock->maxTurnRate * TickSec;

         // limit the yaw if it exceeds the maxYaw
         if(mFabs(yawDiff) > maxYaw)
            movePtr->yaw = maxYaw * mSign(yawDiff);
         else
            movePtr->yaw = yawDiff;


Finally, in script, you need to change the datablocks that your AIPlayers use from PlayerData to AIPlayerData - e.g.
find
datablock PlayerData(DemoPlayer : DefaultPlayerData)
and change it to
datablock AIPlayerData(DemoPlayer : DefaultPlayerData)

and then add a line for maxTurnRate to the datablock.
Don't use very small values for the maxTurnRate, as the networking compresses rotation and positions, so you will get artifacts for too small values. Some useful values are:
maxTurnRate = 6.3; // approx 360 degrees per second
maxTurnRate = 3.15 // approx 180 degrees per second
maxTurnRate = 1.6 // approx 90 degrees per second

*Edit* - simplified to use mSign as Daniel suggests

#1
08/17/2011 (4:31 am)
nice work guy. thanks for Sharing.
#2
08/17/2011 (6:18 am)
Good work, and very interesing about the bug. Ty Guy.
#3
08/17/2011 (8:16 am)
Thanks a bunch for this, I was looking for something like that.
#4
08/17/2011 (1:30 pm)
Guy, did I mess up? Getting an error:

[EDIT] No I'm an idiot ... nothing to see here move along ... move along ...
derp
#5
08/17/2011 (2:34 pm)
Hmm, that's a compile error? maxTurnRate is a member of AIPlayerData, but I think all the datablock references in this resource point at an AIPlayerData.

EDIT: Also, Guy, check out Math::mSign. You've basically rewritten it here - could save a few lines ;P.
#6
08/18/2011 (1:58 am)
@Steve - yes, I think you've left out something, it shouldn't be doing anything with PlayerData any more.

@Daniel - nice, didn't realise that function existed. Updated to use it.
#7
08/18/2011 (7:45 am)
*cough cough*
As expected ... this was my faux pas.

However I have found an issue - mounted objects and emitters don't seem to update with the Ai's position anymore.
[EDIT] Nope, that seems to be me messing up as well ... it's been an erratic few days ... :P

Everything works great, thanks very much, Guy.
#8
08/18/2011 (1:36 pm)
Great Work, i have been looking for something like this big thanks!!!
#9
08/19/2011 (1:11 pm)
Cool resource!
#10
09/01/2011 (6:22 am)
nice work!

i'm not add 'AIPlayerData'.
i added 'maxTurnRate' variable to PlayerData.

add only this code block to aiplayer.cpp.
// control the rotation of the AIPlayer
   
// get the maximum amount we can yaw this tick
F32 maxYaw = mDataBlock->maxTurnRate * TickSec;

// limit the yaw if it exceeds the maxYaw
if (mFabs(yawDiff) > maxYaw)
   movePtr->yaw = maxYaw * mSign(yawDiff);
else
   movePtr->yaw = yawDiff;

everything works fine.. :)

if use UAIK or another AI Solution, maybe re-definition so trouble.
#11
09/07/2011 (9:05 pm)
Wow, this is great stuff, that had always bugged me, but I'd never set out to fix it on my own.
#12
10/01/2011 (7:35 pm)
Awesome! I can't believe how crisp the environment looks while turning the character now. Truly a handy resource. :)