AiPlayer run animation fix
by Manoel Neto · 09/05/2006 (11:23 pm) · 31 comments
In TGE/TSE, AiPlayers seem unable to keep the forward animation playing properly (it keeps resetting, causing a jerky movement, or making the bots "skate" completly) when one of the following conditions are met:
- The bot is moving very slowly;
- The framerate is very low;
- The network is overloaded (lag, packet loss, etc.)
After some debugging, it turned out that AI-controlled players were setting their velocity to (0,0,0) in client-side during some proccessTick() calls, causing the root animation to be played instead of the run one. Since this only happens sometimes, it causes the run animation to reset, and the rate at which zero'ed velocities are generated increases a lot when the bot's speed decreases (as example, when you use the slowDown flag in setMoveDestination), and there are framerate/network performance issues.
The problem happens due to Player::updateMove() being called on AiPlayer ghosts. AiPlayers generate a move in processTick(), but only on the server. On client-side, they are supposed to move based only on velocity/rotation/delta information sent from the server. Since their ghosts never generate moves, updateMove always calculate zero velocities on the client side.
When the framerate is high enough, and when the network isn't lagging, a packet will always arrive with the correct movement information before the AiPlayer can decide which animation to play. But if a packet gets too late, the animation will be decided based on zero velocity (I'm not entirely sure why it simply fails when the AiPlayer is moving slowly enough).
To view the problem, load the starter.fps demo, enter the editor and view kork's ID, then type this on the console:
Now go watch Kork walking around. He can barely play the run animation at that speed, and will mostly "skate" along the path.
To fix the issue, add these mere two lines on top of the Player::updateMove() method in player.cc:
This prevents updateMove from being called on AiPlayer ghosts once for all. Load the starter.fps again and make Kork walk slowly again. He should play the animation without problems now, not matter how slow it's moving.
--EDIT--
With the advent of TGEA 1.7, the fix might cause AiPlayers to not animate when moving randomly. This is caused by mJetting. To fix this, add this to Player::Player():
- The bot is moving very slowly;
- The framerate is very low;
- The network is overloaded (lag, packet loss, etc.)
After some debugging, it turned out that AI-controlled players were setting their velocity to (0,0,0) in client-side during some proccessTick() calls, causing the root animation to be played instead of the run one. Since this only happens sometimes, it causes the run animation to reset, and the rate at which zero'ed velocities are generated increases a lot when the bot's speed decreases (as example, when you use the slowDown flag in setMoveDestination), and there are framerate/network performance issues.
The problem happens due to Player::updateMove() being called on AiPlayer ghosts. AiPlayers generate a move in processTick(), but only on the server. On client-side, they are supposed to move based only on velocity/rotation/delta information sent from the server. Since their ghosts never generate moves, updateMove always calculate zero velocities on the client side.
When the framerate is high enough, and when the network isn't lagging, a packet will always arrive with the correct movement information before the AiPlayer can decide which animation to play. But if a packet gets too late, the animation will be decided based on zero velocity (I'm not entirely sure why it simply fails when the AiPlayer is moving slowly enough).
To view the problem, load the starter.fps demo, enter the editor and view kork's ID, then type this on the console:
(kork id).setMoveSpeed(0.1);
Now go watch Kork walking around. He can barely play the run animation at that speed, and will mostly "skate" along the path.
To fix the issue, add these mere two lines on top of the Player::updateMove() method in player.cc:
if (!getControllingClient() && isGhost())
return;This prevents updateMove from being called on AiPlayer ghosts once for all. Load the starter.fps again and make Kork walk slowly again. He should play the animation without problems now, not matter how slow it's moving.
--EDIT--
With the advent of TGEA 1.7, the fix might cause AiPlayers to not animate when moving randomly. This is caused by mJetting. To fix this, add this to Player::Player():
mJetting = false;The darn thing relied on UpdateMove to get a value, so it used memory garbage instead for fixed AiPlayers. If you want your AiPlayers to jet, you'll need to add mJetting to packUpdate() and unpackUpdate(), as well as set the flag to true in AiPlayer::getAiMove().
About the author
#2
09/05/2006 (1:27 pm)
More like this:void Player::updateMove(const Move* move)
{
if (!getControllingClient() && isGhost())
return;
delta.move = *move;
...
...
...
#3
09/05/2006 (5:17 pm)
Thanks alot. I test it out.
#4
09/05/2006 (11:59 pm)
fix works like a charm!
#5
09/06/2006 (2:12 am)
Wow, I had been looking for a fix and remember that you (Manoel) mentioned something about it too. Is it really that easy, without side-effects? Why was that line there?
#6
09/06/2006 (2:41 am)
What line you talking about Stefan?
#7
09/06/2006 (3:36 am)
Yeah, I wonder too. I misunderstood :) That line was added, not removed. Doh.
#8
09/06/2006 (6:56 am)
So far the fix isn't displaying any side effects, at least in single-player. Further multi-player testing is required, though.
#9
Thanks Manoel!
09/06/2006 (9:04 am)
This has been chapping my hide for almost a year. I was sure it was something i'd done to break the engine!Thanks Manoel!
#10
i ran into a problem with this where if playerA went into a looping animation (eg sitting on the ground),
but then stood up and started walking, playerB would see playerA sliding along the ground, not walking.
- admittedly we've got a fairly heavily (and possibly poorly ;) hacked TGE at this point,
but thought i'd put it out there in case anyone else runs into the same thing.
looking for a fix now..
09/08/2006 (1:47 pm)
uh oh!i ran into a problem with this where if playerA went into a looping animation (eg sitting on the ground),
but then stood up and started walking, playerB would see playerA sliding along the ground, not walking.
- admittedly we've got a fairly heavily (and possibly poorly ;) hacked TGE at this point,
but thought i'd put it out there in case anyone else runs into the same thing.
looking for a fix now..
#11
and moving Manoel's additions into AIPlayer::updateMove().
again, this may be specifically only a problem in my modded version of the engine.
so from the top:
make Player.h like this:
make Player.cc like this:
(that is, like stock TGE)
make AIPlayer.h like this:
make AIPlayer.cc like this:
09/08/2006 (2:43 pm)
okay, i fixed my situation by making Player::updateMove() virtual,and moving Manoel's additions into AIPlayer::updateMove().
again, this may be specifically only a problem in my modded version of the engine.
so from the top:
make Player.h like this:
[b]virtual[/b] void updateMove(const Move *move);
make Player.cc like this:
(that is, like stock TGE)
void Player::updateMove(const Move* move)
{
delta.move = *move;
// Trigger imagesmake AIPlayer.h like this:
virtual bool getAIMove( Move *move );
[b]virtual void updateMove(const Move *move);[/b]make AIPlayer.cc like this:
[b]
void AIPlayer::updateMove(const Move* move)
{
if (!getControllingClient() && isGhost())
return;
Parent::updateMove(move);
}[/b]
#12
09/11/2006 (4:02 pm)
Manoel, you're a star! :-D Thanks for this ...
#14
09/22/2006 (7:29 pm)
Orion, thanks for the update I'll try this out too. Cheers guys.
#15
I couldn't compile with your changes in the Orion? Not sure about that I seem to get linker errors.
09/22/2006 (7:43 pm)
Hmm,I couldn't compile with your changes in the Orion? Not sure about that I seem to get linker errors.
#16
sorry my changes broke the compile for you.
they're pretty simple tho so shouldn't be too hard to fix.
hmm, looking again at the code i don't see why it would break.
what errors are you getting ?
maybe my notation in Player::updateMove() was confusing ?
basically you want the function to look like it did before Manoel's change.
ie, i only wrote the top of the function there, not the whole thing.
09/23/2006 (4:24 am)
hi malcolm,sorry my changes broke the compile for you.
they're pretty simple tho so shouldn't be too hard to fix.
hmm, looking again at the code i don't see why it would break.
what errors are you getting ?
maybe my notation in Player::updateMove() was confusing ?
basically you want the function to look like it did before Manoel's change.
ie, i only wrote the top of the function there, not the whole thing.
#17
Right now what I am doing is playing this animation before the aiPlayer chases the player.
%obj.playThread(0,"celwave");
%obj.schedule( 1000, stopThread, 0 ); // Do this for non looping animations
Unfortunately the moment this animation is played, the monster keeps sliding around no matter how fast it works. If I remove this, everything seems to work fine (obviously).
Do we need to play a looping animation?
---Edited
I change playThread to setActionThread and it works fine now...is this bug?
10/10/2006 (1:39 am)
I applied this fix thinking it would resolve my current issue, but it doesn't seem to help.Right now what I am doing is playing this animation before the aiPlayer chases the player.
%obj.playThread(0,"celwave");
%obj.schedule( 1000, stopThread, 0 ); // Do this for non looping animations
Unfortunately the moment this animation is played, the monster keeps sliding around no matter how fast it works. If I remove this, everything seems to work fine (obviously).
Do we need to play a looping animation?
---Edited
I change playThread to setActionThread and it works fine now...is this bug?
#18
www.garagegames.com/docs/tge/general/apa.php
Or does no-one follow that advice much? ;)
10/12/2006 (2:36 am)
This might be a good fix to submit a patch for?www.garagegames.com/docs/tge/general/apa.php
Or does no-one follow that advice much? ;)
#19
12/20/2006 (9:09 am)
Thanx Manoel!
#20
I did notice a strange thing, though, which I'm curious about. I have a trigger set which does an applyImpulse to my bots when they hit it, so they go flying into the air. When they are flying, the "jerking" comes back, happens one time when they land, and then they are fine again.
Not a major problem for me, but I'm trying to grasp what's happening with the engine in general. Why would the fix stop working when they're being propelled into the air? It seems to me the fix being in player.cc would kinda take care of that?
01/12/2007 (3:15 pm)
Awesome, thanks Manoel! Works great!I did notice a strange thing, though, which I'm curious about. I have a trigger set which does an applyImpulse to my bots when they hit it, so they go flying into the air. When they are flying, the "jerking" comes back, happens one time when they land, and then they are fine again.
Not a major problem for me, but I'm trying to grasp what's happening with the engine in general. Why would the fix stop working when they're being propelled into the air? It seems to me the fix being in player.cc would kinda take care of that?

Torque Owner Fucifer
void Player::updateMove(const Move* move)
{
delta.move = *move;
if (!getControllingClient() && isGhost())
return;