Rigidbody2d Collision Breaks Quaternion.Slerp

I am trying to use the following function during every update to rotate a rigidbody2d sprite (spaceship) towards the mouse using rotateTorque as a speed. This works perfectly until I use the other controls to drive the object into another rigidbody2d collider (planet) which causes the spaceship to get left upside down while my mouse is still pointing up. At this point the Slerp function seems to slow down the rotation such that it is barely rotating towards my mouse anymore. It seems as though colliding with objects through the physics engine somehow breaks the interpolation of the slerp.

How do I fix this?

It works perfectly before colliding with anything and even sometimes after but depends on the final rotation of the spaceship. Nothing else is interfering with the spaceships rotation.

    void HandleRotation() {
        Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
        float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
        Quaternion to = Quaternion.AngleAxis(angle, Vector3.forward);
        Debug.Log("from " + transform.rotation.eulerAngles + " to " + to.eulerAngles + " mouse " + Camera.main.ScreenToWorldPoint(Input.mousePosition));
        transform.rotation = Quaternion.Slerp(transform.rotation, to, rotateTorque * Time.fixedDeltaTime);
        Debug.Log("new " + transform.rotation.eulerAngles + " time " + (rotateTorque * Time.fixedDeltaTime));
    }

Here is a log of when it gets stuck. It seems somehow something is reverting the rotation, but I have checked all my code and nothing else changes the transform and the object is no longer touching any other rigidbody2d/colliders

from (0.0, 0.0, 48.6) to (0.0, 0.0, 89.8) mouse (-0.3, 1.1, -10.0)
UnityEngine.Debug:Log(Object)
BaseShip:HandleRotation3() (at Assets/BaseShip.cs:140)
BaseShip:Update() (at Assets/BaseShip.cs:210)
new (0.0, 0.0, 50.1) time 0.03580748
UnityEngine.Debug:Log(Object)
BaseShip:HandleRotation3() (at Assets/BaseShip.cs:142)
BaseShip:Update() (at Assets/BaseShip.cs:210)
from (0.0, 0.0, 48.4) to (0.0, 0.0, 89.8) mouse (-0.3, 1.1, -10.0)
UnityEngine.Debug:Log(Object)
BaseShip:HandleRotation3() (at Assets/BaseShip.cs:140)
BaseShip:Update() (at Assets/BaseShip.cs:210)
new (0.0, 0.0, 49.9) time 0.03610305
UnityEngine.Debug:Log(Object)
BaseShip:HandleRotation3() (at Assets/BaseShip.cs:142)
BaseShip:Update() (at Assets/BaseShip.cs:210)

Don’t know why you actually have the issue with collisions, but I see one suspicious thing in your code.

Quaternion.AngleAxis() doesn’t give you a ‘full’ resulting rotation. It gives you just a ‘partial’ rotation which is not related to anything and it just represents some rotation from some (not defined) rotation for given degrees around the given axis.

To get the actual rotation that your transform should rotate to in the end of the Slerp() you need to ‘add’ the to rotation to the current transform rotation:

Quaternion to = transform.rotation * Quaternion.AngleAxis(angle, Vector3.forward);

This will be the rotation which is your current rotation plus the rotation around Z axis for angle degrees.


PS: since I’m not smart enough for Tan’s and Sin’s, I prefer to use Vector3.SignedAngle() to get the angle between two direction vectors.

angle = Vector3.SignedAngle(direction, transform.forward, Vector3.forward);

This gives an angle between ship-to-mouse direction and current ship direction, related to the Z axis, e.g., it will be -90 when the ship is looking right and mouse is above the ship. Probably the same as your math stuff, I don’t know.

I was able to find a fix! I needed to use RigidBody2D.MoveRotation() instead of setting transform.rotation. This allows the player input and physics engine to both control the rotation properly without interfering with each other. I probably should also put this inside of a FixedUpdate call instead of Update, but Update seems to be fine for now.