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:
add:
then in AIPlayer.cpp
before
add
now we need to make the AIPlayer class actually use the AIPlayerData datablock
in AIPlayer.h,
after
add
still in AIPlayer.h,
after
add
then in AIPlayer.cpp,
in the AIPlayer constructor, after
add
then at the end of AIPlayer.cpp, add the function to handle the new datablock assignment
Then, the code that actually controls the rate of turn
in AIPlayer.cpp, in the getAIMove function, replace
with
Finally, in script, you need to change the datablocks that your AIPlayers use from PlayerData to AIPlayerData - e.g.
find
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
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
#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
[EDIT] No I'm an idiot ... nothing to see here move along ... move along ...
derp
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
EDIT: Also, Guy, check out Math::mSign. You've basically rewritten it here - could save a few lines ;P.
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
@Daniel - nice, didn't realise that function existed. Updated to use it.
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
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.
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
i'm not add 'AIPlayerData'.
i added 'maxTurnRate' variable to PlayerData.
add only this code block to aiplayer.cpp.
everything works fine.. :)
if use UAIK or another AI Solution, maybe re-definition so trouble.
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. :) 
Torque Owner Jules
Something2Play