Search Unity

Rigidbodies will slide off of moving kinematic rigidbodies, fix?

Discussion in 'Physics' started by Zergling103, Sep 11, 2019.

  1. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    282
    Suppose you were to have a crate (non-kinematic rigidbody) resting atop a platform (kinematic, movement is script-controlled), the crate will simply slide along the platform as though its surface had no velocity; as though it teleported small amounts underneath the crate to give the illusion of movement rather than actually moving and carrying rigidbodies with it.

    Is there a way to fix this? I know that I could perhaps use a rigidbody with extremely high mass, or use fixed joints, but these are not ideal. The intention was to allow my gameplay designer to use the IsKinematic flag and the conveniences that come with it while still having it interact naturally with rigidbodies.

    I'll experiment for a solution on my own and report my findings here, but this seems like the sort of thing that you'd expect to just work, and the solution is just hiding somewhere in plain sight.
     
  2. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    282
    So, I tried the following:

    Code (CSharp):
    1. [RequireComponent(typeof(Rigidbody))]
    2. public class KinematicVelocity : MonoBehaviour
    3. {
    4.     private Quaternion _prevRotation;
    5.     private Vector3 _prevPosition;
    6.  
    7.     private Rigidbody _rigidbody;
    8.                                                        
    9.     public void Start()
    10.     {
    11.         _rigidbody = GetComponent<Rigidbody>();  
    12.        
    13.         _prevPosition = _rigidbody.position;
    14.         _prevRotation = _rigidbody.rotation;
    15.     }
    16.                                                  
    17.     public void FixedUpdate()
    18.     {
    19.         var currentPosition = _rigidbody.position;
    20.         var currentRotation = _rigidbody.rotation;
    21.        
    22.         var invDeltaTime = 1.0f / Time.fixedDeltaTime;
    23.         _rigidbody.velocity = (currentPosition - _prevPosition) * invDeltaTime;    
    24.         _rigidbody.angularVelocity = QuaternionUtil.FromToRotation(_prevRotation, currentRotation).ToAngleAxisRadians() * invDeltaTime;
    25.  
    26.         _prevPosition = currentPosition;      
    27.         _prevRotation = currentRotation;        
    28.     }
    29. }
    This had no change on the way rigidbodies reacted. However, it does solve the problem where a custom character controller thought the platform had 0 velocity as it moved.
     
  3. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    282
    Code (CSharp):
    1. [RequireComponent(typeof(Rigidbody))]
    2. public class KinematicVelocity : MonoBehaviour
    3. {
    4.     private Quaternion _prevRotation;
    5.     private Vector3 _prevPosition;
    6.  
    7.     private Rigidbody _rigidbody;
    8.                                                      
    9.     public void Start()
    10.     {
    11.         _rigidbody = GetComponent<Rigidbody>();
    12.      
    13.         _prevPosition = _rigidbody.position;
    14.         _prevRotation = _rigidbody.rotation;
    15.     }
    16.                                                
    17.     public void FixedUpdate()
    18.     {
    19.         var currentPosition = _rigidbody.position;
    20.         var currentRotation = _rigidbody.rotation;
    21.  
    22.         _rigidbody.position = _prevPosition;
    23.         _rigidbody.rotation = _prevRotation;
    24.         _rigidbody.MovePosition(currentPosition);
    25.         _rigidbody.MoveRotation(currentRotation);                                                                                          
    26.  
    27.         _prevPosition = currentPosition;    
    28.         _prevRotation = currentRotation;      
    29.     }
    30. }
    This code seems to have completely solved the problem from all angles (both from the perspective of querying surface velocity, and by how rigidbodies behave).
    The process: See where I have been moved to by script, store into a temporary variable. Teleport to where I was the previous frame. Then use the MovePosition and MoveRotation command to move to where it was moved to by script.
     
    Last edited: Sep 12, 2019
    Edy likes this.
  4. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,531
    It should be enough by calling MovePosition and MoveRotation feeding the new pose only. All other code seems redundant to me.
     
    hippocoder likes this.
  5. Zergling103

    Zergling103

    Joined:
    Aug 16, 2011
    Posts:
    282
    True, but this only works (and I've confirmed this) for scripts that explicitly use the MovePosition and MoveRotation calls directly. Most scripts ignore this and simply move the transform (such as animation, IK, constraints, scripts that weren't designed with physics in mind) which will produce incorrect behaviour when contacting rigidbodies. This script automatically treats all manipulations of the transform as though they were made by calls to MovePostion/Rotation, almost like a sort of abstraction between the behaviour that does the moving and the kind of object being moved.
     
    Last edited: Sep 12, 2019
    Edy likes this.
  6. Edy

    Edy

    Joined:
    Jun 3, 2010
    Posts:
    1,531
    Ahh, ok. Of course, I had assumed that only MovePosition/MoveRotation would be used, as modifying the transform means "teleporting" the object (incorrect if you want physic interactions). Your solution seems a good workaround to make all those generic scripts compatible with physics! :)
     
    GameDevCouple_I and hippocoder like this.