Skip to content

Instantly share code, notes, and snippets.

@blevok
Forked from kormyen/GyroCamera.cs
Last active October 2, 2021 06:27
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blevok/0b5f4c821f019dd1d74fce98c9de3d5f to your computer and use it in GitHub Desktop.
Save blevok/0b5f4c821f019dd1d74fce98c9de3d5f to your computer and use it in GitHub Desktop.
Gyro camera rotation for Android, tested in Unity 2019.3.2 for API level 28, in landscape left orientation.
// Gyro camera rotation for Android, tested in Unity 2019.3.2 for API level 28, in landscape left orientation.
// Extended to include X and Z axis when claibrating, so you can re-center when looking in any direction, and at a variable speed.
// Attach this script to the parent GameObject of your camera. To re-center, from your main script, call CalibrateAngle, set a bool "letsGoToCenter" to true, a bool "goToCenterSlowly" to true or false, and a string "centerRotSpeed" to slow or fast.
// centerRotSpeed is ignored when goToCenterSlowly is false. You can use "Unity Remote 5" in the Play store to test Android gyro in Unity editor.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GyroCamera : MonoBehaviour
{
// your main script where the rest of your app functions live
public mainAppScript mainappscript;
// STATE
private float _initialXAngle = 0f;
private float _appliedGyroXAngle = 0f;
private float _calibrationXAngle = 0f;
private float _initialYAngle = 0f;
private float _appliedGyroYAngle = 0f;
private float _calibrationYAngle = 0f;
private float _initialZAngle = 0f;
private float _appliedGyroZAngle = 0f;
private float _calibrationZAngle = 0f;
public Transform _rawGyroRotation;
Quaternion currentOffset = Quaternion.Euler(0f, 0f, 0f);
Quaternion currentAngle;
Quaternion newAngle;
float timeToLerp = 1f;
float timeLerped = 0.0f;
private float _tempSmoothing;
// SETTINGS
[SerializeField] private float _smoothing = 0.1f;
private IEnumerator Start()
{
Input.gyro.enabled = true;
_initialXAngle = transform.eulerAngles.x;
_initialYAngle = transform.eulerAngles.y;
_initialZAngle = transform.eulerAngles.z;
_rawGyroRotation = new GameObject("GyroRaw").transform;
_rawGyroRotation.position = transform.position;
_rawGyroRotation.rotation = transform.rotation;
// Wait until gyro is active, then calibrate to reset starting rotation.
yield return new WaitForSeconds(1);
StartCoroutine(CalibrateAngle());
}
private void Update()
{
ApplyGyroRotation();
ApplyCalibration();
// stop moving towards center if x and y are within a certain range of center
if ((transform.rotation.eulerAngles.x >= -1 && transform.rotation.eulerAngles.x <= 1) && (transform.rotation.eulerAngles.y >= -1 && transform.rotation.eulerAngles.y <= 1))
{
mainappscript.letsGoToCenter = false;
}
if (mainappscript.letsGoToCenter == true)
{
if (mainappscript.goToCenterSlowly == true)
{
if (mainappscript.centerRotSpeed == "slow")
{
timeToLerp = 1f;
timeLerped += Time.deltaTime;
newAngle = Quaternion.Euler(0, 0, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, newAngle, (timeLerped / timeToLerp) / 2);
}
else if (mainappscript.centerRotSpeed == "fast")
{
timeToLerp = 0.5f;
timeLerped += Time.deltaTime;
newAngle = Quaternion.Euler(0, 0, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, newAngle, (timeLerped / timeToLerp) / 2);
}
}
else
{
newAngle = Quaternion.Euler(0, 0, 0);
transform.rotation = Quaternion.Slerp(currentAngle, newAngle, 1f);
}
}
else
{
newAngle = _rawGyroRotation.rotation * Quaternion.Inverse(currentOffset);
transform.rotation = Quaternion.Slerp(transform.rotation, newAngle, _smoothing);
}
}
public IEnumerator CalibrateAngle()
{
_tempSmoothing = _smoothing;
_smoothing = 1;
// Offsets the angle in case it wasn't 0 at edit time.
_calibrationXAngle = _appliedGyroXAngle - _initialXAngle;
_calibrationYAngle = _appliedGyroYAngle - _initialYAngle;
_calibrationZAngle = _appliedGyroZAngle - _initialZAngle;
yield return null;
_smoothing = _tempSmoothing;
currentOffset = _rawGyroRotation.rotation;
currentAngle = transform.rotation;
timeLerped = 0.0f;
}
private void ApplyGyroRotation()
{
_rawGyroRotation.rotation = Input.gyro.attitude;
_rawGyroRotation.Rotate(0f, 0f, 180f, Space.Self); // Swap "handedness" of quaternion from gyro.
_rawGyroRotation.Rotate(90f, 180f, 0f, Space.World); // Rotate to make sense as a camera pointing out the back of your device.
// Save the angle around axis for use in calibration.
_appliedGyroXAngle = _rawGyroRotation.eulerAngles.x;
_appliedGyroYAngle = _rawGyroRotation.eulerAngles.y;
_appliedGyroZAngle = _rawGyroRotation.eulerAngles.z;
}
private void ApplyCalibration()
{
// Rotates back however much it deviated when calibrationAngle was saved.
_rawGyroRotation.Rotate(_calibrationXAngle, -_calibrationYAngle, _calibrationZAngle, Space.World);
}
}
@jiayi99
Copy link

jiayi99 commented Sep 29, 2021

Hi is there a way to lock Y axis rotation

@blevok
Copy link
Author

blevok commented Oct 1, 2021

Hi is there a way to lock Y axis rotation

It's been a while since i messed with this or any rotations, but try replacing line 93 with this:
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(newAngle.eulerAngles.x, 0f, newAngle.eulerAngles.z), _smoothing);

It might not be that simple though. You might need to also set the y values for the calibrated and applied angles, and maybe for the input value as well. Doing it without setting every place where the y value could change could result in stuttering.

Rotations are just really confusing to me, so i can't say exactly what will happen unless i actually try it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment