*CardboardSDKはGoogle VR SDKに名前が変わりました
Androidで端末の傾きを取りたいとき、思いのほか苦労します。 Input.gyro などUnity単体のジャイロセンサーでとれる値は、どうやってもズレが発生してしまうようです。
GoogleのVR用デバイスCardboard用のSDKが公開されているのですが、これの傾き取得がかなり優秀のようです。 プラグインを読み込んで使っているようなので、やはりUnity単体では難しいということでしょう。
ただこのSDKをそのまま使うと、VR用のカメラが自動でシーンに追加されてしまいます。 そこでこのSDKから必要な部分のみ拝借し、端末の傾きの数値のみ取得するようにしてみます。
☆今回はAndroidのみ
ライセンスもApache2.0 + MIT と相当ゆるいので、ありがたく使わせていただきましょう。
まずは公式ページからSDKをダウンロード&インポートします。
https://developers.google.com/vr/unity/download?hl=ja
ひな形として、適当な名前でスクリプトを作りましょう。
SDKのソースからいろいろコピペ
Cardboardのいろんなソースからコピペしてくるのですが、いちいち説明が面倒なのでコピペ済みファイルコードを張ります。
using UnityEngine; using System.Collections; using System.Runtime.InteropServices; public class CardboardAndroidTest : MonoBehaviour { [SerializeField] Transform device; // デバッグ用のTransform /** * いろんなファイルからコピペ */ protected MutablePose3D headPose = new MutablePose3D(); private float[] headData = new float[16]; private Matrix4x4 headView = new Matrix4x4(); public struct DisplayMetrics { public int width, height; public float xdpi, ydpi; } void Start () { DisplayMetrics dm = GetDisplayMetrics(); Start(dm.width, dm.height, dm.xdpi, dm.ydpi); } void Update () { GetHeadPose(headData); ExtractMatrix(ref headView, headData); headPose.SetRightHanded(headView.inverse); // headPose.Orientation が傾きになる device.transform.rotation = headPose.Orientation; Debug.Log (string.Format("pose - x : {0}, y : {1}, z {2} ", headPose.Orientation.eulerAngles.x, headPose.Orientation.eulerAngles.y, headPose.Orientation.eulerAngles.z)); } /** * Assets/Cardboard/Scripts/VRDevices/CardboardAndroidDevice.cs * からコピペ */ // Returns landscape orientation display metrics. private const string ActivityListenerClass = "com.google.vr.platform.unity.UnityVrActivityListener"; // !!! overrideは消す public DisplayMetrics GetDisplayMetrics() { using (var listenerClass = GetClass(ActivityListenerClass)) { // Sadly some Android devices still don't report accurate values. If this // doesn't work correctly on your device, comment out this function to try // the Unity implementation. float[] metrics = listenerClass.CallStatic<float[]>("getDisplayMetrics"); // Always return landscape orientation. int width, height; if (metrics[0] > metrics[1]) { width = (int)metrics[0]; height = (int)metrics[1]; } else { width = (int)metrics[1]; height = (int)metrics[0]; } // DPI-x (metrics[2]) on Android appears to refer to the narrow dimension of the screen. return new DisplayMetrics { width = width, height = height, xdpi = metrics[3], ydpi = metrics[2] }; } } private int ExtractMatrix(ref Matrix4x4 mat, float[] data, int i = 0) { // Matrices returned from our native layer are in row-major order. for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++, i++) { mat[r, c] = data[i]; } } return i; } /** * Assets/Cardboard/Scripts/VRDevices/BaseAndroidDevice.cs * からコピペ */ protected AndroidJavaClass GetClass(string className) { try { return new AndroidJavaClass(className); } catch (AndroidJavaException e) { Debug.LogError("Exception getting class " + className + ": " + e); return null; } } #if UNITY_IOS private const string dllName = "__Internal"; #else private const string dllName = "vrunity"; #endif delegate void VREventCallback(int eventID); [DllImport(dllName)] private static extern void Start(int width, int height, float xdpi, float ydpi); [DllImport(dllName)] private static extern void SetEventCallback(VREventCallback callback); [DllImport(dllName)] private static extern void SetTextureId(int id); [DllImport(dllName)] private static extern bool SetDefaultProfile(byte[] uri, int size); [DllImport(dllName)] private static extern void SetUnityVersion(byte[] version_str, int version_length); [DllImport(dllName)] private static extern void EnableDistortionCorrection(bool enable); [DllImport(dllName)] private static extern void EnableAutoDriftCorrection(bool enable); [DllImport(dllName)] private static extern void EnableElectronicDisplayStabilization(bool enable); [DllImport(dllName)] private static extern void SetNeckModelFactor(float factor); [DllImport(dllName)] private static extern void ResetHeadTracker(); [DllImport(dllName)] private static extern void GetProfile(float[] profile); [DllImport(dllName)] private static extern void GetHeadPose(float[] pose); [DllImport(dllName)] private static extern void GetViewParameters(float[] viewParams); [DllImport(dllName)] private static extern void Pause(); [DllImport(dllName)] private static extern void Resume(); [DllImport(dllName)] private static extern void Stop(); }
Update 関数内で取得したheadPose.Orientationが端末の傾きになります。
ちなみにこのコードをエディタで動かそうとするとエラーが発生するのでご注意。
え?どうやればエディタで使えるかって?
じぶんで かんがえろ!