0
\$\begingroup\$

I have some code that allows an object to be rotated back and forth on its Z axis, but am having issues when the object has an initial Y rotation of exactly 180 degrees while also trying to rotate it using Rigidbody2D.MoveRotation(). To be specific, the object spazzes out and rotates wildly all over the place.

If I change how I rotate the object by using transform.rotation = someValue, this problem never occurs. Likewise, changing the initial Y rotation to 179.9 or 180.001 alleviates the issue.

It is important for this code to work regardless of the Y rotation, as certain scenes in my game dynamically spawn these rotating objects in a variety of initial rotations. I am using the 180 degree Y rotation to mirror/flip some of the objects.

using UnityEngine;

[RequireComponent(typeof(Rigidbody2D))]
public class RotateBackAndForth2D : MonoBehaviour
{
    [SerializeField] private float rotationDegrees = 45;
    [SerializeField] private float rotationTime = 5;

    private new Rigidbody2D rigidbody;

    private Quaternion minRotation;
    private Quaternion maxRotation;

    private void DoRotation()
    {
        float rotationDelta = Mathf.PingPong(Time.time / rotationTime, 1);
        rotationDelta = Mathf.SmoothStep(0, 1, rotationDelta);

        Quaternion targetRotation = Quaternion.Slerp(
            minRotation,
            maxRotation,
            rotationDelta);

        // Doesn't work sometimes
        rigidbody.MoveRotation(targetRotation);

        // Always works
        // transform.rotation = targetRotation;
    }

    private void FixedUpdate()
    {
        DoRotation();
    }

    private void OnEnable()
    {
        minRotation = Quaternion.Euler(new Vector3(
            transform.eulerAngles.x,
            transform.eulerAngles.y,
            transform.eulerAngles.z - rotationDegrees));
        maxRotation = Quaternion.Euler(new Vector3(
            transform.eulerAngles.x,
            transform.eulerAngles.y,
            transform.eulerAngles.z + rotationDegrees));
    }

    private void Awake()
    {
        rigidbody = GetComponent<Rigidbody2D>();
        rigidbody.bodyType = RigidbodyType2D.Kinematic;
    }
}
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Your problem is in how you compute your min and max rotations.

Euler angles are half-decent for human data entry, but they are miserable for doing math in because they necessarily contain wrap-around points. Compute your quaternions using quaternion math instead:

minRotation = transform.rotation 
    * Quaternion.Euler(0 0, -rotationDegrees);

maxRotation = transform.rotation 
    * Quaternion.Euler(0 0, rotationDegrees);

This takes the object's current rotation and applies a delta of ±rotationDegrees around its local z axis, similar to what you were trying to do with the Euler angles.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .