Gyroscope gives developers a wide range of exciting control variations. But when it comes to its integration and implementation of camera controller, lots of strange and magic things happen. Developers have to deal with different device orientations, initial camera positions and, of course, quaternions. I’m glad to share my experience and describe how we solved these problems at Heyworks. Hopefully, this will save efforts and nerves of those, who work with gyroscope for the first time.

In my example I want to demonstrate how to implement the following features in camera controller:

  1. Device’s screen is supposed to work as a “window” into the virtual world so, that the turning the device around user could observe it.
  2. It will support autorotation and work on every device orientation.
  3. You will be able to detach the controller from camera, change its rotation and position from the code (play camera animation for example) and then attach controller back without noticeable hitches.
  4. It will keep virtual world up axis in parallel with real world up axis and will recalibrate horizontal rotation.

The first problem we solve is different coordinate system reference types that are used in iOS devices and Unity3D: left-handed and right-handed. In order to convert quaternion from one system to another, let’s use the following function:

private static Quaternion ConvertRotation(Quaternion q)
    return new Quaternion(q.x, q.y, -q.z, -q.w);

Now we can use the following line to calculate camera rotation:

transform.rotation = ConvertRotation(Input.gyro.attitude);

Another problem to deal with is supporting different orientations (was solved internally by Unity developers in Unity 4) which is fixed by the following function for each orientation:

private Quaternion GetRotFix()
    if (Screen.orientation == ScreenOrientation.Portrait)
        return Quaternion.identity;
    if (Screen.orientation == ScreenOrientation.LandscapeLeft
    || Screen.orientation == ScreenOrientation.Landscape)
        return Quaternion.Euler(0, 0, -90);
    if (Screen.orientation == ScreenOrientation.LandscapeRight)
        return Quaternion.Euler(0, 0, 90);
    if (Screen.orientation == ScreenOrientation.PortraitUpsideDown)
        return Quaternion.Euler(0, 0, 180);
    return Quaternion.identity;

And below here is the updated function to calculate camera rotation:

transform.rotation = ConvertRotation(Input.gyro.attitude) * GetRotFix();

The controller should update camera rotation referenced to the base device rotation and reset base camera rotation in the horizontal plane. It means that if user starts the application pointing the device to the north, the game camera will not rotate to the virtual north. The camera will show the given direction. Below is the new function that takes into account camera base rotation and device base rotation.

transform.rotation = cameraBase * ( ConvertRotation(referenceRotation * Input.gyro.attitude) * GetRotFix();

Calculation of the cameraBase and referanceRotation is a bit tricky. The main two obstacles are:

  1. You must take into account the current device orientation while calculating referenceRotation
  2. You must take into account only the rotation around up axis while calculating cameraBase

The exact functions you can find in the attached example.
And the final thing, smoothing camera rotation:

transform.rotation = Quaternion.Slerp(transform.rotation, cameraBase * 
( ConvertRotation(referanceRotation * Input.gyro.attitude) * GetRotFix()), lowPassFilterFactor);

Now let’s add two functions AttachGyro and DetachGyro. The first one will enable the controller and recalculate all base rotations and the second one will disable the controller.

Finally our controller is ready. Please find the working example in the attachment. I will appreciate your comments and questions below. Hope, you will find this post useful!

Download example here

Author Alexander Dezhurko

Chief Technical Officer with Heyworks Unity Studio