Search Unity

Question Rigidbody and Child Rotations without Interpolation

Discussion in 'Physics' started by naajaw2, Nov 28, 2023.

  1. naajaw2


    Jan 11, 2023

    I'm building a kinematic character controller (i.e. kinematic RB with custom collision resolution). The top-level monobehavior is on a GameObject with a Rigidbody (Player), and the player body model that plays animations (PlayerBody) is a child of the Player. Might also be useful to mention that the CapsuleCollider is on an empty GameObject that is a sibling of the PlayerBody, so I can change its orientation relative to the Player Position based on crouching/swimming, etc.

    For root motion animations, I have a setup where I let the animation play on the PlayerBody, and once the clip completes, it fires an event which is communicated to the Player script, which then adds the PlayerBody's local position/rotation to the Player's global position/rotation, then zeros the PlayerBody's local position/rotation, all in a simple function called ApplyRootMotion(). (another sidenote: I found that I couldn't do this in an event handler, so I just set a flag where I can do this in the next FixedUpdate). For the PlayerBody, I just modify its local transforms, but with the Player, I directly set Rigidbody.position and Rigidbody.rotation, as I need these transitions to happen instantly, unlike normal movement (which uses MovePosition and MoveRotation for the interpolation)

    This usually works, but I recently was given an animation that is flipped in the Z direction. Naturally, I just rotate the PlayerBody around 180 deg on the Y axis before the animation starts, then reset back to Quaternion.idenitity once it gets the completion flag. The reset-on-completion happens before I make the call to ApplyRootMotion(), so I'm not applying the 180 rotation to the Rigidbody. But, no matter what I do, the last rotation seems to be "interpolate", or at least happen gradually across frames. I'm not confident that its Rigidbody interpolation, or just interpolation of the PlayerBody transform rotation.

    I made these test functions to look at the problem atomically with simple button bindings:

    // body = PlayerBody (child mesh)
    // rb = RigidBody on the Player (parent GameObject)
    private readonly Vector3 _invertedAnimCorrection = new (0, 180, 0);
    private bool rotateBodySwitch = true;
    private void RotateBody()
    body.localEulerAngles = rotateBodySwitch
    ? _invertedAnimCorrection
    rotateBodySwitch = !rotateBodySwitch;

    private bool rotateRbSwitch = true;
    private void RotateRigidbody()
    // rb.interpolation = RigidbodyInterpolation.None; // Line X
    rb.rotation = rotateRbSwitch
    ? Quaternion.Euler(_invertedAnimCorrection)
    : Quaternion.identity;
    rotateRbSwitch = !rotateRbSwitch;

    I only saw the slow rotation happening on RotateRigidbody(), and when I un-comment Line X to turn off interpolation (I have another line in FixedUpdate() which switches interpolation back on), the slow rotation is gone. This was backed up by my observed behavior when manually changing the Interpolation field on the RB in Unity. Also, the "slow" rotation was quite random in its actual speed. Sometimes it was a slow burn, sometimes fast, sometimes immediate, sometimes it would jump to halfway thru the rotation and slowly complete it (note that I was looking with TimeScale way down)
    So I was indeed getting a gradual, interpolated rotation when directly setting the Rigidbody.rotation, which directly contradicts whats in the docs.
    Even with the "immediate" rotations (both the PlayerBody rotations and the RB rotations with Interpolate=None), I can see a single frame where it looks like it could be interpolating, or it could be a weird blur. But this doesn't get any more visible, even at extremely low TimeScales--always just one frame (not physics frame).
    And, when I added the interpolation switching to my real code, it didn't work! Even when I remove all Rigidbody rotation altogether (but keep the displacement), and remove the reset where I rotate the PlayerBody back to face forward after the animation, I still get a slow rotation (after the animation is done and the PlayerBody is still locally rotated by 180 deg, the correctly-facing Idle animation takes over within the correctly-facing Rigidbody and so the PlayerBody slowly rotates to adhere to its erroneous local rotation--so that must be coming from the PlayerBody transform??)

    Anyone got a clue? or is this a bug? Where is the bug?
    Wondering if it could be related the the animation crossfading, but in my button test, there was no animation change, and I was still seeing erroneously interpolated rotations from direct Rigidbody.rotation assignments.