0
\$\begingroup\$

Posting this here because I spent several days trying to figure out why I couldn't rotate my entities the way I wanted to in Unity.

For a little bit of background, I'm creating a flight dynamics model using Unity's Entities package. Part of this project involves providing keyboard input to a system in order to manipulate the motion of an aircraft--a very basic simulator.

I encountered an issue when I tried rotating my aircraft entity using the Rotation.Euler() method. In fact, just about every approach I saw on every forum I visited could not provide the answer I was looking for. My keyboard input logic manipulated the bank angle (roll), alpha (pitch), and yaw, and I was trying to set an absolute angle for my entity based on those values. It seemed that I was unable to set the rotation values the way I would in the inspector panel, since rotating the entity one direction would make rotating it along another axis unpredictable.

Such is the nature of quaternions.

Thus, I found a solution to my issue, involving a simple helper function that converts a float3 array of rotation values (one for each axis) to a quaternion. I'll be posting the code and explanation in the answer below.

EDIT: In the previous iteration of my code, this is what I had tried in order to update my entity's rotation:

Vector3 temp = transform.rotation.eulerAngles;
temp.x = -90.0f + state.bank;
temp.y = 0.0f;
temp.z = state.alpha;

Entities.ForEach((ref Translation translation, ref Rotation rotation, ref AircraftData aircraftData) => {
            translation.Value = position;
            rotation.Value = Quaternion.Euler(temp);
        }).Schedule();

All other code within the answer below was the same (minus the new quaternion calculations).

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Can you show us the code you were trying to use previously? This can improve search hits for users struggling with similar attempts. \$\endgroup\$
    – DMGregory
    May 11, 2021 at 0:06
  • \$\begingroup\$ Ahh, there's your problem, you're converting from a quaternion to Euler angles, doing math on those angles, then converting back. The quaternion-to-Euler conversion is not required to return the same numbers you put in, only a trio that accomplishes some equivalent orientation, even if it reaches it by a very different series of three rotations. Did you consider just storing your Euler angle triplet throughout and using the basic Quaternion.Euler for a one-way conversion to quaternion from that persistent source of truth? \$\endgroup\$
    – DMGregory
    May 11, 2021 at 17:39

2 Answers 2

0
\$\begingroup\$

So how does one manipulate the axis angles of an entity without all of the headache?

Here's the function:

public void update_entity(ref State state)
    {
        // velocity information
        float vx = state.q0;
        float vy = state.q4;
        float vz = state.q2;
        // position information
        float x = state.q1;
        float y = state.q5;
        float z = state.q3;

        float3 position = new float3(x, y, z);
        float bank = -90.0f + state.bank;
        float alpha = state.alpha;

        // calculate new quaternion value for rotation
        float roll = bank * Mathf.Deg2Rad;
        float pitch = 0.0f;
        float yaw = alpha * Mathf.Deg2Rad;

        float croll = Mathf.Cos(roll * 0.5f);
        float cpitch = Mathf.Cos(pitch * 0.5f);
        float cyaw = Mathf.Cos(yaw * 0.5f);

        float sroll = Mathf.Sin(roll * 0.5f);
        float spitch = Mathf.Sin(pitch * 0.5f);
        float syaw = Mathf.Sin(yaw * 0.5f);

        float cyawcpitch = cyaw * cpitch;
        float syawspitch = syaw * spitch;
        float cyawspitch = cyaw * spitch;
        float syawcpitch = syaw * cpitch;

        Quaternion newRot = new Quaternion((cyawcpitch * sroll - syawspitch * croll),
            (cyawspitch * croll + syawcpitch * sroll),
            (syawcpitch * croll - cyawspitch * sroll),
            (cyawcpitch * croll + syawspitch * sroll));

        Entities.ForEach((ref Translation translation, ref Rotation rotation, ref AircraftData aircraftData) => {
            translation.Value = position;
            rotation.Value = newRot;
        }).Schedule();

    }

My state struct contains the location, velocity, bank, alpha, and flap projection of the aircraft (The values not used in this function are used later for the instruments UI panel). Using the bank and alpha angles (with a 90 degree offset because of how the aircraft model was spawned), I was able to calculate the appropriate quaternion values (x, y, z, and w) to provide the necessary rotation to my aircraft entity.

Hope this helps someone else out there who struggled to find a solution as bad as I did! :)

\$\endgroup\$
0
\$\begingroup\$

With Unity ECS:

  1. you should use the (new) Unity.Mathematics.quaternion instead of the (old) UnityEngine.Quaternion

  2. you should use quaternion.EulerZXY() which is exactly Quaternion.Euler() Indeed, the order of axis rotations is key and the old Quaternion.Euler() has an order Z, then X, then Y (as per the documentation).

  3. BUT x, y and z are radians while they were degrees

Example: Quaternion.Euler(10,20,30) becomes quaternion.EulerZXY(math.PI / 180 * 10, math.PI / 180 * 20, math.PI / 180 * 30)

Voila

\$\endgroup\$
1
  • \$\begingroup\$ It's been a while since I published the above findings, so the details escape me at the moment, but I do remember EulerZXY giving me issues. I don't exactly recall what those issues were, though. Perhaps when I continue research in this area I'll come back and make a comment with whatever issues I was having with this function :) Thanks! \$\endgroup\$ Jan 31 at 18:24

You must log in to answer this question.

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