EulerAngles conversion Quaternion problem


I want to tilt the body of a vehicle in order to simulate a (cheap but still) suspension.

Here is my code:

float tilt = (Input.GetAxis("Horizontal") * 3.0f); // Rotate the body by -3/3 degree
Vector3 targetRot = new Vector3(270 + tilt, body.transform.eulerAngles.y, body.transform.eulerAngles.z);
Quaternion targetRotation = Quaternion.Euler(targetRot);
body.transform.rotation = Quaternion.Slerp(body.transform.rotation, targetRotation, Time.time * 0.1f);

I’m tilting on the X axis because it corresponds to the body’s forward.

My problem is the vehicle doesn’t rotate correctly. It should go from 267 to 273 degrees depending on the Input. But it never goes below 270.

I found that the issue comes after “Quaternion.Euler”. It always return an EulerAngle between 270 and 273.

Did I forget something ? How could I fix this ?

Thank you in advance !

As @Kryptos said, there are several Euler angles combinations that result in the same rotation. This may become a big problem when you try to modify one of the Euler angles: Unity may return a completely different combination when one of the other angles crosses some “magical” boundary (changes signal, crosses 180 degrees etc.), and the resulting rotation suddenly becomes completely wrong.

The easiest way to tilt an object independently of its current orientation is to make it a child of the main object and modify its localEulerAngles:

  • Car hierarchy:

    Car ← main object, which moves and turns
    Body ← empty game object that will be tilted forth and back
    BodyModel ← actual body model

  • Body script:

    public float speed = 5.0f;

    void Update(){
    Vector3 tilt = new Vector3(Input.GetAxis(“Horizontal”) * 3.0f, 0, 0);
    transform.localEulerAngles = Vector3.Lerp(transform.localEulerAngles, tilt, speed * Time.deltaTime);

The speed variable controls the tilting time: 5 means that the object will take about 1 second to reach 95% of the desired rotation, 10 means half second, and so on (0.1f would take 50 seconds! that’s why the body didn’t even move!). Input.GetAxis has its own smoothing time, which will add to the Lerp effect, thus you may have to use higher speed values.

NOTE: Notice that an extra parenting level was added: the Body empty object is sandwiched between the Car and the BodyModel. From your code, it seems that the model have flipped axes (a common problem when importing models), thus it must be rotated to (270,0,0) to be properly orientated. The Body empty object works as an interface between the model and the car: it can have localEulerAngles = (0,0,0) while allowing complete freedom to orient the model. You can eliminate this intermediate object, if you want: child the model directly to the Car, attach the script to the model and change the base X angle to 270, as you did in your code.

Since you are only changing the x-value (roll) of the euler angles why do you use quaternions?

float tilt = (Input.GetAxis("Horizontal") * 3.0f); // Rotate the body by -3/3 degree

Vector3 eulerAngles = body.transform.eulerAngles;
eulerAngles.x = Mathf.LerpAngle(eulerAngles.x, 270+tilt, Time.deltaTime*0.1f);

body.transform.eulerAngles = eulerAngles;

Depending on the situation, you may need to use localEulerAngles instead of eulerAngles.