2
\$\begingroup\$

It's been a long two weeks. Quaternions are freaking complicated. I've managed to make a few baby steps towards finishing my project, but I've hit a brick wall and could use a few of your beautiful minds to help me past it.

I'm making an asset for Unity that will automatically rotate/shift objects to make them appear 2.5D. What I'm having trouble with is the rotation part. I go through a trigonometric process to get a calculation of how much, between 0 and 180 degrees (typically), that the object should rotate on its x-axis.

The rotation of this object should be locked between 0 and 180 degrees. Think of it this way: my trigonometric calculation is telling me whether the object should be flipped right-side-up or upside-down

So, we've got the angle it should rotate. Just plop it in a Quaternion.Euler(MyNewXValue, prevEulerY, prevEulerZ) and we should be good, right? WRONG.

There are a number of things wrong with this.

  1. The previous eulers can come back a bit wonky since they can correspond to multiple values in the -360 to +360 degree range, so we have to "normalize" them a bit.
  2. Due to the rotation order, it ends up "swirling" in 3D space and altering the other two rotations instead of maintaining a stable x-axis rotation change.

With that said, here is my code:

// C#

if (current_euler.z < 180f)
{
   rot = Quaternion.Euler(rotation_angle.x, current_euler.y, current_euler.z);
} else
{
   rot = Quaternion.Euler(rotation_angle.x, current_euler.y-180, current_euler.z-180);
}

Ignore the (if < 180). That's just so I can get this test running and working. I'll find a proper way to normalize it later.

Anyway, this snippet works, but has one problem: It twirls in 3D space and doesn't remain locked on its x-axis. I've tried numerous other solutions, but they either spin out of control or... they all spun out of control.

My question: Is there a way to set the rotation by rotating on one axis, while allowing the other rotations to change at the user's command without it "swirling" in 3D space?

EDIT: SOLUTION

For my use case I was able to use transform.localScale and modify it's Y-scale as a solution to my issue. It has the exact effect as I was looking for, and is definitely more stable. I accepted @Lunin's answer, however, as it seems to be another possible solution, and is written on this forum :).

To clarify, my wish was to rotate a 2D object on its x-axis, which would cause it to rotate vertically. At the same time, I wanted to allow the other axes to be editable at any given time. I can mimic a vertical rotation by changing the y-scale of the 2D object. +1 y-scale is normal, -1 y-scale is flipped upside down. Since I only needed that one axis to change, this workaround worked perfectly.

\$\endgroup\$
  • \$\begingroup\$ It's generally unsafe to modify one angle in an Euler angle triplet - they're really meant for serializing & deserializing an orientation, not computing modified orientations. In this case, the simplest way to get what you want is likely to use parenting: wrap your object in an empty parent, and apply your yaw & roll there. Then apply your pitch to the child object's local rotation. This lets you be explicit & clear about the precedence order you want to use for your rotations, if you're uncomfortable with doing the math on quaternions yourself. \$\endgroup\$ – DMGregory Mar 16 at 20:49
  • \$\begingroup\$ Can you give us a bit more context of what's setting / modifying current_euler? \$\endgroup\$ – DMGregory Mar 17 at 12:42
  • \$\begingroup\$ Hi @DMGregory! Thanks for the question. Current_euler was a variable set a few lines before this. It was essentially a Vector3 of obj.transform.eulerAngles with nothing modifying it. I've since been able to get past this issue, as member in the game dev community I'm in was kind enough to dedicate a good amount of time to understand my issue and provide a work-around for me that works perfectly/maybe even better! \$\endgroup\$ – gmanicus Mar 18 at 5:40
  • \$\begingroup\$ It's unclear from this question how modifying the y scale produces the rotation behaviour you wanted. Can you edit the question to clarify, then post your solution as an Answer so others can benefit from this technique too? \$\endgroup\$ – DMGregory Mar 18 at 10:56
  • \$\begingroup\$ Will do! ...... \$\endgroup\$ – gmanicus Mar 18 at 21:43
1
\$\begingroup\$

I'm not sure I entirely grok what you're going for, however if you're looking to set one axis explicitly a good choice is Quaternion.LookRotation

This will set the forward axis to pointing at whatever you want, while spinning the rest around that axis in such a way as to keep the up axis pointing as "Up" as possible. The nice thing is, "Up" is another parameter you can put in, so you can use this function to allow a 2D direction around said rotation. You may find the axes being changed are not what you want, but it's not too bad to either do it to a container object where the "forward" is actually the direction you want to change or to rotate the resulting Quaternion afterwards into the orientation you want.

Just remember that with this method, the "Forward" angle gets priority, and the "Up" gets as close as it can given the "Forward", so you can decide based on your logic which angle should most get its way based on the gameplay you're going for :)

\$\endgroup\$
  • 1
    \$\begingroup\$ Thank you! I'm going to accept this as the solution because it seems solid. However, I was discussing this issue with another member of the game dev community I'm a part of, and he was kind enough to dedicate a lot of time to thinking through my issue and providing a solution for me. He ended up pointing me to Transform.localScale. Altering the y-scale of the objects in question has the exact same effect as what I was trying to produce, but thank you for your answer nonetheless! \$\endgroup\$ – gmanicus Mar 18 at 5:37
  • \$\begingroup\$ Glad to hear you found a solid solution for what you were going for :) \$\endgroup\$ – Lunin Mar 18 at 7:06
1
\$\begingroup\$

For my use case I was able to use transform.localScale and modify it's Y-scale as a solution to my issue. I simply got a proportion of my trigonometric calculation's result and turned it into a value between -1 and +1. It has the exact effect as I was looking for, and is definitely more stable.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.