概要
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と検索してインストールしてください.
Javaファイルの作成
Androidプラグインとして読み込むJavaファイルを作成します.
Unityプロジェクト内にAssets/Plugins/Androidというフォルダを作成してください(Pluginsフォルダがない場合はそれも作成).
Assets/Plugins/Androidに以下のような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ファイルを作成します.
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のプラグインの呼び出しにはAndroidJavaObject と AndroidJavaClassを使用します.
上記のコードでは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にチェックを入れて有効化してください.

Assets/Plugins/AndroidにAndroidManifest.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のウィンドウが立ち上がります.
以下の画像ようにクォータニオンが出力されたら成功です.

