- Home /
MovePosition not as precise as Translate with Kinematic rigidbodies (example unitypackage included)
So I've been doing some testing and I've come to the conclusion that MovePosition have a lag when moving a rigidbody. The included unitypackage comes with a simple scene with 2 instances of a prefab.
(Download UnityPackage (10kb))
That prefab is a rigidbody with:
Interpolate: Set to "Interpolate"
Use gravity: Unchecked
Is Kinematic: Checked
In Update, the arrow keys are scanned to check if the game objects must be moved around.
One of the instances (Player 1) moves it's position using transform.Translate in Update. The other (Player 2) uses rigidbody.MovePosition in FixedUpdate, where all physics interactions should be.
The code (very simple) is as follows:
[Range(1, 2)]
public int PlayerId = 1;
public float speed = 5;
private Rigidbody rigidBody;
private Vector2 input;
void Awake()
{
rigidBody = GetComponent<Rigidbody>();
}
void Update () {
input = new Vector2();
if (Input.GetKey(KeyCode.LeftArrow)) { input.x = -1; }
if (Input.GetKey(KeyCode.RightArrow)) { input.x = 1; }
if (Input.GetKey(KeyCode.UpArrow)) { input.y = 1; }
if (Input.GetKey(KeyCode.DownArrow)) { input.y = -1; }
if (PlayerId == 1)
{
Vector3 movement = Vector3.right * input.x * speed * Time.deltaTime;
movement += Vector3.forward * input.y * speed * Time.deltaTime;
transform.Translate(movement);
}
}
void FixedUpdate()
{
if (PlayerId == 2) {
Vector3 movement = Vector3.right * input.x * speed * Time.deltaTime;
movement += Vector3.forward * input.y * speed * Time.deltaTime;
rigidBody.MovePosition(rigidBody.position + movement);
}
}
In theory both game objects should move at the same time, maintaining their relative position between each other. But if you run this sample, you will notice that Player 2 have a noticeable lag between the moment the arrow is pressed and its actual movement on the screen.
While Player 1 (using translate) moves instantly as it should, Player 2 seems to do it at a different time. If you follow Player 2 in the Scene View (using Shift + F) and keep going in one direction, you will notice that eventually it will surpass Player 1.
Is there a solution to this problem? Is there something I'm missing or not understanding? This lag completely ruins precision gameplay, but I want to use the physics system.
Thanks in advance.
Answer by Bunny83 · Oct 15, 2015 at 11:44 AM
I'm not sure if you are familiar with how FixedUpdate works. FixedUpdate is always executed before Update. However it's "fixed" (not at a fix rate but with a "fixed" time / deltaTime).
Time.deltaTime usually returns the time the last frame took overall. However inside FixedUpdate Time.deltaTime returns "fixedDeltaTime" instead. That's because FixedUpdate doesn't run necessarily when Update runs or it might even run multiple times within one frame to keep it's execution rate "pseudo constant".
In pseudo code Unity's main loop looks something like this:
while(true)
{
ReadInput();
while(Time.fixedTime < Time.time)
{
RunFixedUpdate();
UpdateInternalPhysics();
Time.fixedTime += Time.fixedDeltaTime;
}
RunUpdate();
Render();
Time.time += Time.deltaTime;
}
"Time.fixedDeltaTime" is a constant value it's what you have set as your fixed timestep. (Usually 0.02 == 50fps)
As you can see if fixedDeltaTime is larger than the actual deltaTime FixedUpdate might skip an execution since the amount Time.time advanced the last frame doesn't exceed the current "fixedTime". Both Time.time and Time.fixedTime will advance at the same speed. They never drift apart. However they will almost never be equal since they are advanced at different intervals. FixedUpdate (with a timestep of 0.02) will always be called 50 times in a sec while the Update rate depends on the performance and maybe vsync limitations.
If your framerate drops to 5 fps, FixedUpdate will be called roughly 10 times per frame. If you have a framerate of about 100fps FixedUpdate will be called about every second frame. So every other frame no FixedUpdate call is made.
You get the smoothest movement if you sync the movement with the visual update. FixedUpdate just ensures a constant rate which is important if your movement should be the same at all framerates.
As we all know using Time.deltaTime ensures that a linear change which we apply each frame is time based and frame independent. However that only works for linear changes. Since acceleration (linear change of velocity) results in a quadratic change in position there's an error which is different depending on the framerate. Using a constant rate for physics calculations ensures that you always get the same result after a certain amount of time.
Using FixedUpdate for movement will always introduce a bit of stutter since sometimes you might get two visual updates but only one actual movement so it looks like it's stuck for a frame.
As far as i know it should be no problem to use MovePosition inside Update especially since you have just a linear motion (no acceleration). The advice to use FixedUpdate applies to when using forces. Since forces are applied inside the Internal physics step you should apply the force right before Unity's physics system internally updates the velocity / position.
edit
I once made a Webplayer build to visualize how FixedUpdate works. Of course you need a browser that still supports the Unity webplayer plugin ^^. You can adjust the virtual "FPS rate" and the "FixedRate" at the bottom. The simulaition speed is 100 times slower than realtime(can also be adjusted). Each red section represents "one frame" where each blue rect represents an Update call(once per frame) while the magenta rects represents an FixedUpdate call. You can also see how Time.time and Time.fixedTime will change over time.
Following your answer I've modified the code, putting $$anonymous$$ovePosition into Update (because as you said it is a linear motion) but still I'm having lag when moving the GO with $$anonymous$$ovePosition that doesn't happen with Translate:
[Range(1, 2)]
public int PlayerId = 1;
public float speed = 5;
private Rigidbody rigidBody;
private Vector2 input;
private Vector3 movement;
void Awake()
{
rigidBody = GetComponent<Rigidbody>();
}
void Update()
{
input = new Vector2();
if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.LeftArrow)) { input.x = -1; }
if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.RightArrow)) { input.x = 1; }
if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.UpArrow)) { input.y = 1; }
if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.DownArrow)) { input.y = -1; }
movement = Vector3.right * input.x * speed * Time.deltaTime;
movement += Vector3.forward * input.y * speed * Time.deltaTime;
if (PlayerId == 1)
transform.Translate(movement);
if (PlayerId == 2)
rigidBody.$$anonymous$$ovePosition(rigidBody.position + movement);
}
Is it as if $$anonymous$$ovePosition lags somehow, even reducing the fixed timeStep to 0.166667
The nature of Interpolation is guaranteed to add error. This is because it is working things out based on information it has got. A translate is a one time operation but a $$anonymous$$oveposition is actually rendered over multiple frames if the interpolation is set in the rigidbody. It states this in the docs :
If Rigidbody interpolation is enabled on the Rigidbody, calling Rigidbody.$$anonymous$$ovePosition results in a smooth transition between the two positions in any intermediate frames rendered. This should be used if you want to continuously move a rigidbody in each FixedUpdate. Set Rigidbody.position ins$$anonymous$$d, if you want to teleport a rigidbody from one position to another, with no intermediate positions being rendered.
"Use .position if you want no intermediate frames rendered" (To paraphrase)
A Translate has no such intermediacy.
Its not really an issue but a $$anonymous$$ovePosition is more intensive than a Translate as it performs a sweeptest between the two points to deter$$anonymous$$e is any collisions could have occured, I do believe.
This case re$$anonymous$$ds me of the common problem of 'Camera Jitter' where either the camera or object appear to jitter/shake when moving. The fix that worked for me was to calculate the acculative fixedDeltaTime that occured between Updates and plug that figure in the the calculations. (Where I had success with this, the other guy didnt).
http://answers.unity3d.com/questions/1012337/player-or-background-stutter.html
The reason I add this in here, if you freeze your "Update" object and just observe the "FixedUpdate" object it appears to stutter periodically, moving in bursts.
Sorry @meat5000, I meant, Position. Even though I still notice a small amount of lag compared to just doing transform is closer to what I want. I'll have to learn to live with that rounding problem :P
Same thing dude. $$anonymous$$oving by position is teleporting and wont properly register collisions. Dont get me wrong, they can still occur but objects are very likely to pass through colliders.
After testing it works acceptably with my scenario. But you are right, I see how it won't work in other situations. I'm a little perplexed that this see$$anonymous$$gly common situation is so complicated and Unity kind of force you to apply forces on the rigidbody...