Zinnia.Unity: Wrong velocity calculation in RigidbodyAngularVelocity follow modifier

Environment

  • Zinnia version: 1.25.0
  • Unity version: 2018.3+
  • 3rd party dependencies: None

Steps to reproduce

  1. Create an empty Unity project with Zinnia package added to it.
  2. Create a new empty scene.
  3. Setup objects: 3.1. Create a cube, name it “Follower Cube”. Scale to (0.1; 1; 10). 3.2. Create another cube, “Target Cube”. Scale to (0.1; 1; 10). 3.3. Create a new Material, paint it bright red and apply it to the “Follower”. It will help distinguish the objects visually. 3.4. Add non-kinematic Rigidbody to the “Follower Cube”. 3.5. Remove colliders form both cubes.
  4. Create script to rotate the “Target Cube” in FixedUpdate: 4.1. Create a new script, “RotateInFixedUpdate” (sample code below). Make it rotate a transform in FixedUpdate: 4.2. Add this script component to “Target Cube”.
  5. Setup Zinnia’s “ObjectFollower” script. 6.1. Put “Target Cube” to “Sources” list. 6.2. Put “Follower Cube” to “Targets” list. 6.3. Use “Rigidbody Angular Velocity” as Rotation Modifier. 6.4. Use any modifiers for position and scale (i.e. “Transform Position”, “Transform Scale”).
  6. In “Project Settings -> Time”, reduce physics timestep to 0.05 (20 FPS). This will make it easier to observe the effect.
  7. Run the scene.

Code for “RotateInFixedUpdate”:

using UnityEngine;

public class RotateInFixedUpdate : MonoBehaviour
{
    public float rotationSpeed = 60f;

    private void FixedUpdate()
    {
        var rotationDelta = new Vector3(0f,rotationSpeed,0f) * Time.deltaTime;
        transform.Rotate(rotationDelta);
    }
}

Expected behavior

“Follower” follows the “Target” rotation smoothly (with small offset due to FixedUpdate).

Current behavior

“Follower” jumps around the “Target” orientation each frame, but fails to align with it.

Possible cause

Current implementation of RigidbodyAngularVelocity follow modifier script calculates angle difference in degrees, clamps it and applies it directly to the velocity of follower Rigidbody (lines 48-61 of the original script):

Quaternion rotationDelta = source.transform.rotation * Quaternion.Inverse(offset != null ? offset.transform.rotation : target.transform.rotation);

rotationDelta.ToAngleAxis(out float angle, out Vector3 axis);
angle = angle.GetSignedDegree();

if (!angle.ApproxEquals(0))
{
    Vector3 angularTarget = angle * axis;
    Vector3 calculatedAngularVelocity = Vector3.MoveTowards(cachedTargetRigidbody.angularVelocity, angularTarget, MaxDistanceDelta);
    if (float.IsPositiveInfinity(AngularVelocityLimit) || calculatedAngularVelocity.sqrMagnitude < AngularVelocityLimit)
    {
        cachedTargetRigidbody.angularVelocity = calculatedAngularVelocity;
    }
}

There are 2 problems with this:

  1. “angle” is not converted to radians. Rigidbody.angularVelocity accepts radians, not degrees.
  2. “angle” is not divided by Time.deltaTime before being applied as a velocity.

Proposed solution

Convert angle to radians and divide it by deltaTime before applying it as angular velocity. This can be done by changing line 51 of current RigidbodyAngularVelocity script to:

angle = angle * Mathf.Deg2Rad / Time.deltaTime;

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 25 (22 by maintainers)

Most upvoted comments

Yup, this is the problem. I’ve added a GetSignedRadian method and turn the radians into signed radians and it fixes the problem with grabbing, and doesn’t introduce a problem with this original ticket

the thing you’re talking about is already applied in the interactable prefab. It’s the MaxAngularVelocity by default is randomly set to 7 by unity, but it is forced to be infinity to prevent such issues.