rotate local z and world y without affecting transform local x rotation

Hello,

I have been battling with a problem for some time now… I need to control rotation around the x-axis with Input.GetAxis(“Vertical”) and around the y-axis with Input.GetAxis(“Horizontal”). Additionally I would like the yaw around the y-axis to also rotate the game object around the local z-axis without affecting the other axes. However, with my current solution rotating around world y and local z slightly rotates the object around the x-axis as well when continuously rotating the object with Input.GetAxis(“Horizontal”) for world y and local z. How can i rotate the gameobject around world y and local z simultaneously without it affecting the x-axis?

Here’s the code:

void Update () {
       
        yaw = Input.GetAxis("Horizontal");
        roll = yaw;
        pitch = Input.GetAxis("Vertical");

        RotateAircraft();
}

void RotateAircraft()
    {
        Vector3 rotationAngle = Vector3.zero;
        rotationAngle += transform.right * pitch;
        rotationAngle += Vector3.up * yaw;
        rotationAngle += transform.transform.forward * -roll;

        transform.RotateAround(transform.localPosition, rotationAngle, rotationSpeed * Time.deltaTime);
    }

Thanks!

I suppose you tried doing them one at a time.

2 Likes

As mentioned, you can try just do RotateAround for one control at time.

If you still get problems you can:

  1. Store the current X rotation before doing anything else.
  2. Apply your new Y and Z rotation.
  3. Restore the X rotation (but keep the updated Y and Z rotation).
  4. Apply your new X rotation.
1 Like

Of course you gonna get the appearance of the aircraft performing a minor barrel roll (not to be confused with an aileron roll), you using one input axis to control two euler axis, and the result is gimbal lock. Instead use Quaternions to rotate the your aircraft. its beneficial to get familiar with the concept of Quaternions, especially in an game involving aircraft.

The exact solution depends on the type of game you’re making. is it a rail shooter like Star Fox, an arcade like Ace Combat, or a flight sim like… Flight Simulator? Each of these games use vastly different flight models from each other so the way these aircraft rotate varies, and most of it is due to how the camera behaves.

1 Like

You should avoid using eulerAngles whenever possible.

If I understand exactly what you want to do, it can be done with the transform.Rotate function:

var dPitch = GetVerticalRotation ();
var dYaw = GetHorizontalRotation ();
var dRoll = -dYaw;

transform.Rotate (new Vector3 (0, dYaw, 0), Space.World); //Left-right
transform.Rotate (new Vector3 (dPitch, 0, 0)); //up-down
transform.Rotate (new Vector3(0, 0, dRoll)); //roll

The aircraft will get stuck in a roll position, even if there is no change in the yaw. Is that want you want to happen?

3 Likes

This solution works great for rotating the axes without them affecting one another. However, the z-rotation is supposed to stay within -30 to 30 degrees rotation (it is just to add a slight roll to the yaw to give the plane a more realistic animation while still retaining the tight arcade control). I have been using a Slerp to do this but this also seems to cause a flip of the plane when pointing straight up or down. Any idea on how to clamp the z-axis rotation within this range?

This is how the Slerp looks as is now:

Quaternion turnRotationMax = Quaternion.Euler(transformRotation.x, transformRotation.y, 30f * roll);

transform.rotation = Quaternion.Slerp(transform.rotation, turnRotationMax, Time.deltaTime * 10f);

Once you start altering the rotation of a gameobject , you should no longer try to use or interpret Euler angles anymore. This is why people say to avoid using Euler angles if possible.

One solution is: in the script, keep track of how much the aircraft has rolled. And then see if applying the desired roll would cause the ship to roll past the thresholds. If so, don;'t allow the ship to roll past the threshold.

float totalRoll = 0;

public void Update()
{
   var dPitch = GetVerticalRotation ();
   var dYaw = GetHorizontalRotation ();
   var dRoll = -dYaw;

   const float MinRoll = -30f;
   const float MaxRoll = 30f;

        if (dRoll < 0) {
            //Going towards the right (lower limit)
            if (totalRoll + dRoll < MinRoll) {
                //If allowed to rotate this frame, the total rotation would be less than MinRoll
                dRoll = MinRoll - totalRoll;//Set the dRoll so the total rotation  will be the min
                totalRoll = MinRoll; //Set it to minimum allowable roll
            }
        }
        else if (dRoll > 0) {
            //Going towards the left (Max Roll)
            if (totalRoll + dRoll > MaxRoll) {
                //If allowed to rotate this frame, the total rotation would exceed 30 degrees
                dRoll = MaxRoll - totalRoll; //Set the dRoll so the total rotation  will be the maximum
                totalRoll = MaxRoll; //Set it to minimum allowable roll
            }
        }

        totalRoll += dRoll; //Keep track of the total amount the aircraft has rolled

       transform.Rotate (new Vector3 (0, dYaw, 0), Space.World);
        transform.Rotate (new Vector3 (dPitch, 0, 0));       
        transform.Rotate (new Vector3(0, 0, dRoll));
}

From what you are saying you rotate on world y-axis (no contraint) but local x-axis( no contraint) and z-axis (30 degree contraint). The problem you have to realize is what happens when two axis line up, you’re still gonna get gimbal lock. you can have the local z-axis point in the same direction as world y, and suddenly you can’t spin on y without violating z rules. What exactly do you want to happen when the plane is flying straight up and you give only horizontal input?

What happens when the plane performs a half loop and is now upside-down flying the the opposite direction? with the current implementation that I’m seeing yaw controls will be opposite from desired. Your flight model design (i.e. how the aircraft should control at certain orientations and velocities, and how the player camera orients, which can have an effect on input control) which you’ve presented us is incomplete so any implementation people provide here will likely be insufficient. its important to get these rules clarified before a solid flight model can be developed.

I don’t fully know the nature of your game and theres several different types of flight models that could be used for aircraft. And though I’ve narrowed it down from what you’ve described so far, theres still some holes in the design.

Thats still not using Quaternions. Last I checked, transform.Rotate() uses Euler angles.

1 Like

I don’t know if you are familiar with the game Star Wars: Rogue Leader. I’m looking to do something similar to that. I still want the plane to be able to do full rolls… the limited 30 degree roll for the yaw is supposed to function more as an animation than anything. Regarding the pitch loops, I would like the controls to remain the same relative to the airplane no matter the orientation so the player does not suddenly have reverse controls.

Thank you! I will give this code a try.

This works great, but as [JoshuaMcKenzie]( https://discussions.unity.com/t/677296 members/joshuamckenzie.866650/) points out the yaw is reversed when the plane is flying upside-down. I suppose this is due to yaw being world space. Is there a way to retain the control of yaw in world space while also not reversing yaw control when traveling upside-down?

Hi again,

Just wanted to post the solution i ended up with if anyone else should have a similar problem. It would likely be considered a hack solution for the more learned engineers, it does however get the job done. Thank you all for your suggestions and input.

void Start() {
 
        // Aircraft transform is comprised of a container (this.transform) and three child transforms
        // to allow for independent local rotations.
        yzTransform = this.gameObject.transform.GetChild(0);
        xTransform = yzTransform.gameObject.transform.GetChild(0);
        bodyTransform = xTransform.gameObject.transform.GetChild(0);
    }

    void Update () {
     
        yaw = Input.GetAxis("Horizontal") * rotationSpeed * Time.deltaTime;
        pitch = Input.GetAxis("Vertical") * rotationSpeed * Time.deltaTime;
        roll = -yaw;

        if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
        {
            canRoll = true;
        }
        else
            canRoll = false;

        RotateAircraft();
        MoveAircraft();
    }
     
    void RotateAircraft()
    {
        if (!canRoll)
        {
            // If roll-key is not pressed, yaw and pitch respective child transforms according to input.
            yzTransform.Rotate(new Vector3(0, yaw, 0));
            xTransform.Rotate(new Vector3(pitch, 0, 0));

            // Animate a slight tilt of the aircraft's body when yawing.
            Quaternion modelRotation = bodyTransform.localRotation;
            Quaternion toRotation = Quaternion.Euler(0f, 0f, roll * 30f);
            bodyTransform.localRotation = Quaternion.Slerp(modelRotation, toRotation, Time.deltaTime * 10f);
        }
        else
        {
            // If roll-key is pressed allow only for rolling using horizontal input.
            yzTransform.Rotate(new Vector3(0, 0, roll));
        }
    }
2 Likes