Posted at

ジャイロとMainCameraの傾き・プレイヤーの同期をもっと密にとる

More than 3 years have passed since last update.

ここまでスマホのジャイロセンサとカメラの同期プレイヤーの同期2回に渡って触れてきたが完全なものとは言い難かったので今回それをブラッシュアップした形で書き直します。

大きな問題点は以下の二つだった

・Mainカメラが回転に追従しない

・プレイヤーがz軸周り、x軸周りどちらの回転角の値を指定しても左右にプレイヤーがうごいてしまう


Mainカメラが回転に追従しない

この事象が起きたのは下記のコードが原因でした。

this.transform.localRotation = Quaternion.Euler(0, -start_gyro.y, 0);

正面の向きに合わせるキャリブレーションを行うコードが固定的に各軸周りの回転度を指定していたため起こったようです。さらにいうとクオータニオンで指定されたものをオイラー変換なしにオイラー角に指定するのはどうやら御法度のようです...

クオータニオン難しい、、、

これを正面の向きに合わせるキャリブレーションを行いつつ、現在のジャイロセンサの値を反映させるには、初期回転の逆回転を行い、そのご現状のジャイロセンサの値分だけ回転させる操作が必要になります。それを行った方法が以下


CameraController.cs

using UnityEngine;

using System.Collections;
//OnGUIの部分は今回は削除
public class CameraController : MonoBehaviour {
private GUIStyle labelStyle;
Quaternion start_gyro;
Quaternion gyro;

void Start(){

}

void Update () {
Input.gyro.enabled = true;
if (Input.gyro.enabled)
{
gyro = Input.gyro.attitude;
gyro = Quaternion.Euler(90, 0, 0) * (new Quaternion(-gyro.x,-gyro.y, gyro.z, gyro.w));
this.transform.localRotation = Quaternion.Inverse(StartCameraController.ini_gyro) * gyro;
}
}
}


StartCameraController.ini_gyroが初期のジャイロセンサの値、Quaternion.Inverseがジャイロセンサの値をもとに逆回転を行う関数です。その後、現状のジャイロセンサの回転をかけてさらに回して現状のジャイロセンサの値を反映しています。

※逆回転の時にくれぐれも「各要素にー1をかけて回転すればいいや!」と勘違いしてはいけません。-1をかけていない時と同じ回転をするらしいですよ!下記の記事参考↓

http://www23.atwiki.jp/yahirohumpty/pages/13.html


プレイヤーがz軸周り、x軸周りどちらの回転角の値を指定しても左右にプレイヤーがうごいてしまう

今回はVRレーシングゲーム用ジャイロセンサで操作するプレイヤーを作成するの記事で記載するプレイヤーをジャイロセンサで操作するロジックの下記の部分が問題だったから起こった事象だと考えられます。


before

Vector3 p = new Vector3 (action_gyro.x, 0, Speed);


上記の部分が下記のように変更することで解決しました。


after

Vector3 p = new Vector3 (-1.0f*Mathf.Sin(action_gyro.eulerAngles.z * Mathf.Deg2Rad), 0, Speed);


今まで、クオータニオンから直接x軸周りの値を入れていましたが、そもそもx軸周りの回転を入れることが正しくないことに気づきました(このゲームの進行方向がz軸方向なのでこの場合z軸周りの値をとるのが正しい)。

それでも正しく作動していたように見えたのは、x軸とz軸の値が同時に変化していたためでした。

なぜ、x軸とz軸の値が同時に変化していたのかはわからないですが多分、それこそがクオータニオン特有の回転だったのだと思います。

上記のことを改めるべく、クオータニオンをオイラー角に変化させ、それにSin関数を当てはめました。

コードの完全版は下記になります。


Runner.cs

public class Runner : MonoBehaviour {

private GUIStyle labelStyle;
Quaternion gyro;
bool goal = false;
public float Speed = 0.3f;
void Update () {
if (goal == false) {
Input.gyro.enabled = true;
if (Input.gyro.enabled) {
Quaternion gyro = Input.gyro.attitude;
Quaternion action_gyro = Quaternion.Euler (90, 0, 0) * (new Quaternion (-gyro.x, -gyro.y, gyro.z, gyro.w));
action_gyro = Quaternion.Inverse(StartCameraController.ini_gyro) * action_gyro;
Vector3 p = new Vector3 (-1.0f*Mathf.Sin(action_gyro.eulerAngles.z * Mathf.Deg2Rad), 0, Speed);
Debug.Log (action_gyro.eulerAngles.z);
transform.position += p;
}
}
}
void OnGoal() {
goal = true;
}

}



まとめ

今回学んだことはくれぐれもクオータニオンを要素で使おうとしてはいけないということでした、なるべくクオータニオンは一つの行列として扱うべきだったのです。そして、各要素にアクセスする場合は直感的にも想像できるオイラー角に直して利用するべきでした。それにしてもクオータニオンは難しいです。