如何解决使用移动设备的陀螺仪制作球平衡游戏
正如标题已经说过的,我目前正在使用陀螺仪旋转来旋转一个带有球的浮动平台,我目前正在开发一个小型球平衡游戏。 我经常遇到的问题是:我无法锁定 y 轴。我使用了欧拉角,效果很好,但后来我必须处理万向节锁。因为我不想处理欧拉这个问题,所以我尝试使用四元数,它确实以某种方式起作用。 我可以设置旋转并可以定义陀螺仪的校准偏移。如果我不计算这个偏移量,旋转将是绝对的,而不是相对于游戏中的世界空间。但我总是有某种 y 旋转正在进行,即使我将四元数设置为 0。
例如:我开始游戏,将陀螺仪校准为我的旋转,但之后在现实世界空间中围绕 y 轴旋转。平台一直面向相机,但旋转轴不是。
正常行为及其应该如何(在现实世界空间中没有在 y 轴上旋转): https://www.nani-games.net/share/umSc6XH1vE.gif
实际行为及其不应该如何(在现实世界空间中沿 y 轴旋转): https://www.nani-games.net/share/AVicmV4fWe.gif
我已经尝试过的列表:
- 使用欧拉角
- 使用欧拉角和四元数的组合来组成具有所需参数/旋转的新四元数
- 使用 Quaternion.AngleAxis 来改变欧拉轴的顺序
- 在约束下选中平台刚体的“冻结 Y 旋转”框(这不起作用,因为我没有施加力来旋转而是直接设置旋转)
我的场景是如何构建的: https://www.nani-games.net/share/Unity_fAQKW7cfPK.png
我有两个脚本用于获取陀螺仪旋转,使其在统一中有用并使用它来旋转其他对象。
一个名为 DeviceRotation 的静态脚本。它激活陀螺仪并从中获得旋转:
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,1,0);
}
public static void calibrateCoords()
{
Quaternion deviceRotation = Get();
offsetRotation = deviceRotation;
}
}
附加到平台对象的第二个脚本。它获得一个校准四元数,从校准四元数和一个不断更新的四元数(均来自陀螺仪旋转)中计算出一个新的四元数,然后将平台变换设置为这个新的旋转:
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:\n" +
"X: " + offsetRotationEuler.x + "\n" +
"Y: " + offsetRotationEuler.y + "\n" +
"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:" + "\n" +
"X: " + platformAngles.x + "\n" +
"Y: " + platformAngles.y + "\n" +
"Z: " + platformAngles.z;
}
}
所以,总结一下:我的问题是我无法锁定四元数的一个轴(y 轴)。 我还尝试了使用 euler 进行我想要的所需旋转然后将其转换为四元数的组合(这就是脚本 OffsetGyro 中存在 QuaternionToEuler 和 EulerToQuaternion 这两个函数的原因),但不幸的是这导致了万向节锁定(我猜这真是一个惊喜).
我真的需要帮助解决这个四元数问题,我已经坚持了好几天了。
解决方法
评论中的解释:
// FIELDS
// device rotation from last frame
// init with DeviceRotation.Get() where appropriate
private Quaternion prevDeviceRotation;
//...
// UPDATE LOGIC
// get the new device rotation
Quaternion newDeviceRotation = DeviceRotation.Get();
// Find difference between previous and current device rotation along previous axes
Quaternion relativeDelta = Quaternion.Inverse(prevDeviceRotation) * newDeviceRotation;
// Apply that difference to the transform's rotation to get an unfixed rotation
// for the transform
Quaternion unfixedTransformRotation = transform.rotation * relativeDelta ;
// determine the unfixed up direction after that rotation is applied.
Vector3 unfixedUp = unfixedTransformRotation * Vector3.up;
// find angle of up from world up
float angle = Vector3.Angle(Vector3.up,unfixedUp);
Vector3 axis = Vector3.Cross(Vector3.up,unfixedUp);
// if up is colinear with world up,use identity or flipped rotation
if (axis == Vector3.zero)
{
transform.rotation = Quaternion.AngleAxis(unfixedUp.y >= 0f ? 0f: 180f,Vector3.right);
}
else
{
transform.rotation = Quaternion.AngleAxis(angle,axis);
}
// update prevDeviceRotation
prevDeviceRotation = newDeviceRotation;
,
所以,我玩了一段时间,并尝试随机创建一个可能的解决方案。这是我当前的函数 setGyroRotation:
private void setGyroRotation()
{
Quaternion deviceRotation = DeviceRotation.Get();
Quaternion newRotation = Quaternion.Inverse(DeviceRotation.offsetRotation) * deviceRotation; // Calculating "initial" rotation
axisObject.transform.rotation = newRotation; // Setting the axis' rotation to our "initial" rotation
Vector3 unfixedUp = newRotation * Vector3.up;
float angle = Vector3.Angle(Vector3.up,unfixedUp);
Vector3 axis = Vector3.Cross(Vector3.up,unfixedUp);
if (axis == Vector3.zero)
{
transform.rotation = Quaternion.AngleAxis(unfixedUp.y >= 0f ? 0f : 180f,Vector3.right);
}
else
{
transform.rotation = Quaternion.AngleAxis(angle,axis);
}
Vector3 platformAngles = transform.localRotation.eulerAngles;
platformCoords.text = "Platform:" + "\n" +
"X: " + platformAngles.x + "\n" +
"Y: " + platformAngles.y + "\n" +
"Z: " + platformAngles.z;
}
它几乎按预期工作:没有万向节锁定(至少不是一个容易发生的锁定。只有当你在 x 或 z 上旋转手机约 180 度时)并且 y 轴被冻结,但初始/校准旋转轴仍然是一个大问题。 这是一个 gif,希望能比其他的更好地显示问题: https://www.nani-games.net/share/93eHK0Fz1t.gif
平台上方的轴显示应用了偏移量的陀螺仪的旋转。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。