読者です 読者をやめる 読者になる 読者になる

いんでぃーづ

個人でゲーム開発してる上で吸収したモノたち紹介。UnityからGIMPまでなんでも。デザインとかゲーム論まで語っちゃうよ的なアレ。

MENU
{スポンサーリンク}

UnityでAndroid 端末の正確な傾きを取得したい→とりまCardboardSDK使えばいけるよ

Unity Tips VR

*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が端末の傾きになります。

ちなみにこのコードをエディタで動かそうとするとエラーが発生するのでご注意。

え?どうやればエディタで使えるかって?

じぶんで かんがえろ!

Amazon.co.jpアソシエイト