如何反轉 Gyroscope (Quaternion) X-axis, Y-axis 輸入 ?

其實標題應該修改為學術一點的說法, 不過自己也會忘記就算了.
這問題應該細分為下面幾個部分

  1. 在 Local Space 的 Quaternion 中把 X-axis 或 Y-axis 反轉的方法.
  2. Gyroscope 是 Right hand 的輸入, 如何把 Right hand 轉換為 Left Hand
  3. Gyroscope 的方向是 World space, 如何把 World space 轉換為遊戲中的 Local space.

反轉 Quaternion 某個軸的方法 (限 Local Space)

方法其實有點取巧, 

private Quaternion GetPlayerInput(bool isFlipX, bool isFlipY)
{
	Vector3 euler = GetBiasDeviceOrientation();
	if (isFlipX) euler.x = -euler.x;
	if (isFlipY) euler.y = -euler.y;
	return Quaternion.Euler(euler);
}

取巧的方法就是把 Quaternion 轉為 Euler Angle, 即是降維, 由四維降到三維.
限制也很明顯就是 Gimbal lock 的限制,
但除此以外.並沒有甚麼大問題.

Right hand 轉換為 Left hand

這推涉及 Sin / Cos 算法的東西我就不深研了.
總之 Unity3D tutorial 是這樣教的我也沒有甚麼意見.

private Quaternion GyroToUnity(Quaternion q) => new Quaternion(q.x, q.y, -q.z, -q.w);

 

World Space 轉為 Local Space

為甚麼需要 World Space 轉為 Local Space 呢?
原因在於 Player 拿著手機的時候, 你是不知道他怎樣拿的, 有沒有拿返等等的意思.
以圖片表示就是你以為世界

我們把 角度畫出來就像上圖這樣, 外面較大的象限是代表世界, 裡面少的象限是代表槍模型.

那麼 Gryoscope 可能會給你這種扭轉了的面向.
而我們就要把這種扭轉了的面向糾正. 方法有兩個, 但原理是一致的.
就是把 Gryoscope 的差距消除(廢話).

Low tech 的辦法是,

用兩層 GameObject, 在校正階段把外層 Gryoscope 寫入 Input.gryoscope 的值.
而內層的 Gun Model 則扭到你希望對齊的地方 (e.g. 鏡頭 Camera 的正前方)

數學一點的辦法就是把 Quaterion 對消掉.
像這樣

Quaternion SampleRotation;
SampleRotation.Inverse() * SampleRotation; // This will zero out all rotation

像上面的代碼不論 SampleRotation 的值是甚麼最終是會回傳一個 Quaternion (0,0,1,0) 的值. 就是 local space 的初始值.
因為反轉後的動量就會對消掉左右/上下的角度值, 變相是取得了相對的 Local space quaternion.

所以在 Awake 時先把 Gyroscope 的值暫存.
然後在需要使用時把 Quaternion 當作角動量來算的話, 先 Inverse 一下先前的動量再疊加現在 Gyroscope 的動量
當我們是 Local space 的時候, 就可以套用開頭的 Euler angle 方式就可以套用下去.

Quaternion m_WorldSpaceOrientationBias;
private void Awake()
{
	m_WorldSpaceOrientationBias = GyroToUnity(Input.gyro.attitude);
}

private Quaternion GetPlayerInput(bool isFlipX, bool isFlipY)
{
	Vector3 euler = GetBiasDeviceOrientation();
	if (isFlipX) euler.x = -euler.x;
	if (isFlipY) euler.y = -euler.y;
	return Quaternion.Euler(euler);
}
private Quaternion GetBiasDeviceOrientation()
{
	return m_WorldSpaceOrientationBias.Inverse() * GyroToUnity(Input.gyro.attitude);
}
private Quaternion GyroToUnity(Quaternion q) => new Quaternion(q.x, q.y, -q.z, -q.w);

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料