Part of these game systems:
intermediate Touch & Mobile

Gyroscope Camera Controller

Controls camera rotation using the device gyroscope sensor with smoothing and sensitivity settings. Includes calibration, axis locking, and fallback for devices without a gyroscope.

Unity 2022.3+ · 3.5 KB · GyroscopeCameraController.cs

How to Use

1

Attach the GyroscopeCameraController script to your Main Camera

2

Adjust sensitivity and smoothSpeed in the inspector

3

Lock any axes you do not want the gyroscope to control

4

Enable useTouchFallback so the camera still works in the editor or on devices without a gyroscope

5

Call Calibrate() at runtime to reset the neutral orientation

Source Code

GyroscopeCameraController.cs
C#
using UnityEngine;

public class GyroscopeCameraController : MonoBehaviour
{
    [Header("Gyroscope Settings")]
    [SerializeField] private float sensitivity = 1f;
    [SerializeField] private float smoothSpeed = 5f;

    [Header("Axis Locking")]
    [SerializeField] private bool lockX;
    [SerializeField] private bool lockY;
    [SerializeField] private bool lockZ;

    [Header("Fallback")]
    [SerializeField] private bool useTouchFallback = true;
    [SerializeField] private float touchSensitivity = 0.2f;

    public bool GyroAvailable { get; private set; }
    public bool IsCalibrated { get; private set; }

    private Gyroscope gyro;
    private Quaternion calibrationOffset = Quaternion.identity;
    private Quaternion targetRotation;
    private Quaternion initialRotation;

    // Conversion from gyro space to Unity space
    private static readonly Quaternion gyroFix = new Quaternion(0f, 0f, 1f, 0f);

    private Vector2 touchDelta;
    private float fallbackYaw;
    private float fallbackPitch;

    private void Start()
    {
        initialRotation = transform.rotation;

        if (SystemInfo.supportsGyroscope)
        {
            gyro = Input.gyro;
            gyro.enabled = true;
            GyroAvailable = true;
            Calibrate();
        }
        else
        {
            GyroAvailable = false;
            Debug.Log("Gyroscope not available. Using touch fallback.");
        }
    }

    private void Update()
    {
        if (GyroAvailable)
        {
            UpdateGyro();
        }
        else if (useTouchFallback)
        {
            UpdateTouchFallback();
        }
    }

    private void UpdateGyro()
    {
        Quaternion rawGyro = gyro.attitude;
        Quaternion convertedRotation = ConvertGyroRotation(rawGyro);
        Quaternion calibrated = calibrationOffset * convertedRotation;

        targetRotation = calibrated;
        ApplyRotation(targetRotation);
    }

    private Quaternion ConvertGyroRotation(Quaternion gyroAttitude)
    {
        return Quaternion.Euler(90f, 0f, 0f) * new Quaternion(
            gyroAttitude.x, gyroAttitude.y,
            -gyroAttitude.z, -gyroAttitude.w);
    }

    private void UpdateTouchFallback()
    {
        if (Input.touchCount == 1)
        {
            Touch touch = Input.GetTouch(0);
            if (touch.phase == TouchPhase.Moved)
            {
                touchDelta = touch.deltaPosition;
                fallbackYaw += touchDelta.x * touchSensitivity;
                fallbackPitch -= touchDelta.y * touchSensitivity;
                fallbackPitch = Mathf.Clamp(fallbackPitch, -80f, 80f);
            }
        }

        // Mouse fallback for editor
        if (Input.GetMouseButton(1))
        {
            fallbackYaw += Input.GetAxis("Mouse X") * touchSensitivity * 10f;
            fallbackPitch -= Input.GetAxis("Mouse Y") * touchSensitivity * 10f;
            fallbackPitch = Mathf.Clamp(fallbackPitch, -80f, 80f);
        }

        targetRotation = Quaternion.Euler(fallbackPitch, fallbackYaw, 0f);
        ApplyRotation(targetRotation);
    }

    private void ApplyRotation(Quaternion rotation)
    {
        Vector3 euler = rotation.eulerAngles;

        if (lockX) euler.x = initialRotation.eulerAngles.x;
        if (lockY) euler.y = initialRotation.eulerAngles.y;
        if (lockZ) euler.z = initialRotation.eulerAngles.z;

        Quaternion final_rot = Quaternion.Euler(euler);

        if (sensitivity != 1f)
        {
            final_rot = Quaternion.Slerp(initialRotation, final_rot, sensitivity);
        }

        transform.rotation = Quaternion.Slerp(
            transform.rotation, final_rot, Time.deltaTime * smoothSpeed);
    }

    public void Calibrate()
    {
        if (!GyroAvailable) return;

        Quaternion currentGyro = ConvertGyroRotation(gyro.attitude);
        calibrationOffset = Quaternion.Inverse(currentGyro) * initialRotation;
        IsCalibrated = true;
    }

    public void SetSensitivity(float value)
    {
        sensitivity = Mathf.Clamp(value, 0.1f, 3f);
    }

    public void SetSmoothSpeed(float value)
    {
        smoothSpeed = Mathf.Clamp(value, 1f, 20f);
    }

    private void OnDisable()
    {
        if (gyro != null)
            gyro.enabled = false;
    }

    private void OnEnable()
    {
        if (gyro != null)
            gyro.enabled = true;
    }
}
Ready for more? Endless Runner Controller Complete endless runner character controller with auto-forward movement, lane switching, jump and slide mechanics.