-3

i like to rotate a cube on the vertical as well as the horizontal axis. For this I have 4 inputs. Rotate left/right and up/down. The input ranges from -180 to 180 degrees on each axis.

I have tried this so far:

Vector3 targetEulerAngles = new Vector3(y, 0, 0); // Target rotation in Euler angles
 m_transForYaw.eulerAngles = (targetEulerAngles);

targetEulerAngles = new Vector3(0, 0, -x);

m_tranfForPitch.eulerAngles= (targetEulerAngles);

m_transRotateAround.rotation = m_transForYaw.rotation * m_tranfForPitch.rotation;

My question/ problem is: when the x rotation is over 90 then the m_transForYaw does not rotate to the side but rotates around the forward vector.

I have also tried Rotate around.

Do you know how to solve this that it only rotates to left/right regardless of prevous rotations.

As I see it the problem is that I previously rotated the cube and thats the problem. E.g.

  1. I rotated on Vector.right -> normal behaviour -> the cube turns forward/backwards
  2. Now I rotate on Vector.up. -> the cube does now not rotate to the left/right but rotates around the global Vector.forward axis

Here is a video for showcasing the problem.

https://drive.google.com/file/d/1PZ4ItbmmSByU3dfGp2bmKzmcD1yqXBf_/view?usp=sharing enter image description here

11
  • You mean always rotate in world space instead of local? Why not directly m_transRotateAround.rotation = Quaternion.Euler(y, 0, -x); ? Commented Dec 23, 2024 at 18:03
  • 1
    Otherwise you might be looking for this Commented Dec 23, 2024 at 18:29
  • The problem is when I turn the cube 90 degrees on the x axis. The the y rotation does not move from left to right but rotates around the forward axis then Commented Dec 23, 2024 at 20:55
  • 1
    What are m_transForYaw, m_tranfForPitch, and m_transRotateAround meant to be? the objective of this question is very unclear. Maybe adding some examples of desired inputs and outputs would clear things up. Consider including drawn diagrams or mocked screenshots in the body of the question. Commented Dec 23, 2024 at 21:49
  • 1
    So wait .. they are all in the same hierarchy? So why even rotate the most bottom at all? It is a bit hard to understand what exactly your desired behavior looks like .. but from what I get you probably will want to go for the worldSpaceRotation * currentRotation approach Commented Dec 23, 2024 at 23:11

2 Answers 2

1

Interpreting the question as a "why can this be expressed as a series of rotations in local space" question

For the case of an object starting as non-rotated, combining rotations in a certain order in the axes as they change each time (in other words, local axes) is always equivalent to combining rotations in the reverse order on the global axes. This is just a quirk how rotations work in euclidean/3d space and not something that can be avoided.

a tale of two rotations

Note that the bottom path shows the result of only global rotations (which as far as I can tell is what you are after) and the top path is how you described it in your diagram and text which is explaining that it is incorrect. Both are valid interpretations of outputTransform.rotation = Quaternion.Euler(-90f,0f,0f) * Quaternion.Euler(0f,-90f,0f);.

And this equivalence is true of any-sized sequence of rotations (again, starting with a non-rotated object)

enter image description here

Edit: In your particular case, you're comparing the results of two different sequences of rotations which start from the same orientation but one result being presented first which creates the illusion of the object being rotated from that first presented orientation. Note that your code doesn't use any data about the orientation from the previous frame at all.

Here's a diagram to help explain what that means in your case (I designated some local axes for your cube for the sake of illustration):

enter image description here

Suppose you have some orientation on frame A made up of two 90º rotations on x and y axes. Note that the result of frame A can be expressed as a sequence of local or global rotations (1st and 2nd rows) but the result is the same.

Suppose on frame B you then "add" another 90 degrees of rotation on the y axis. Because your eyes are seeing frame A before frame B, it appears like it must be a local rotation [see "BEFORE CALCULATIONS FRAME B (apparent)]. But you have to remember that your code is starting with the cube in the same unrotated state. That means you can also express that result as a series of global rotations (bottom row).

If you want a different kind of calculation, you probably want to use the orientation of the cube from the previous frame but once that is added as an input then the same knob positions will often not result in the same output orientation.

If you already have the x,y, and rotation from the previous frame, here's a method which calculates the rotation of the current frame. This is just one way of doing it:

private Quaternion HandleKnobs(float previousX, float previousY, float x, float y,
        Quaternion previousRotation) {
    float xDiff = x - previousX;
    float yDiff = y - previousY;
    Vector3 axis = new Vector3(y,-x,0f);
    Vector3 magnitude = Mathf.Sqrt(xDiff*xDiff + yDiff*yDiff);
    float scale = 2.0f // tune as appropriate
                       // do not include deltaTime here
    return Quaternion.AngleAxis(magnitude * scale, axis) * previousRotation;
}

// Example use:
// m_transRotateAround.rotation = HandleKnobs(prevX, prevY, x, y, 
//         m_transRotateAround.rotation);
Sign up to request clarification or add additional context in comments.

1 Comment

If someone knows about Quaternions it's always you ^^ I was just wondering .. wouldn't this still mean that the second rotation around Z is still being applied as relative to the first rotation around Y? I somewhat understood OP that the rotation should be applied in world space axes regardless of the objects current rotation .. still not sure how this is supposed to look like though
0

I ended up with a simpler approach to achieve my desired rotation. Instead of dealing with complex quaternion multiplications, I calculate the difference in yaw and pitch angles and use the RotateAround method around the desired vector.

Here’s the code:

float pitchAngle = Mathf.Lerp(m_minHorizontalRotation, m_maxHorizontalRotation, (fHorizontalMarkerPos + 1f) / 2f); // Horizontal rotation (left/right)
float yawAngle = Mathf.Lerp(m_minVerticalRotation, m_maxVerticalRotation, (fVerticalMarkerPos + 1f) / 2f); // Vertical rotation (up/down)

// Calculate the difference in angles
float difPitchAngle = pitchAngle - prevPitchAngle;
prevPitchAngle = pitchAngle;

float difYawAngle = yawAngle - prevYawAngle;
prevYawAngle = yawAngle;

// Apply the rotation
m_transRotateAround.RotateAround(m_transRotateAround.position, Vector3.right, difYawAngle);
m_transRotateAround.RotateAround(m_transRotateAround.position, Vector3.up, difPitchAngle);

Why I chose this approach, and what I have tried

This method worked for me because it bypasses issues with previous rotations and allows me to work directly with world vectors. What I tried before (and why it didn’t work)

  1. Using Quaternion.Euler for combined rotations:
outputTransform.rotation = Quaternion.Euler(pitchAngle, 0f, 0f) * Quaternion.Euler(0f, yawAngle, 0f);

This approach didn’t work if the transform already had a previous rotation, as I wanted the rotation to be applied relative to world vectors, not the local orientation.

  1. Using transform.LookAt(target): While this can work in some cases, it also had issues with prior rotations and didn’t produce the result I wanted.

Sequential Quaternion.AngleAxis rotations:

transform.rotation = Quaternion.AngleAxis(yawAngle, Vector3.right) * transform.rotation;
transform.rotation = Quaternion.AngleAxis(pitchAngle, transform.up) * transform.rotation;

This resulted in strange initial rotations and behaved unpredictably.

More complex approaches like HandleKnobs posted by Ruzihm: I could set it up and it was a good approach, but a bit to complicated for my usecase.

Thanks for everyone who has commented and helped with this issue. Happy new year :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.