1
1

UnityでAndroid端末の回転ベクトルを取得する

Last updated at Posted at 2024-03-15

概要

Android端末の複数センサを用いて算出された回転ベクトルをUnityから取得する方法についてまとめます.

やりたいこと

Android OS は複数センサの情報を複合した回転ベクトルを算出できるので,Unityからその情報を取得したい.

解決手法

UnityからAndroidの回転ベクトルを取得するAndroid Pluginを作成.

環境

  • Unity 2022.3.4f1
  • Windows 11
  • Google Pixel 3a
  • Android バージョン12

実装

Androidプラグインの作成にはいくつかの方法がありますが,今回はJavaファイルをUnityのプロジェクト内に配置してプラグインとして読み込ませる方法をとることにしました.

プラグインの再利用や配布を考えるなら,直接Javaファイルを配置するのではなく,Android Libraries や Android Archives (AAR) を利用した方法の方が適していると思います.

Unityプロジェクトの準備

まずunityで新規プロジェクトを立ち上げます.
新規プロジェクトが立ち上がったら,File > Build Settings > Androidからswitch platformを選択して,プラットフォームをAndroidに切り替えてください.

次に実機デバッグ用に Android Logcatという拡張機能を導入します.
Android LogcatはPCに接続されたアンドロイド端末のログをUnityの専用タブから出力してくれます.
Window > Packagemanagerを起動して,左上のPackages:○○Packages:Unity Registryに切り替え,右上からAndroid Logcatと検索してインストールしてください.

スクリーンショット 2024-03-15 145507.png

Javaファイルの作成

Androidプラグインとして読み込むJavaファイルを作成します.
Unityプロジェクト内にAssets/Plugins/Androidというフォルダを作成してください(Pluginsフォルダがない場合はそれも作成).
Assets/Plugins/Androidに以下のようなRotation.javaファイルを作成します.

Rotation.java
package Assets.Plugins.Android;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import com.unity3d.player.UnityPlayerActivity;

public class rotation extends UnityPlayerActivity  implements SensorEventListener {
    private SensorManager sensorManager;
    private Sensor rotationSensor = null;
    private Float sensorX = 0f;
    private Float sensorY = 0f;
    private Float sensorZ = 0f;
    private Float sensorW = 1f;
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        rotationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        sensorManager.registerListener(this,rotationSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
    @Override
    public void onSensorChanged(SensorEvent event) {
        sensorX = event.values[0];
        sensorY = event.values[1];
        sensorZ = event.values[2];
        sensorW = event.values[3];
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {

    }
    public float[] getRotation(){
        return new float[]{sensorX, sensorY, sensorZ, sensorW};
    }
}

コードの解説

public class rotation extends UnityPlayerActivity  implements SensorEventListener{
//省略
}

Androidでセンサデータを見るにはActivityクラスとSensorEventListenserインターフェースを継承する必要があります.
今回はUnityが提供するActivityの派生クラスUnityPlayerActivityを継承しました.UnityPlayerActivityに関する詳しい説明はこちらを確認してください.

@Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
        rotationSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
        sensorManager.registerListener(this,rotationSensor, SensorManager.SENSOR_DELAY_NORMAL);
    }

この部分ではセンサを取得してイベントリスナに登録しています.
取得できるセンサ情報はAndroid公式ドキュメントから確認できます.今回は回転ベクトルSensor.TYPE_ROTATION_VECTORを取得しました.

    @Override
    public void onSensorChanged(SensorEvent event) {
        sensorX = event.values[0];
        sensorY = event.values[1];
        sensorZ = event.values[2];
        sensorW = event.values[3];
    }

onSensorChangedは新しいSensorEventが発生するたびに呼び出されます.
eventからセンサの値を取得してセンサ情報を更新します.
ちなみに今回取得する回転ベクトルはオイラー角ではなくクォータニオン形式なので,変数は4つです.

public float[] getRotation(){
        return new float[]{sensorX, sensorY, sensorZ, sensorW};
    }

Unityから利用する関数です.回転ベクトルの配列を返します.

C#ファイルの作成

Androidプラグインを呼び出すC#のプログラムを書きます.
UnityのAssetsフォルダ内にScriptsフォルダを作成し,その中にRotationManager.csファイルを作成します.

RotationManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotationManager : MonoBehaviour
{
    private AndroidJavaObject activity;
    // Start is called before the first frame update
    void Start()
    {
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        activity  = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    }

    // Update is called once per frame
    void Update()
    {
        var rot = activity.Call<float[]>("getRotation");
        Debug.Log(new Quaternion(rot[0],rot[1],rot[2],rot[3]));

    }
}

コードの解説

void Start()
    {
        AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        activity  = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    }

C#からのJavaやKotlinのプラグインの呼び出しにはAndroidJavaObjectAndroidJavaClassを使用します.
上記のコードではunityPlayer.GetStaticを用いて,com.unity3d.player.UnityPlayerクラスの静的なフィールドcurrentActivityを取得しています.
後述するエントリポイントの設定により,currentActivityにはRotation.javaで作成したrotationクラスのインスタンスが登録されています.

void Update()
    {
        var rot = activity.Call<float[]>("getRotation");
        Debug.Log(new Quaternion(rot[0],rot[1],rot[2],rot[3]));
    }

毎フレーム回転ベクトルを出力するコードです.
activityにはrotationのオブジェクトが格納されているので,activity.Call<float[]>("getRotation")getRotationメソッドを実行し,回転ベクトルを取得します.

カスタムマニフェストの作成

カスタムマニフェストを作成し,プラグインで作成したアクティビティをアプリケーションのエントリポイントに設定します.
Edit > Project SettingsよりProject Settingsウィンドウを起動して,PlayerからAndroidのマークを選び,Publishing Settings > Build > Custom Main Manifestにチェックを入れて有効化してください.
スクリーンショット 2024-03-15 143656.png
Assets/Plugins/AndroidAndroidManifest.xmlというファイルが生成されるため,AndroidManifest.xmlを以下のように書き換えてエントリポイントを変更します.

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">
    <application>
-       <activity android:name="com.unity3d.player.UnityPlayerActivity"
+       <activity android:name="Assets.Plugins.Android.rotation"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>

アプリケーションのビルドと動作テスト

UnityエディタのHierarchyウィンドウにEmptyオブジェクトを追加し,作成したC#スクリプトをアタッチします.
Android端末をPCに接続し,File > Build and Runからビルドを実行します.
ビルドが成功すると,Android端末でアプリケーションが起動してUnityエディタにAndroid Logcatのウィンドウが立ち上がります.
以下の画像ようにクォータニオンが出力されたら成功です.
スクリーンショット 2024-03-15 185645.png

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1