I'm working on a mobile game in Unity and using the gyroscope to rotate an object along the x and y axes.

float x = Input.gyro.attitude.x; 
float y = Input.gyro.attitude.y;

This code give me the rotation I want when pointed in a certain direction along the z-axis. I want the rotation to not depend on the z-axis. I want the rotation to act the same no matter what direction I'm pointed in the real-world.

Here is a 12 second clip of what I get using a mobile gyroscope when pointed at 1 or North on the z-axis:

Animation of oven mitt holding pan tilting left/right/forward/back

I want the rotation to be the same no matter what direction I am pointed on the z-axis with the gyroscope. Is there some way to rotate the whole coordinate system?

  • \$\begingroup\$ You're going to have trouble. Rotation in 3D space can only be accurately represented as a Quaternion (W,X,Y,Z). Euler angles (what the Input.gyro gives you) suffer from Gimbal Lock. Restricting yourself down to two axis is just going to not be possible. \$\endgroup\$ – Draco18s no longer trusts SE Nov 7 '19 at 16:18
  • \$\begingroup\$ @Draco18s - the particular interaction they want can definitely be done with two axes. They don't need to express every possible orientation, just a small subset. \$\endgroup\$ – DMGregory Nov 7 '19 at 17:39

Input.gyro.attitude gives you a quaternion representing (a best estimate of) the device's absolute orientation in space - including its orientation relative to the north pole.

But that's not what you want here. You want a measure of how the device is tilted relative to the gravity vector - no matter how we're oriented to north/south/east/west.

// Get the vector representing global up (away from gravity)
// within the device's coordinate system.
Vector3 localDown = Quaternion.Inverse(Input.gyro.attitude) * Vector3.down;

// Extract our roll rotation - how much gravity points to our left or right.
float rollDegrees = Mathf.Asin(localDown.x) * Mathf.Rad2Deg;

// Extract our pitch rotation - how much gravity points forward or back.
float pitchDegrees = Mathf.Atan2(localDown.y, localDown.z) * Mathf.Rad2Deg;

This gives 0 pitch when the gravity vector points along the phone's z+ axis. If you want your neutral pitch to sit somewhere else, or adapt based on the player's resting pose when holding the phone, you can shift & wrap the angle accordingly using Mathf.DeltaAngle

  • \$\begingroup\$ Pitching the phone forwards and backwards in any direction appears to produce the correct results, but rolling from left to right changes both the roll and pitch. How would I fix this? Also wouldn't it be more efficient to use Input.gyro.gravity? @DMGregory \$\endgroup\$ – airvine Nov 8 '19 at 3:07
  • \$\begingroup\$ You can use the same Asin technique for both if you like — it'll just give you a somewhat narrower range of pitch and you may need to manually adjust it when playing mostly-horizontal vs mostly-vertical. Re: gravity, feel free to use it if it works for you. \$\endgroup\$ – DMGregory Nov 8 '19 at 3:10
  • \$\begingroup\$ +1 That worked, thank you so much! I'd upvote your answer, but my reputation is too low. \$\endgroup\$ – airvine Nov 8 '19 at 3:24

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.