Improving the AIPlayer turning movement
by Luis Anton · 02/28/2004 (9:11 pm) · 8 comments
If an AIPlayer is moving towards "10 10 0" and it must suddenly change its destination point to "10 -10 0", it will just 'flip' its orientation suddenly, without any transition. The following changes in AIPlayer.h and AIPlayer.cc allow a smoother movement.
In order to solve the turning problem, the AIPlayer must keep an orientation vector and change it accordingly to its MoveDestination. These three new attributes should be added in aiplayer.h:
Then, in aiplayer.cc, in AIPlayer::getAIMove, change these lines:
to
The idea is to change the mOrientation vector just a little bit on each tick, using the interpolate function. The turning speed can be changed with the third parameter. 0 means no interpolation at all, while 1 converts the first vector to the second. For a human 0.2 seems ok, but for a car you may need 0.02 or something like that. Try different values. A nice and easy touch would be adding a turningSpeed to the AIPlayer, so you may change it in the scripts.
Finally, almost at the end of the function, add the location line:
so the last location where the object was is properly stored.
And that's all, I hope. I have not tested it too much, but it seems to work pretty well...
In order to solve the turning problem, the AIPlayer must keep an orientation vector and change it accordingly to its MoveDestination. These three new attributes should be added in aiplayer.h:
... bool mMoveSlowdown; // Slowdown as we near the destination [b] Point3F mLastLocation; // added For stuck check Point3F mOrientation; // added Bot's orientation in previous tick S32 mTicksStuck; // added Number of ticks without moving [/b] SimObjectPtr<GameBase> mAimObject; // Object to point at...
Then, in aiplayer.cc, in AIPlayer::getAIMove, change these lines:
bool AIPlayer::getAIMove(Move *movePtr)
{
...
F32 xDiff = mAimLocation.x - location.x;
F32 yDiff = mAimLocation.y - location.y;
...to
Point3F vDiff = mAimLocation - location;
vDiff.normalize();
mOrientation.normalize();
mOrientation.interpolate(mOrientation,vDiff,0.2);
F32 xDiff = mOrientation.x;
F32 yDiff = mOrientation.y;The idea is to change the mOrientation vector just a little bit on each tick, using the interpolate function. The turning speed can be changed with the third parameter. 0 means no interpolation at all, while 1 converts the first vector to the second. For a human 0.2 seems ok, but for a car you may need 0.02 or something like that. Try different values. A nice and easy touch would be adding a turningSpeed to the AIPlayer, so you may change it in the scripts.
Finally, almost at the end of the function, add the location line:
if (location == mLastLocation) {
throwCallback("onMoveStuck");
mMoveState = ModeStop;
}
[b]
mLastLocation = location; //store the location
[/b]so the last location where the object was is properly stored.
And that's all, I hope. I have not tested it too much, but it seems to work pretty well...
#2
Thanks a lot for sharing.
Edit - might be my head that has some buggers, but to get it compilable I had to add aiPlayer.h to my vc6 project. Also m_LastLocation was already declared in aiPlayer.h
02/29/2004 (1:45 am)
Verrrry cool. Saved me an hour or so today were I was going to code something similar. I have some sharks that swim in the water, and they need to do a smooth turn. Looks damn stupid when they suddenly turn 90 degrees.Thanks a lot for sharing.
Edit - might be my head that has some buggers, but to get it compilable I had to add aiPlayer.h to my vc6 project. Also m_LastLocation was already declared in aiPlayer.h
#3
To get TurningSpeed script access follow below :)
in aiPlayer.h
under
Point3F mOrientation;
add
F32 mTurningSpeed;
after
void setMoveSpeed( const F32 speed );
add
void setTurningSpeed( F32 speed );
in aiPlayer.cc
in AIPlayer Constructor
AIPlayer::AIPlayer()
add
mTurningSpeed = 0.2;
Add after function
AIPlayer::setMoveSpeed
add
find and change
mOrientation.interpolate(mOrientation,vDiff,0.2);
to
mOrientation.interpolate(mOrientation,vDiff,mTurningSpeed);
after
ConsoleMethod( AIPlayer, setMoveSpeed, ...
add
Thats all you need for being able to control the turning speed via script, however.. after looking over the AIPlayer I see that mMoveSpeed and now mTurningSpeed are not sent over the network which _could_ make the AIPlayer jumpy on the client.
in aiPlayer.h
after enum
enum MoveState { ... };
add
enum AIPlayerMasks {
AIPlayerUpdate = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
};
after
~AIPlayer();
add
U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *con, BitStream *stream);
in aiPlayer.cc
include this FIRST
#include "core/bitStream.h"
after function
AIPlayer::~AIPlayer()
add
replace these functions
AIPlayer::setMoveSpeed( F32 speed )
AIPlayer::setTurningSpeed( F32 speed )
with
Now, open player.h
find
enum MaskBits {
make this look like below
We do this so AIPlayer can access NextFreeMask.
Save & compile!
Sorry... got a little carried away with this :)
02/29/2004 (5:27 am)
@Thomas, you only needed to add "Point3F mOrientation;" to aiPlayer class.To get TurningSpeed script access follow below :)
in aiPlayer.h
under
Point3F mOrientation;
add
F32 mTurningSpeed;
after
void setMoveSpeed( const F32 speed );
add
void setTurningSpeed( F32 speed );
in aiPlayer.cc
in AIPlayer Constructor
AIPlayer::AIPlayer()
add
mTurningSpeed = 0.2;
Add after function
AIPlayer::setMoveSpeed
add
void AIPlayer::setTurningSpeed( F32 speed )
{
mTurningSpeed = getMax(0.0f, getMin( 1.0f, speed ));
}find and change
mOrientation.interpolate(mOrientation,vDiff,0.2);
to
mOrientation.interpolate(mOrientation,vDiff,mTurningSpeed);
after
ConsoleMethod( AIPlayer, setMoveSpeed, ...
add
ConsoleMethod( AIPlayer, setTurningSpeed, void, 3, 3, "( float speed )"
"Sets the turning speed for an AI object.")
{
object->setTurningSpeed( dAtof( argv[2] ) );
}Thats all you need for being able to control the turning speed via script, however.. after looking over the AIPlayer I see that mMoveSpeed and now mTurningSpeed are not sent over the network which _could_ make the AIPlayer jumpy on the client.
in aiPlayer.h
after enum
enum MoveState { ... };
add
enum AIPlayerMasks {
AIPlayerUpdate = Parent::NextFreeMask << 0,
NextFreeMask = Parent::NextFreeMask << 1
};
after
~AIPlayer();
add
U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
void unpackUpdate(NetConnection *con, BitStream *stream);
in aiPlayer.cc
include this FIRST
#include "core/bitStream.h"
after function
AIPlayer::~AIPlayer()
add
U32 AIPlayer::packUpdate(NetConnection *con, U32 mask, BitStream *stream) {
U32 retMask = Parent::packUpdate(con, mask, stream);
if(stream->writeFlag(mask & AIPlayerUpdate)) {
Con::printf("AI packUpdate");
stream->writeSignedFloat(mMoveSpeed, 6);
stream->writeSignedFloat(mTurningSpeed, 6);
}
return retMask;
}
void AIPlayer::unpackUpdate(NetConnection *con, BitStream *stream) {
Parent::unpackUpdate(con, stream);
if(stream->readFlag()) {
Con::printf("AI unpackUpdate");
mMoveSpeed = stream->readSignedFloat(6);
mTurningSpeed = stream->readSignedFloat(6);
}
}Note: remove the Con::printf after you know its all working :)replace these functions
AIPlayer::setMoveSpeed( F32 speed )
AIPlayer::setTurningSpeed( F32 speed )
with
void AIPlayer::setMoveSpeed( F32 speed ) {
if(speed != mMoveSpeed) {
mMoveSpeed = getMax(0.0f, getMin( 1.0f, speed ));
if(isServerObject())
setMaskBits(AIPlayerUpdate);
}
}
void AIPlayer::setTurningSpeed( F32 speed ) {
if(speed != mTurningSpeed) {
mTurningSpeed = getMax(0.0f, getMin( 1.0f, speed ));
if(isServerObject())
setMaskBits(AIPlayerUpdate);
}
}Now, open player.h
find
enum MaskBits {
make this look like below
public:
/// Bit masks for different types of events
enum MaskBits {
ActionMask = Parent::NextFreeMask << 0,
MoveMask = Parent::NextFreeMask << 1,
ImpactMask = Parent::NextFreeMask << 2,
NextFreeMask = Parent::NextFreeMask << 3
};
private:We do this so AIPlayer can access NextFreeMask.
Save & compile!
Sorry... got a little carried away with this :)
#4
Nice one Chris.
Been trying to keep the add a new flag to the aiPlayer/Player, so that they follow paths in 3d space rather than always following the surface (good for my sharks in the water), but that seems to be harder than such........ If I get this to work, I'll post the changed needed to aiPlayer for the turning in 3d space. Its a bit more tricky than the head code
02/29/2004 (5:42 am)
*grins*Nice one Chris.
Been trying to keep the add a new flag to the aiPlayer/Player, so that they follow paths in 3d space rather than always following the surface (good for my sharks in the water), but that seems to be harder than such........ If I get this to work, I'll post the changed needed to aiPlayer for the turning in 3d space. Its a bit more tricky than the head code
#5
Nice to hear it has been useful!
02/29/2004 (10:02 am)
Thanks, Chris, now it's much better ;) That's what I meant with the turning speed.Nice to hear it has been useful!
#6
03/01/2004 (7:45 am)
Nice detail - I echo Stefan, it's universal enough to be useful in HEAD.
#7
The method used by Luis (thanks for great work, Luis) is very easy to implement, but I need a final tweak that I can't seem to get right.
The remaining problem is that the AI rotates smoothly around corners, but actually sidesteps instead of going forward in the direction he is facing. How can I correct this movement? I would like to have him moving forward only, onlly correcting his direction through this interpolated rotation.
We have a camera following an AI bot (using the cam node in 3rd person), which is running along a spline. Following a spline can be very jerky and this information has helped a great deal in getting me the direction I needed to go to solve this.
We've tried to use the advanced camera resource and interpolated the position as the AI moved, but a new problem was added later in that now we want a first person camera in place as well.
Correcting the smooth movement of the AI bot seems it should be the simplest method of attaining our goals.
Thanks in advance for any assistance,
- Jeremy
11/14/2005 (4:28 pm)
This was very close to what I need to do, and I'm hoping someone has already found a correction for this.The method used by Luis (thanks for great work, Luis) is very easy to implement, but I need a final tweak that I can't seem to get right.
The remaining problem is that the AI rotates smoothly around corners, but actually sidesteps instead of going forward in the direction he is facing. How can I correct this movement? I would like to have him moving forward only, onlly correcting his direction through this interpolated rotation.
We have a camera following an AI bot (using the cam node in 3rd person), which is running along a spline. Following a spline can be very jerky and this information has helped a great deal in getting me the direction I needed to go to solve this.
We've tried to use the advanced camera resource and interpolated the position as the AI moved, but a new problem was added later in that now we want a first person camera in place as well.
Correcting the smooth movement of the AI bot seems it should be the simplest method of attaining our goals.
Thanks in advance for any assistance,
- Jeremy
#8
F32 xDiff = mAimLocation.x - location.x;
F32 yDiff = mAimLocation.y - location.y;
with this:
Point3F vDiff = mAimLocation - location;
vDiff.normalize();
mOrientation.normalize();
mOrientation.interpolate(mOrientation,vDiff,0.2);
F32 xDiff = mOrientation.x;
F32 yDiff = mOrientation.y;
My enemies disapear when they are givet setMoveDestination().. I have gone trough this code multiple times and it seems like i have done everything exactly as it says. Using TGE 1.4.
04/28/2006 (8:38 am)
When i replace this:F32 xDiff = mAimLocation.x - location.x;
F32 yDiff = mAimLocation.y - location.y;
with this:
Point3F vDiff = mAimLocation - location;
vDiff.normalize();
mOrientation.normalize();
mOrientation.interpolate(mOrientation,vDiff,0.2);
F32 xDiff = mOrientation.x;
F32 yDiff = mOrientation.y;
My enemies disapear when they are givet setMoveDestination().. I have gone trough this code multiple times and it seems like i have done everything exactly as it says. Using TGE 1.4.

Associate Stefan Beffy Moises
Thx for sharing!