Part of these game systems:
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.
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
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;
}
}