as the title already says, i'm currently working on a small ball balancing game using the gyroscope rotation to rotate a floating platform with a ball on it.
The problem i always encounter is: i can't lock the y axis. I used euler angles and it worked perfectly, but then i have to deal with gimbal lock. Because i don't want to deal with this issue of euler, i tried using quaternions instead and it does work in some kind of way.
I can set the rotation and can define a calibration offset for the gyroscope. If i wouldn't calculate this offset, the rotation would be absolute and not relative to the ingame world space. But i always have some sort of y rotation going on, even if i set it to 0 for the quaternion.
For example: i start the game, calibrate the gyro to my rotation but after that rotate around the y axis in real world space. The platform keeps facing the camera, but so doesn't the rotation axes.
The normal behaviour and how it always should be (no rotation on y axis in real world space):
https://www.nani-games.net/share/umSc6XH1vE.gif
The actual behaviour and how it shouldn't be (rotation on y axis in real world space):
https://www.nani-games.net/share/AVicmV4fWe.gif
A list of what i already tried:
- Using euler angles
- Using a combination of euler angles and quaternions in order to make up a new quaternion with desired parameters / rotations
- Using Quaternion.AngleAxis in order to change the order of the euler axes
- Checking the box "Freeze Y Rotation" of the platform's rigidbody under constraints (this doesn't work because i'm not applying force to rotate but rather set the rotation directly)
How my scene is build up:
https://www.nani-games.net/share/Unity_fAQKW7cfPK.png
I have two scripts which are used to get the gyroscope rotation, make it useful in unity and use it to rotate other objects.
A static script called DeviceRotation. It activates the gyro and gets the rotation from it:
using UnityEngine;
static class DeviceRotation
{
public static Quaternion offsetRotation;
private static bool gyroInitialized = false;
public static bool HasGyroscope
{
get
{
return SystemInfo.supportsGyroscope;
}
}
public static Quaternion Get()
{
if (!gyroInitialized)
{
InitGyro();
}
return HasGyroscope
? ReadGyroscopeRotation()
: Quaternion.identity;
}
private static void InitGyro()
{
if (HasGyroscope)
{
Input.gyro.enabled = true;
Input.gyro.updateInterval = 0.0167f; // 60Hz
}
gyroInitialized = true;
}
private static Quaternion ReadGyroscopeRotation()
{
return new Quaternion(0.5f, 0.5f, -0.5f, 0.5f) * Input.gyro.attitude * new Quaternion(0, 0, 1, 0);
}
public static void calibrateCoords()
{
Quaternion deviceRotation = Get();
offsetRotation = deviceRotation;
}
}
The second script which is appended to the platform object. It gets a calibration quaternion, calculates a new quaternion out of the calibration quaternion and a constantly updating quaternion (both taken from the gyroscopes rotation) and then sets the platforms transform to this new rotation:
using UnityEngine;
using UnityEngine.UI;
public class Gyroscope : MonoBehaviour
{
public Button resetSphere;
public Button calibrate;
public GameObject sphere;
public Text platformCoords;
public Text gyroOffsetCoords;
void Start()
{
DeviceRotation.calibrateCoords();
calibrate.onClick.AddListener(DeviceRotation.calibrateCoords);
setGyroRotation();
resetSphere.onClick.AddListener(ResetSphere);
}
void Update()
{
setGyroRotation();
Vector3 offsetRotationEuler = DeviceRotation.offsetRotation.eulerAngles;
gyroOffsetCoords.text = "GyroOffset:
" +
"X: " + offsetRotationEuler.x + "
" +
"Y: " + offsetRotationEuler.y + "
" +
"Z: " + offsetRotationEuler.z;
}
private void ResetSphere()
{
GameObject temp_sphere = Instantiate(sphere);
temp_sphere.transform.position = new Vector3(0, 3, 0);
}
private void setGyroRotation()
{
Quaternion deviceRotation = DeviceRotation.Get();
Quaternion newRotation = Quaternion.Inverse(DeviceRotation.offsetRotation) * deviceRotation;
transform.localRotation = newRotation;
Vector3 platformAngles = transform.localRotation.eulerAngles;
platformCoords.text = "Platform:" + "
" +
"X: " + platformAngles.x + "
" +
"Y: " + platformAngles.y + "
" +
"Z: " + platformAngles.z;
}
}
So, to conclude everything: my problem is that i cannot lock one axis (the y axis) of a quaternion.
I also tried a combination of using euler to make the desired rotation i want and then converting it to a quaternion (that's why those two functions QuaternionToEuler and EulerToQuaternion exist in the script OffsetGyro), but unfortunately this caused gimbal lock (what a surprise i guess).
I really need help at this quaternion problem, i'm stuck with it since several days.