Search code examples
c#unity-game-enginequaternionsgyroscope

How to enable gyroscope camera at current device orientation


I would like to enable gyro controlled camera onButtonClick event but I want it to start at the camera's current position. Currently when the gyro gets enabled it moves the camera off to a new position (probably the devices current gyro rotation) rather than leaving it where it is and gyro-ing from that point.

Hope I'm making sense, but basically I don't want the user to notice any change in what they are seeing in the game (ie. camera controlled by gyro but not that user would notice that change). Here's the code I'm using:

void Update () 
{
    Quaternion attitudeFix = new Quaternion (-gyro.attitude.x, -gyro.attitude.z, -gyro.attitude.y, gyro.attitude.w);
    Quaternion offsetRotation =  initialGyroRotation * attitudeFix;
    rotation = initialRotation * offsetRotation;
    transform.rotation = rotation;
}

public void EnableGyro() 
{
    initialGyroRotation = Input.gyro.attitude;
    initialRotation = transform.rotation;

    Debug.Log("initialRotation: " + initialRotation.ToString());
    Debug.Log("transform.rotation: " + transform.rotation.ToString());
    Debug.Log("initialGyroRotation: " + initialGyroRotation.ToString());
}

**

EDIT: Here's a screen of exactly how I want the view to look as the user is holding their device in front of their face (portrait) AND heading north. Regardless of the orientation of the device when the app starts, this is how it should look when heading north with phone in portrait orientation (again as the user is looking through the phone).

enter image description here

EDIT 2: Tests were getting confusing so I put the code back to exactly how your solution suggests. There is still a slight problem, but it seems like this script is very close. The main problem is the screen doesn't look like the above pic when I run each test, starting the app with the device on strange angles. It really shouldn't matter what angle the device is when the app is started, it needs to look like the above screen when pointing north and portrait.

I need to do more tests, and will do so with a new/clean project.


Solution

  • You need to get the offset camera position in the Awake or Start function. In the Update function, apply that offset value to the value from the gyro sensor.

    It looks like you already know offset should be used but you are not doing that the right way. The confusing part is getting the offset which requires subtracting the current camera rotation from the gyro sensor value.

    To subtract a Quaternion multiply the Inverse not just the Quaternion:

    Quaternion = Quaternion *Quaternion.Inverse

    To add a Quaternion multiply the Quaternion:

    Quaternion = Quaternion * Quaternion.
    

    This is what your code should look like:

    Quaternion offset;
    
    void Awake()
    {
        Input.gyro.enabled = true;
    }
    
    void Start()
    {
        //Subtract Quaternion
        offset = transform.rotation * Quaternion.Inverse(GyroToUnity(Input.gyro.attitude));
    }
    
    void Update()
    {
        GyroModifyCamera();
    }
    
    void GyroModifyCamera()
    {
        //Apply offset
        transform.rotation = offset * GyroToUnity(Input.gyro.attitude);
    }
    
    private static Quaternion GyroToUnity(Quaternion q)
    {
        return new Quaternion(q.x, q.y, -q.z, -q.w);
    }