Game Development Community

Player warp in unpackupdate() though pos/vel remains the same

by Drew Parker · in Torque Game Engine · 09/29/2006 (7:04 am) · 7 replies

I have noticed something possibly strange going on in player::unpackupdate(). The code appears to be running a warp even though the server and ghost are in sync. My player is not moving, with no positional or velocity change, yet the warp code is being run with delta.warpticks being set.

Here is the code block in Player::unpackupdate():

// Determin number of ticks to warp based on the average
         // of the client and server velocities.
         delta.warpOffset = pos - delta.pos;
         F32 as = (speed + mVelocity.len()) * 0.5 * TickSec;
         F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
         delta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5), 1.0f) : 0.0f);

         if (delta.warpTicks) {
            // Setup the warp to start on the next tick.
            if (delta.warpTicks > sMaxWarpTicks)
               delta.warpTicks = sMaxWarpTicks;
            delta.warpOffset /= delta.warpTicks;

            delta.rotOffset = rot - delta.rot;
            if(delta.rotOffset.z < - M_PI)
               delta.rotOffset.z += M_2PI;
            else if(delta.rotOffset.z > M_PI)
               delta.rotOffset.z -= M_2PI;
            delta.rotOffset /= delta.warpTicks;
         }
         else {
            // Going to skip the warp, server and client are real close.
            // Adjust the frame interpolation to move smoothly to the
            // new position within the current tick.

From what I can tell, this code checks the server and ghost position and velocity. If they differ too much, it causes a "warp" - a position and rotation difference are setup to play out over multiple ticks, to have the ghost quickly catch up to the server. This is run if the delta.warpticks are greater then 0.

If there are no delta.warpticks, then the warp is skip, and just the frame interpolation is updated (the else statement).


It seems since my player has not moved, the frame interpolation should be run instead of warping. However, the warp code keeps running even when the player is not moving. It appears to be because of these three lines:

F32 as = (speed + mVelocity.len()) * 0.5 * TickSec;
         F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;
         delta.warpTicks = (S32)((dt > sMinWarpTicks) ? getMax(mFloor(dt + 0.5), 1.0f) : 0.0f);


I did some debugging to investigate more. The average speed (as) is zero. When the second line uses as to set the dt, it checks if the as is above zero, and if so, does some calculations including the positional offset. If the as is zero, it assumes a warp by setting dt = sMaxWarpTicks. This then causes delta.warpTicks to be set, causing the warp code to run.


So, the big question:
If I change this to not warp when the average speed is zero, is it possible I will run into any other side effects?

I know this means I will miss an update when only the position changes and not the velocity. So I will check for a positional change as well.

Thanks to any who reply...

#1
09/29/2006 (7:55 am)
Your logic seems correct - it sure looks like if as is say zero then dt will me sMaxWarpTicks,
thus triggering the warp code.

possibly the original code meant to use sMinWarpTicks instead of sMaxWarpTicks ?
#2
09/29/2006 (8:09 am)
I was working with this today and noticed the same thing. So I thought I would go here to see if anyone else had mentioned it and what do I find!

There was a thread a while ago where they mentioned a potential fix, but I can not find it anymore. The search function has gotten worse :/
#3
09/29/2006 (8:14 am)
Hey guys,

Thanks for the replies. I'm trying this out for now:

// I'm changing this line so no warps happen when the player is still --dp
         //F32 dt = (as > 0.00001f) ? delta.warpOffset.len() / as: sMaxWarpTicks;

         F32 dt = 0.0;
         // If the average speed is greater then zero...
         if (as > 0.00001f)
            // Take into account our position offset as well, and use this to set the dt.
            dt = delta.warpOffset.len() / as;
         else
         {
            // Else, check if we have a positional change.  If so, warp.  If not, no warping!
            if (delta.warpOffset.len() > 0.0001f)
               dt = sMaxWarpTicks;            
         }

It seems to be working fine with no side effects, but that's with all of 1 hour of testing. :) I'll post something here if I find otherwise. Hopefully someone from the GG crew who has time and knows about this can confirm if there are any unknown side effects (like some other code depending on the constant warp)
#4
09/29/2006 (10:30 am)
That looks like a fine way to fix it. Using epsilon is generally a good idea, float equality comparisons tend to break.

Obviously without spending several hours testing it, I can't say for sure whether it's a good fix, but based on my understanding of the code that is a valid and more robust reimplementation.
#5
09/29/2006 (10:38 am)
Hey Ben,

Great, thanks for the feedback! I'll continue to test it and if I come across something post it back here.
#6
09/29/2006 (10:55 am)
Although I have not seen any problems, and I honestly do not understand the full scope of this fix - it can not hurt to test it. I will too post here if I find out anything broke.

Thanks guys
#7
01/18/2007 (11:51 am)
Quick update on this - after testing it for a while, it seems that the ghost object's rotation is not interpolated after this change is made. When a player turns without running, the turning of his ghost is really chunky and not smooth.

In other words, I guess the original code was assuming, "If the average speed between the client and server is zero - go ahead and warp even if the position hasn't changed, just in case they are turning, so we get interpolation for that."

I suppose a way to fix it would be to have a seperate flag or comparison for a rotation change, even if there is no velocity or positional change. This may require another maskBit or some additional stuff in pack/unpackUpdate, which means more bandwidth.

I'm trying to remember what the trade off is between the original line and the change I posted - I guess the original code warps more, meaning the clientside ghost prediction (updateMove, updatePos) is not running as often.