Finding physical world x,y,z with respect to an iOS device camera
[This is a problem and solution.]
Currently, if you Google "unity ios attitude" you'll find considerable confusion about how to understand an iOS device's attitude (i.e. orientation) in physical space, in a way that can be used in Unity. You'll also find some scripts that purport to be solutions to variants of this problem. It seems that some work, based on comments on those pages.
My specific need was to know the vectors for up, forward, and right within the physical world I'm in, given with respect to the iOS device camera.
The obvious solution fails
One obvious thing to try is to define a game object and set its transform.rotation as follows:
transform.rotation = Input.gyro.attitude;
With this strategy, transform.up, transform.right, and transform.forward would seemingly give the vectors in Unity-world space that correspond to the iOS device's attitude in physical-world space. I recommend testing this direct assignment of rotation on a Unity camera (put it in the a script's Update() function on that camera object). You should see that 2 of 3 rotation axes move in the opposite direction than what is needed to have the Unity camera mimic the phone's camera. As you'll see below, multiplying the corresponding Euler angles by -1 is only part of the solution.
After I studied quaternions (more interesting than the mysterious headache they seemed to be), tried almost exhaustively searching parameters for different manipulations on quaternions and Euler angles, and conducted an embarrassing amount of unrigorous trial and error, I now have the following solution.
Solution for finding vectors describing physical world orientation from iOS device's perspective
(1) Create an empty game object and name it "PhoneDummy". On PhoneDummy, create a script with the following update code.
void Update () {
Quaternion deviceRotation = Input.gyro.attitude;
transform.eulerAngles = new Vector3 (
-1 * deviceRotation.eulerAngles.x,
-1 * deviceRotation.eulerAngles.y,
deviceRotation.eulerAngles.z);
}
If this game object was a camera (which it doesn't need to be), you would see that it changes its rotation correctly with respect to fixed objects, but it is rotated 90 degrees along the forward axis. The next part accounts for that remaining error while computing vectors for the physical world's orientation.
(2) The orientation of the physical world, within the local space of the iOS device camera, can then be found by
Vector3 upVec = phoneDummy.transform.InverseTransformDirection( -1f * Vector3.forward);
Vector3 rightVec = phoneDummy.transform.InverseTransformDirection( 1f * Vector3.up);
Vector3 forwardVec = phoneDummy.transform.InverseTransformDirection( -1f * Vector3.right);
Note that the local space of the Unity camera isn't actually used to calculate these.
Also, just to be clear, at some point prior phoneDummy would have been assigned the return value of
GameObject.Find ("PhoneDummy");
These vectors can be used to move objects so that they maintain the same rotation with respect to the physical world. To see an example—and in doing so test this code on your iOS device—create an object with directionality (e.g., a tree). Set that object's parent as the Unity camera, move the object's position to be centered in the camera, and attach the following script to the object:
using UnityEngine;
using System.Collections;
public class KeepRotInPhysWorld : MonoBehaviour {
GameObject phoneDummy;
void Start () {
phoneDummy = GameObject.Find ("PhoneDummy");
}
void Update () {
/**
* Use phoneDummy rotation to find actual direction vectors relative to the phone
*/
Vector3 upVec = phoneDummy.transform.InverseTransformDirection(-1f * Vector3.forward);
Vector3 rightVec = phoneDummy.transform.InverseTransformDirection(1f * Vector3.up);
Vector3 forwardVec = phoneDummy.transform.InverseTransformDirection(-1f * Vector3.right);
transform.LookAt (transform.position + forwardVec, upVec);
}
}
When you run this on your iOS device, you should see the 3D object maintain its rotation with respect to the physical world.
An alternative
If you specifically want to control the Unity camera to mimic the phone camera—so that its rotation in world space is the same as the iOS device's camera's rotation in physical space—check out the script here, which worked well for me in brief testing: https://forum.unity3d.com/threads/sharing-gyroscope-camera-script-ios-tested.241825/. You may want to test it with a scene with visual variety, even if that's just a rectangular-prism room with different colors for each wall.
How Unity's internal team could save us from this mess
Both solutions are messy and quite difficult to wrap your head around. It's deeply unfortunate that Unity doesn't redefine Input.gyro.attitude such that setting a Unity camera's rotation by
transform.rotation = Input.gyro.attitude;
makes the Unity camera mimic the phone camera. The current output of Input.gyro.attitude could still be available as Input.gyro.rawAttitude for the crazy folks that want to use that.
This same solution is working for Android, except that you need to enable the gyro before querying it for attitude. A general solution for iOS and Android is below, as an expansion of the "PhoneDummy" object's script. (Everything else written above stays the same.)
using UnityEngine;
using System.Collections;
public class $$anonymous$$imicPhoneCam : $$anonymous$$onoBehaviour {
private Gyroscope gyro;
private bool gyroEnabled = false;
void Start () {
gyroEnabled = EnableGyro();
//Debug.Log ("Gyro enabled: " + gyroEnabled);
}
private bool EnableGyro() {
if (SystemInfo.supportsGyroscope) {
gyro = Input.gyro;
gyro.enabled = true;
return true;
}
return false;
}
void Update () {
if (gyroEnabled) {
Quaternion deviceRotation = gyro.attitude;
transform.eulerAngles = new Vector3(
-1 * deviceRotation.eulerAngles.x,
-1 * deviceRotation.eulerAngles.y,
deviceRotation.eulerAngles.z);
}
}
}
Answer by Romaleks360 · Jun 15, 2019 at 06:42 PM
Isn't it written in the manual?
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
https://docs.unity3d.com/ScriptReference/Gyroscope.html
But yeah it's still anti-intuitive and useless
Well, the orientation of a device in the real world is something completely different from the virtual world inside Unity. In mathematics we usually use a right handed system and that's also used by the gyro hardware of your device. Unity uses a left handed coordinate system in the virtual world. GyroToUnity does this transformation. $$anonymous$$eep in $$anonymous$$d that there's not only one valid way how to translate the real world orientation into Unity's virtual space.
Answer by HanSoloYolo · Jul 23, 2017 at 09:25 PM
Hello,
I am having the same issue, but am developing for Android. How would I modify the following Script to allow me to transform.localRotation of GyroControl (My parent game object) to be offset the current gyro.attitude. Allowing the user to look up and reset the scene to their current Z axis or direction they are facing. I would like to use a if (Input.touchCount == 2)
() to recenter the original scene perspective on the current gyro.attitude.
using UnityEngine;
public class GyroController : MonoBehaviour
{
private bool gyroEnabled;
private Gyroscope gyro;
private GameObject GyroControl;
private Quaternion rot;
private void Start ()
{
GyroControl = new GameObject ("Gyro Control");
GyroControl.transform.position = transform.position;
transform.SetParent (GyroControl.transform);
gyroEnabled = EnableGyro();
}
private bool EnableGyro()
{
if (SystemInfo.supportsGyroscope)
{
gyro = Input.gyro;
gyro.enabled = true;
GyroControl.transform.rotation = Quaternion.Euler(90f, -90f, 0f);
rot = new Quaternion(0, 0, 1, 0);
return true;
}
return false;
}
private void Update ()
{
if (gyroEnabled)
{
transform.localRotation = gyro.attitude * rot;
}
}
}
Your answer
Follow this Question
Related Questions
Get single axis from gyroscopic input (iOS) 0 Answers
IOS gyro trick ? 0 Answers
Ignore gyroscope yaw 0 Answers
How to make GameObject follow phone's gyro? 0 Answers
Gyroscope Controls for FPS 0 Answers