いんでぃーづ

ゲームメインのブレブレブログ。ゲーム,Unity,デザイン,UI/UX

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

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

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

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

じぶんで かんがえろ!


“Unity” and Unity logos are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere, and are used under license.


免責事項

当サイトの広告バナー、リンクによって提供される情報、サービス内容について、当サイトは一切の責任を負いません。

また、当サイトの情報を元にユーザ様が不利益を被った場合にも、当サイトは一切の責任を負いません。

すべて自己責任でお願いします。