strange rotating at an angle of 90 degrees

Hello everyone, I am currently in the process of writing a spaceship controller, unfortunately I have a problem as soon as I fly a winekl of 90 degrees up or down, the spaceship starts to rotate uncontrollably

Maybe one of you has an idea what it could be, I actually expect that it flies a looping or just 90 degrees up but without rotating

using UnityEngine;
using Cinemachine;

public class SpaceshipController : MonoBehaviour {

    [SerializeField] private InputReader inputReader;
    [SerializeField] private CinemachineFreeLook freeLookCamera;
    [SerializeField] private SpaceshipDataSO spaceshipData;

    [SerializeField] private Transform shipModel;
    [SerializeField] private Rigidbody spaceshipRigidbody;

    [SerializeField] private float steeringSmoothing;
    [SerializeField] private float thrustSmoothing;

    private Vector3 rawInputSteering;
    private Vector3 smoothInputSteering;

    private float rawInputThrust;
    private float smoothInputThrust;


    private void OnEnable() {
        inputReader.SteeringEvent += OnSteering;
        inputReader.ThrustEvent += OnThrust;

        inputReader.EnableGameplayInput();
    }

    private void OnDisable() {
        inputReader.SteeringEvent += OnSteering;
        inputReader.ThrustEvent += OnThrust;
    }

    private void OnSteering(Vector2 inputSteering) {
        rawInputSteering = new Vector3(inputSteering.y, 0, -inputSteering.x);
    }

    private void OnThrust(float inputThrust) {
        rawInputThrust = inputThrust;
    }

    private void Update() {
        smoothInputSteering = Vector3.Lerp(smoothInputSteering, rawInputSteering, Time.deltaTime * steeringSmoothing);
        smoothInputThrust = Mathf.Lerp(smoothInputThrust, rawInputThrust, Time.deltaTime * thrustSmoothing);

        freeLookCamera.m_XAxis.Value = spaceshipData.CameraTurnAmount * smoothInputSteering.z;
        freeLookCamera.m_Lens.FieldOfView = 40 + (smoothInputThrust * 10);
    }

    private void FixedUpdate() {
        spaceshipRigidbody.velocity = transform.forward * spaceshipData.ThrustAmount * (Mathf.Max(smoothInputThrust, 0.2f));

        Vector3 newTorque = new Vector3(smoothInputSteering.x * spaceshipData.PitchSpeed, -smoothInputSteering.z * spaceshipData.YawSpeed, 0);
        spaceshipRigidbody.AddRelativeTorque(newTorque);

        spaceshipRigidbody.rotation = Quaternion.Slerp(spaceshipRigidbody.rotation, Quaternion.Euler(new Vector3(transform.localEulerAngles.x, transform.localEulerAngles.y, 0)), 0.5f);

        shipModel.localEulerAngles = new Vector3(smoothInputSteering.x * spaceshipData.LeanAmountY, shipModel.localEulerAngles.y, smoothInputSteering.z * spaceshipData.LeanAmountX);
    }
}

This is likely an issue with the problems with relying on euler angles, where you seem to be getting a mix of gimbal lock, and how a set of euler angles can represent multiple different rotations.

You’ll likely want to deal with exclusively quaternions (as in rotations) here, using the methods provided by the Quaternion class to generate the right rotations.

Kind of hard to explain in words, so I bunged out this script that will rotate an object to input and also ‘correct’ towards that magical space ‘up’ that most spaceship games like to do:

using UnityEngine;

public class ShipController : MonoBehaviour
{

    private void Update()
    {
        // input
        float xInput = Input.GetAxis("Horizontal");
        float yInput = -Input.GetAxis("Vertical");

        float leftRoll = Input.GetKey(KeyCode.Q) ? 1 : 0;
        float rightRoll = Input.GetKey(KeyCode.E) ? -1 : 0;

        float roll = leftRoll + rightRoll;

        // rolling ship to input
        Quaternion shipRotation = transform.rotation;
        Quaternion xRot = Quaternion.AngleAxis(xInput, transform.up);
        Quaternion yRot = Quaternion.AngleAxis(yInput, transform.right);
        Quaternion rollRot = Quaternion.AngleAxis(roll, transform.forward);

        shipRotation = xRot * shipRotation;
        shipRotation = yRot * shipRotation;
        shipRotation = rollRot * shipRotation;

        // rotating to magic space up
        Vector3 worldUp = Vector3.up;
        Vector3 projectdWorldUp = Vector3.ProjectOnPlane(worldUp, transform.forward);
        projectdWorldUp.Normalize();

        // help visualise the up vectors
        Debug.DrawRay(transform.position, projectdWorldUp, Color.yellow);
        Debug.DrawRay(transform.position, transform.up, Color.red);

        float upSignedAngle = Vector3.SignedAngle(transform.up, projectdWorldUp, transform.forward);        

        if (!Mathf.Approximately(upSignedAngle, 0f))
        {
            float signedDir = Mathf.Clamp(upSignedAngle, -1f, 1f);
            Quaternion forwardRoll = Quaternion.AngleAxis(0.5f * signedDir, transform.forward);
            shipRotation = forwardRoll * shipRotation;
        }

        transform.rotation = shipRotation;
    }

}

Throw this on a cone or something that you can use to visualise ‘forwards’ and play around with it. You’ll notice there’s no wigging out when you face directly up or down.

As you can see we just use rotations here, using Quaternion.AngleAxis to rotate on the main three axis, and combining said rotations by multiplying them together.

Naturally this can be improved to be more smooth, and, of course, less hard coded.

Few pointers for your code too: Break down your lines. Do one thing at a time with values that are descriptively named. Makes it easier for you and others to read your code.

Another tip is that your Lerp functions aren’t doing what you think they’re doing. You’re inputting the same delta into them every time, meaning whatever the result of Time.deltaTime * steeringSmoothing is for example, is as far as the lerp is going to go. You likely want to be using something like Vector3.MoveTowards to gradually move towards a desired value.

Also if you’re manipulating a rigidbody’s rotation, you should be using RigidBody.MoveRotation().

1 Like

Spiny is right it looks like you are not wrapping your euler angles and you have a half and half quaternion situation as well. So you should decide which method you prefer.

Your euler angles display a negative which is on track for a gimbal lock. It means you’re rotating like a quaternion for an euler angles which won’t work.

Read through spineys advice or else make a wrap for euler angles and implement them properly but seems it would rewrite alot of code.

Agreed, but our resident StarManta has a fantastic summary:

All about Euler angles and rotations, by StarManta:

https://starmanta.gitbooks.io/unitytipsredux/content/second-question.html

1 Like