针对不同的 "flat" 位置校准陀螺仪,然后提取 x 和 y 倾斜
Calibrating a gyroscope for different "flat" positions, then extracting x and y tilt
我有一个自上而下的 2D 球类游戏,就像旧的 Labyrinth 球类游戏一样。我的游戏是在手机上玩的 phone。用户通过倾斜 phone 来控制它。这个想法是,当您将屏幕倾斜设置时,球会真实地滚动到倾斜的“底部”。例如,如果 phone 平放在 table 上,球不应该飞向任何地方。但如果我将设备向上倾斜,就像翻书一样,那么球应该会落到屏幕底部。使用加速度计很容易做到这一点,但是...
我的问题是如何校准陀螺仪,以便可以从设备的任何起始“平面”3d 旋转轻松玩游戏。我希望用户能够“校准”游戏,以便无论他们想如何握住设备,他们都可以正确控制球。我在想:
如果 phone 平放在 table 上,那么倾斜它会“正常”工作,如第一段所述。
如果phone是倒过来的,那么应该是#1的反面。这适用于那些想要在床上将 phone 举过头顶之类的人。
如果 phone 与墙壁平行(如壁挂式电视),那么它应该就像墙壁是平的 table,如果说得通的话。
目前我的代码是:
public void OnHitCalibrate() {
_offset = Input.gyro.attitude;
}
public void Update() {
if(!_hasGyro) return;
Quaternion reading = Input.gyro.attitude;
reading *= Quaternion.Inverse(_offset); //subtract the offset
_gyro.rotation = reading;
}
这得到了我想要的输入类型,但我不明白 Input.gyro.attitude
返回的四元数轴如何帮助我找到我的 2d 倾斜值。例如,当设备指向北方,并且姿态与Quaternion.Identity
相同时,值似乎发生了合理的变化——当我将attitude
变成欧拉角时,x值发生了变化对应于我的 y 倾斜,y 值的变化对应于我的 x 倾斜的变化。但是当我旋转时,一切都变了,当我将 phone 旋转得更平行于墙壁时,数字似乎与欧拉角中的轴没有直接相关,即使我使用 OnHitCalibrate
函数并减去偏移量。有什么方法可以解释 attitude
以便我始终可以知道设备正在转向哪个方向?
我的最终目标是将 attitude
提炼成表示力的 Vector2——其中 x 和 y 都在 -1 和 1 之间,其中 1 表示向上或向右的最大力,-1 是向下或向左的最大力,0 表示球的力没有变化。
更新:已解决!我附上了处理陀螺仪偏移量的 class。希望对您有所帮助!请注意,它是为 Unity C# 编码的,并且已经包含了一些我的自定义 classes。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum TiltModule { GYROSCOPE, ACCELEROMETER, KEYBOARD_ONLY, NONE }
public class CalibratedGyroscope : SingletonKTBehavior<CalibratedGyroscope>
{
public bool _supported;
private Quaternion _off;
private Vector3 _offEuler;
int _activeSemaphore = 0;
private float _degreesForFullTilt = 10;
public Vector2 _lastTilt;
public void Init() {
_off = Quaternion.identity;
_supported = SystemInfo.supportsGyroscope;
}
public bool Activate(bool isActivated) {
if(isActivated) _activeSemaphore++;
else _activeSemaphore--;
_activeSemaphore = Mathf.Max(_activeSemaphore, 0);
if(_activeSemaphore > 0) {
if(_supported) {
Input.gyro.enabled = true;
} else {
return false; //everything not ok; you requested gyro but can't have it!
}
} else {
if(_supported) {
Input.gyro.enabled = false;
}
}
return true; //everything ok;
}
public void Deactivate() {
_activeSemaphore = 0;
}
public void SetCurrentReadingAsFlat() {
_off = Input.gyro.attitude;
_offEuler = _off.eulerAngles;
}
public Vector3 GetReading() {
if(_supported) {
return (Quaternion.Inverse(_off) * Input.gyro.attitude).eulerAngles;
} else {
Debug.LogError("Tried to get gyroscope reading on a device which didn't have one.");
return Vector3.zero;
}
}
public Vector2 Get2DTilt() {
Vector3 reading = GetReading();
Vector2 tilt = new Vector2(
-Mathf.DeltaAngle(reading.y, 0),
Mathf.DeltaAngle(reading.x, 0)
);
//can't go over max
tilt.x = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.x) * 2 - 1;
tilt.y = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.y) * 2 - 1;
//get phase
tilt.x = Mathf.Clamp(tilt.x, -1, 1);
tilt.y = Mathf.Clamp(tilt.y, -1, 1);
_lastTilt = tilt;
return tilt;
}
public string GetExplanation() {
Vector3 reading = GetReading();
string msg = "";
msg += "OFF: " + _offEuler + "\n";
Vector2 tilt = new Vector2(
-Mathf.DeltaAngle(reading.y, 0),
Mathf.DeltaAngle(reading.x, 0)
);
msg += "DELTA: " + tilt + "\n";
//can't go over max
tilt.x = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.x) * 2 - 1;
tilt.y = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.y) * 2 - 1;
msg += "LERPED: " + tilt + "\n";
//get phase
tilt.x = Mathf.Clamp(tilt.x, -1, 1);
tilt.y = Mathf.Clamp(tilt.y, -1, 1);
msg += "CLAMPED: " + tilt + "\n";
return msg;
}
public void SetDegreesForFullTilt(float degrees) {
_degreesForFullTilt = degrees;
}
}
我有一个自上而下的 2D 球类游戏,就像旧的 Labyrinth 球类游戏一样。我的游戏是在手机上玩的 phone。用户通过倾斜 phone 来控制它。这个想法是,当您将屏幕倾斜设置时,球会真实地滚动到倾斜的“底部”。例如,如果 phone 平放在 table 上,球不应该飞向任何地方。但如果我将设备向上倾斜,就像翻书一样,那么球应该会落到屏幕底部。使用加速度计很容易做到这一点,但是...
我的问题是如何校准陀螺仪,以便可以从设备的任何起始“平面”3d 旋转轻松玩游戏。我希望用户能够“校准”游戏,以便无论他们想如何握住设备,他们都可以正确控制球。我在想:
如果 phone 平放在 table 上,那么倾斜它会“正常”工作,如第一段所述。
如果phone是倒过来的,那么应该是#1的反面。这适用于那些想要在床上将 phone 举过头顶之类的人。
如果 phone 与墙壁平行(如壁挂式电视),那么它应该就像墙壁是平的 table,如果说得通的话。
目前我的代码是:
public void OnHitCalibrate() {
_offset = Input.gyro.attitude;
}
public void Update() {
if(!_hasGyro) return;
Quaternion reading = Input.gyro.attitude;
reading *= Quaternion.Inverse(_offset); //subtract the offset
_gyro.rotation = reading;
}
这得到了我想要的输入类型,但我不明白 Input.gyro.attitude
返回的四元数轴如何帮助我找到我的 2d 倾斜值。例如,当设备指向北方,并且姿态与Quaternion.Identity
相同时,值似乎发生了合理的变化——当我将attitude
变成欧拉角时,x值发生了变化对应于我的 y 倾斜,y 值的变化对应于我的 x 倾斜的变化。但是当我旋转时,一切都变了,当我将 phone 旋转得更平行于墙壁时,数字似乎与欧拉角中的轴没有直接相关,即使我使用 OnHitCalibrate
函数并减去偏移量。有什么方法可以解释 attitude
以便我始终可以知道设备正在转向哪个方向?
我的最终目标是将 attitude
提炼成表示力的 Vector2——其中 x 和 y 都在 -1 和 1 之间,其中 1 表示向上或向右的最大力,-1 是向下或向左的最大力,0 表示球的力没有变化。
更新:已解决!我附上了处理陀螺仪偏移量的 class。希望对您有所帮助!请注意,它是为 Unity C# 编码的,并且已经包含了一些我的自定义 classes。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum TiltModule { GYROSCOPE, ACCELEROMETER, KEYBOARD_ONLY, NONE }
public class CalibratedGyroscope : SingletonKTBehavior<CalibratedGyroscope>
{
public bool _supported;
private Quaternion _off;
private Vector3 _offEuler;
int _activeSemaphore = 0;
private float _degreesForFullTilt = 10;
public Vector2 _lastTilt;
public void Init() {
_off = Quaternion.identity;
_supported = SystemInfo.supportsGyroscope;
}
public bool Activate(bool isActivated) {
if(isActivated) _activeSemaphore++;
else _activeSemaphore--;
_activeSemaphore = Mathf.Max(_activeSemaphore, 0);
if(_activeSemaphore > 0) {
if(_supported) {
Input.gyro.enabled = true;
} else {
return false; //everything not ok; you requested gyro but can't have it!
}
} else {
if(_supported) {
Input.gyro.enabled = false;
}
}
return true; //everything ok;
}
public void Deactivate() {
_activeSemaphore = 0;
}
public void SetCurrentReadingAsFlat() {
_off = Input.gyro.attitude;
_offEuler = _off.eulerAngles;
}
public Vector3 GetReading() {
if(_supported) {
return (Quaternion.Inverse(_off) * Input.gyro.attitude).eulerAngles;
} else {
Debug.LogError("Tried to get gyroscope reading on a device which didn't have one.");
return Vector3.zero;
}
}
public Vector2 Get2DTilt() {
Vector3 reading = GetReading();
Vector2 tilt = new Vector2(
-Mathf.DeltaAngle(reading.y, 0),
Mathf.DeltaAngle(reading.x, 0)
);
//can't go over max
tilt.x = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.x) * 2 - 1;
tilt.y = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.y) * 2 - 1;
//get phase
tilt.x = Mathf.Clamp(tilt.x, -1, 1);
tilt.y = Mathf.Clamp(tilt.y, -1, 1);
_lastTilt = tilt;
return tilt;
}
public string GetExplanation() {
Vector3 reading = GetReading();
string msg = "";
msg += "OFF: " + _offEuler + "\n";
Vector2 tilt = new Vector2(
-Mathf.DeltaAngle(reading.y, 0),
Mathf.DeltaAngle(reading.x, 0)
);
msg += "DELTA: " + tilt + "\n";
//can't go over max
tilt.x = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.x) * 2 - 1;
tilt.y = Mathf.InverseLerp( -_degreesForFullTilt, _degreesForFullTilt, tilt.y) * 2 - 1;
msg += "LERPED: " + tilt + "\n";
//get phase
tilt.x = Mathf.Clamp(tilt.x, -1, 1);
tilt.y = Mathf.Clamp(tilt.y, -1, 1);
msg += "CLAMPED: " + tilt + "\n";
return msg;
}
public void SetDegreesForFullTilt(float degrees) {
_degreesForFullTilt = degrees;
}
}