Need help figuring out how to rotate XR player with thubstick input

I am using 2021.3 and XR Integration Toolkit. I am testing with an Oculus 2, or Quest 2, whatever they call it now, through Steam VR/Oculus Link. I’m having an issue where, when I rotate the player using thumbsick, it creates some alignment issues.

About my project. The player has a jetpack. I am using continuousMovement on the left controller to move the player when they are on the ground. The player also has a rigidbody components, and I’m using physics and the right controller to allow them to thrust up, and move when they are in the air (right grip is thrust up, right control stick thrust them directionally forward, backward, left or right). This all works really well, and I based it on this tutorial:

So, my setup is:

I have created an XR Origin. On this, I have my Locomotion and Continuous Movement Provider. I also have a capsule collider and rigidbody. And, of course, I have my jetpack script.

I determine thrust direction basesd on the x,y input of the right thumbstick:

thrustDirection = Camera.main.transform.forward * secondaryThrustInput.y + Camera.main.transform.right * secondaryThrustInput.x;

And I apply that thrust with an AddRelativeForce().

This works great. If I turn my head and thrust forward, I thrust where I’m looking. If I turn myself physically while I’m flying, I can change the way I’m going, and thrust directions all work well.

The problem arises when I’ve tried adding a function to rotate the player using the jetpack and right controller. The idea is, I want the player to not have to turn their feet to rotate. It makes sense if they use their feet to rotate themselves when they are on the ground, but when they are flying, I want them to be able to use the jetpack thrust to rotate themselves.

I’ve been able to rotate using my own script, or using the Continuous Turn Provider. But, whenever I rotate, my thrust direction no longer works as expected (even though Continuous Movement does). So, after rotating, if I thrust forward, I’m not going forward but some other direction. And, if I rotate again, it will go in another different direction. I’ve tried various solutions to this… I’ve tried Rotate, RotateAround, I’ve tried creating a parent object for XR Origin and rotating that. They all lead to the same result.

I’m not fully grasping some aspect of either my directional thrusts, or the rotate systems. Can anyone provide some insight on a solution to this? I would like to keep the rigidbody and physics forces if at all possible (for thrusting up and thrusting directionally, not for rotation). I do have rotations frozen on my rigidbody but have also experimented with unlocking Y rotation, to no avail.

I’ve tried rotation calculations as such:

_xrOrigin.transform.parent.RotateAround(new Vector3(Camera.main.transform.position.x, 0, Camera.main.transform.position.z), Vector3.up, secondaryThrustInput.x * 50  * Time.deltaTime);

Quaternion rotationDelta = Quaternion.Euler(0, secondaryThrustInput.x * 50 * Time.deltaTime, 0);
_xrOrigin.transform.rotation *= rotationDelta;

Video demonstration of issue (shows head turn and thrust direction working, but then 90 degree rotate and thrust goes in wrong direction): https://cdn.discordapp.com/attachments/1099009674017243187/1099016251226980482/Unity_2023-04-21_12-57-20.mp4

TY!

Hi! Sounds like a fun project.
I can’t say for certain, but my guess is that you should try using Rigidbody AddForce (Unity - Scripting API: Rigidbody.AddForce) instead of AddRelativeForce().

My thinking is:
Lets say you have rotated the origin 90 degrees to the right so it’s forward direction is now pointing along the world X axis.
Your body/head has not rotated so now your camera is looking along the world X axis, the thrust direction would be (1,0,0) and you expect to move in that direction. When you apply that force to the rotated origin’s rigidbody as a relative force, it will move along its local x axis (which is now the world -z), instead of the world x.

Another option if you want to keep using AddRelativeForce would be to convert the thrust direction into the XR orign’s relative coordinate frame by adding

thrustDirection = _xrOrigin.transform.InverseTransformDirection(thrustDirection);

Let me know if this doesn’t work, in which case I might need to see more code to see what’s up exactly.

1 Like

Aha! Thank you! That did the trick! I always confuse myself on these 3d vectors, I will study up on them a bit more. Thanks you for the tips AND the explanation!

So, as mentioned, this works. But I was wondering if you could help me understand the reasoning a bit more? You mention above that when I rotate the origin the body/head doesn’t move. But in the game it seems like I am rotating? My view rotates and I’m facing a new direction. And, if I watch scene view I see the camera rotating. So I’m not clear on what is rotating and what is not rotating. What can I look for in the scene view or inspector to understand better what is happening? I’m still confused on this as it seems intuitively that addRelariveForce should be the correct option since it applies force on a local axis - so it seems to me after I rotate an object I should apply force in the local forward direction. I’m glad your solution works! I’d just like to understand why better so I can really learn from this. TY!

Glad that worked, and yeah I can try to explain it a bit more.

When I said “Your body/head has not rotated” I meant that your head in real life does not move. In the game it seems you are rotating, but your VR headset has not rotated in the real world, which is what will control the Camera’s relative rotation as a child of the origin.

In the inspector, you can look at the Transform values of the Main Camera, and of the XR Origin. The value you see in the inspector is relative to the parent(s).

The main camera has the component Tracked Pose Driver which is directly getting the value from the headset’s rotation relative to your real life room/tracking space.
The XR Origin is the one that you move and rotate to move the player around the virtual world via locomotion.

So when you get the “camera forward” direction, it returns the world rotation which is the final/total rotation of camera: the headset’s rotation combined with the virtual rotation.
If you apply that as a relative force, then it is takes that value and applies the rotation of the XR Origin to make it relative. However that rotation is already taken into account by the “camera.forward”. So in effect, you end up double-counting the rotation of the xr origin. When you switch to the non-relative, it tells the rigidbody that the direction does not need to account for the rotation of the xr origin, because it is included already.

1 Like