Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

MovePosition not as precise as Translate with Kinematic rigidbodies (example unitypackage included)

Discussion in 'Physics' started by SVC-Games, Oct 15, 2015.

  1. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    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. 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:
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class PlayerControl : MonoBehaviour {
    5.  
    6.     [Range(1, 2)]
    7.     public int PlayerId = 1;
    8.     public float speed = 5;
    9.  
    10.     private Rigidbody rigidBody;
    11.     private Vector2 input;
    12.      
    13.     void Awake()
    14.     {
    15.         rigidBody = GetComponent<Rigidbody>();
    16.     }
    17.  
    18.  
    19.     void Update () {
    20.         input = new Vector2();
    21.         if (Input.GetKey(KeyCode.LeftArrow)) { input.x = -1; }
    22.         if (Input.GetKey(KeyCode.RightArrow)) { input.x = 1; }
    23.         if (Input.GetKey(KeyCode.UpArrow)) { input.y = 1; }
    24.         if (Input.GetKey(KeyCode.DownArrow)) { input.y = -1; }
    25.  
    26.         if (PlayerId == 1)
    27.         {
    28.             Vector3 movement = Vector3.right * input.x * speed * Time.deltaTime;
    29.             movement += Vector3.forward * input.y * speed * Time.deltaTime;                  
    30.             transform.Translate(movement);
    31.         }
    32.     }
    33.  
    34.  
    35.     void FixedUpdate()
    36.     {      
    37.         if (PlayerId == 2) {
    38.             Vector3 movement = Vector3.right * input.x * speed * Time.deltaTime;
    39.             movement += Vector3.forward * input.y * speed * Time.deltaTime;
    40.             rigidBody.MovePosition(rigidBody.position + movement);          
    41.         }
    42.     }
    43.    
    44. }
    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.
     

    Attached Files:

  2. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    1,370
    Hi, didn't check your file,

    but maybe it's just the fact that you move player1 with transform.Translate, that moves the *object* outside of Physics updates, and it happens in Update, so FPS and Update might be in simple scene happening more often than fixed step update (set in Physics settings).

    When you move object with rigidBody.MovePosition, object will be moved on next tick of FixedUpdate. I'm quite noob with Unity Physics stuff, so those who know better correct if I'm wrong, but that's how I think it works.
     
  3. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    I dont believe you have to move kinematic rbs in fixed update.

    if you want smoother physics, and can get away with it, decrease fixed time step
     
  4. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    I've reduced it to 0.166667 (to achieve 60 Hz refresh rate) and moved the rigidbody manipulation to Update, since it's linear movement and doesn't involve acceleration. It's better now but I'm still having a little bit of lag compared to the movement of the GO using translate.
    Code (CSharp):
    1.   [Range(1, 2)]
    2.      public int PlayerId = 1;
    3.      public float speed = 5;
    4.      private Rigidbody rigidBody;
    5.      private Vector2 input;
    6.      private Vector3 movement;
    7.      
    8.      void Awake()
    9.      {      
    10.          rigidBody = GetComponent<Rigidbody>();
    11.      }
    12.      void Update()
    13.      {
    14.          input = new Vector2();
    15.          if (Input.GetKey(KeyCode.LeftArrow)) { input.x = -1; }
    16.          if (Input.GetKey(KeyCode.RightArrow)) { input.x = 1; }
    17.          if (Input.GetKey(KeyCode.UpArrow)) { input.y = 1; }
    18.          if (Input.GetKey(KeyCode.DownArrow)) { input.y = -1; }
    19.          movement = Vector3.right * input.x * speed * Time.deltaTime;
    20.          movement += Vector3.forward * input.y * speed * Time.deltaTime;
    21.          if (PlayerId == 1)          
    22.              transform.Translate(movement);
    23.          if (PlayerId == 2)
    24.             rigidBody.position = rigidBody.position + movement; //instead of rigidBody.MovePosition(rigidBody.position + movement);      
    25.            
    26.      }
    I'm quite surprised that nobody finds this disturbing. I know that rigidbody are usually moved using forces, etc, but moving one changing it's coordinates seems like an usual scenario...
     
  5. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    I would move it using Translate if its kinematic.
     
  6. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,574
    Kinematic rigidbodies should always be moved via MovePosition / MoveRotation so the physic interactions would be properly applied. Otherwise, using Translate you'll surely encounter issues when the moving object has to interact with other non-kinematic physic objects. Typical cases are collisions and carrying other objects (i.e. a character walking over a kinematic moving platform).

    The discrepancies you're observing might have to do with the accumulated error of the different delta times between Update and FixedUpdate. They are different tasks running at different rates. I wouldn't expect delta times at Update to be exact at all and every frame.
     
  7. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    http://docs.unity3d.com/Manual/class-Rigidbody.html

    Is Kinematic

    If enabled, the object will not be driven by the physics engine, and can only be manipulated by its Transform. This is useful for moving platforms or if you want to animate a Rigidbody that has a HingeJoint attached.


    That sounds more like Translate than MovePosition to me...
     
  8. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    I'm actually quite baffled by this whole issue. I understand how different timings affect the smoothness of the final movement, but I'm surprised that there isn't an integrated solution in such a simple scenario...
     
  9. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    975
    To my understanding, moveposition and rigidbody.position will run in fixedupdate even if you use it in update.
    If you are going to use physics, and your fixed timestep is less than your framerate, you will have a delay compared to just update. Interpolation will increase that delay by like 1 frame more.

    Also, again to my understanding, the difference between moveposition and translating is that moveposition will push other rigidbodies as if you were moving it with addforce, while setting the transform will just teleport and then slide any collisions away so they dont collide.
     
  10. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,574
    Nothing prevents you to do so, but then your object won't collide or push/move other non-kinematic objects.

    That's it.

    The correct solution is using MovePosition/MoveRotation in FixedUpdate for moving kinematic objects in a physically precise way. Having Interpolate enabled ensures visually smooth movement.

    Comparing the results to using Translate in Update is rather pointless and subject to errors and discrepancies (frame timing, numerical accumulations...). Moreover, if you verify the actual results of each case you'll encounter that the FixedUpdate/MovePosition solution actually brings the exact result.

    Example: distance traveled by the object when moving at 1 m/s during 60 seconds. The FixedUpdate/MovePosition method will yield 60 meters exactly. The Update/Translate method will yield varying results depending on frame rate during execution, frame timing inaccuracies, numerical accumulations, etc.

    So if you need precision gameplay, the FixedUpdate/MovePosition is definitely the way to go. You can reduce the fixed time step if you need to increase the precision.
     
  11. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    Of course, but if I chose to set it to kinematic, that's probably what I had in mind. And it will collide/push non-kinematic... not sure why you suggest it wont.

    I just wrote a runner with kinematic cubes for the level and a non kin for the player.
     
  12. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    But as you can see in the sample scene I attached, using FixedUpdate + MovePosition results in stuttering while moving, even with interpolation and a fixed timestep of 0.166667 (for 60fps refresh)

    Is there anything obvious I'm missing?
    Code (CSharp):
    1.  
    2.     void Update () {
    3.         input = new Vector2();
    4.         if (Input.GetKey(KeyCode.LeftArrow)) { input.x = -1; }
    5.         if (Input.GetKey(KeyCode.RightArrow)) { input.x = 1; }
    6.         if (Input.GetKey(KeyCode.UpArrow)) { input.y = 1; }
    7.         if (Input.GetKey(KeyCode.DownArrow)) { input.y = -1; }        
    8.     }
    9.  
    10.     void FixedUpdate()
    11.     {    
    12.             Vector3 movement = Vector3.right * input.x * speed * Time.deltaTime;
    13.             movement += Vector3.forward * input.y * speed * Time.deltaTime;
    14.             rigidBody.MovePosition(rigidBody.position + movement);        
    15.     }
     
  13. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    Are you moving the camera? Ive used similar code that doesnt stutter, although I might have turned my timestep down to 0.01...

    I often find that stuttering is coming from the camera.
     
  14. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    975
    Weird, I just did a quick test and it seems moveposition, rigidbody.position, translate, and transform.position will act the same in fixedupdate. It seems they are all moving the colliding rigidbody, but not pushing it.
    I could have sworn in 5.0 or so moveposition was pushing rigidbodies. I am now in 5.2.1f1
     
  15. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    Yep, they are all doing the same thing.

    The obvious failings will become apparent when you put colliders in front of the moving object.

    Here is what I would expect in a few different scenarios..

    Kinematic - Moving via Translate/MovePosition - forces don't work.

    pushing into kinematic. Colliders pass through each other without resistance
    pushing into non-kinematic. Non-kinematic gets pushed away

    Non-Kinematic - Moving via Translate

    pushing into kinematic. Collider will move clip other collider with some resistance (bad - the non kinematic should not pass/move through the blocking collider)

    pushing into non-kinematic. They will push each other. Some clipping will occur. Again bad form. They should not clip.

    Non-Kinematic - Moving via MovePosition/Forces/Velocity

    pushing into kinematic - Colliders will not clip, kinematic will prevent non-kinematic pushing through

    pushing into non-kinematic - Colliders will push each other away without clipping. RB with largest mass will have biggest effect on which collider gets pushed.
     
  16. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    I've just updated to 5.2.1f1 and now it seems that moving the kinematic rigidbody using MovePosition is smoother than using translate using the same script as post #1... wait, WHAT?

    There's still some lag between the button press and the actual movement of the rigidbody that doesn't occur using translate. My head is spinning...
     
  17. JamesLeeNZ

    JamesLeeNZ

    Joined:
    Nov 15, 2011
    Posts:
    5,618
    odd. Could be related to a graphics setting... maybe vsync or something.
     
  18. UCh

    UCh

    Joined:
    Nov 18, 2012
    Posts:
    29
    I know this is old but here my 5 cents. You're using Time.deltaTime inside the fixed update and this is incorrect. Your should use Time.fixedDeltaTime. That is probably why you got so much difference.
     
  19. SVC-Games

    SVC-Games

    Joined:
    May 21, 2013
    Posts:
    112
    I've read somewhere that inside fixed update Time.deltaTime = Time.fixedDeltaTime, but I could be wrong. Thanks for your input :)
     
  20. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,574
    Within FixedUpdate Time.deltaTime has the save value as Time.fixedDeltaTime. That part is correct.
     
  21. fben

    fben

    Joined:
    Oct 24, 2015
    Posts:
    6
    I know it's an old post .... but important topic.
    Just like HiddenMonk says impossible to get a kinematic rigidbody to push other non-kinematic rigidbodies when using MovePosition (or setting the transform).
    Could someone confirm this changed (I'm using 5.4) ? Would happily get rid of my slightly dodgy collision position correction script (even if it took me ages to get it right ...) and let Unity do the work and fix position.
     
  22. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    975
    Im assuming you mean someone official from the unity team?
     
  23. fben

    fben

    Joined:
    Oct 24, 2015
    Posts:
    6
    That would be the cherry on the cake, but if you tell me that you took the time to run the same project on two different version and one would deal with the collision/position correction and the other version wouldn't I would trust you!
    I guess I could do it myself (there's lots of things I could/should do ...)
     
  24. HiddenMonk

    HiddenMonk

    Joined:
    Dec 19, 2014
    Posts:
    975
    Well I just did a test in unity version 5.2.2p3 and 5.4.0f3 and it seems that in both versions, MovePosition does addforce to the colliding objects (so if it hits a sphere, the sphere will go rolling away), but using Translate will result in just moving the colliding object out of the way (so if it hits a sphere, the sphere will just translate however much it needs to not be colliding anymore).

    That was how I originally thought everything worked, but it seems my test in the past in unity version 5.2.1f1 gave different results. It could have been that I was testing wrong (for example, maybe my floor just had a very high friction so it seemed like MovePosition wasnt adding force).

    Here was my testing code
    Code (CSharp):
    1.  
    2. using UnityEngine;
    3.  
    4. public class TestMovePosition : MonoBehaviour
    5. {
    6.     public float speed = 5f;
    7.  
    8.     void FixedUpdate()
    9.     {
    10.         GetComponent<Rigidbody>().MovePosition(transform.position + (transform.forward * (speed * Time.fixedDeltaTime)));
    11.         //transform.Translate((transform.forward * (speed * Time.fixedDeltaTime)));
    12.     }
    13. }
     
  25. fben

    fben

    Joined:
    Oct 24, 2015
    Posts:
    6
    Ok so I tried again I can get a non kinematic object pushing a kinematic one. I think I just didn't understand well what could be achieved when having a mix of kinematic non-kinematic objects...
    What I was expecting is more something like : "I move the kinematic object towards a kinematic one and the kinematic obj will stop moving when it gets to the non-kinematic object".
    Let's say in your game the character is kinematic but the world is non-kinematic so that you would't have to fix the position when the character hits a wall or fall.
    If you know a way for a kinematic object to have its position fixed when collisions happen I'll take it.

    Thanks anyway!
     
  26. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,408
    In certain cases we do have to use MovePosition for moving kinematic bodies and do it in FixedUpdate rather than Update.

    Imagine, we have two boxes on top of each other. The lower one is kinematic, the upper one is not. We move the lower box.

    Transform.position / Rigidbody.position vs Rigidbody.MovePosition


    Moving the lower box using Transform/Rigidbody.position just teleports it to a new position. The upper box doesn't get any forces applied, keeps its old position and eventually falls down when the lower box moves far enough.

    Using Rigidbody.MovePosition will apply friction force to the upper box and it also will move, always staying on top of the lower box.

    Update vs FixedUpdate


    Since friction forces get involved, we have to move the lower body in FixedUpdate. Using Update will make the upper box motion inaccurate and jerky.
     
  27. Dan2013

    Dan2013

    Joined:
    May 24, 2013
    Posts:
    193
    @SVC-Games
    This reminds me an old and very informative thread.
    It seems there is always input delay and other headaches if you try to use Rigidbody to precisely control your character.

    If you try to precisely control Player/NPC with Rigidbody.MovePosition (in Update or FixedUpdate), you always get some delay that is caused in the Physic Engine (black box). To verify that again, I build a testing scene to move a capsule player with Rigidbody.MovePosition, CharacterController.Move, or Transform.Translate. I draw a red line on player's up direction before calling the moving function; I draw a shorter green line on player's up direction after calling the moving function. Please see the testing result in the picture below.
    Result.png

    I also attached the testing scene here.
    My conclusion is that if you really want to precisely control your character with Rigidbody, you have to deal with this kind of delays.
     

    Attached Files:

    Last edited: Oct 13, 2016
    svobodajak likes this.