VSCodeでKotlinを使ってUnityで使えるAndroid用処理を作る　その２

##はじめに
[その1](https://qiita.com/fuminyami/items/ad2c1ebdb309a92a17a5)ではVSCodeを使いkotlinのプロジェクトをビルドするところまでを紹介しました。
その2ではUnityの拡張機能としてUnity側からの呼び出し可能なメソッドの実装例をAndroidのセンサーイベントをベースに紹介します。

##実装例

```kotlin
package ***.****

import kotlin.math.*
import com.unity3d.player.UnityPlayer
import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

fun start() {
    val activity: Activity = UnityPlayer.currentActivity as Activity;
    val sensorManager: SensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager;
    val accelerometer: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) as Sensor;

    sensorManager.registerListener(Receiver, accelerometer, SensorManager.SENSOR_DELAY_NORMAL)
}

fun stop() {
    val activity: Activity = UnityPlayer.currentActivity as Activity;
    val sensorManager: SensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager;
    
    sensorManager.unregisterListener(Receiver);
}

private val Receiver = object : SensorEventListener {

    private val lowpassFactor = 0.8f;
    private val NS2S = 1.0f / 1000000000.0f;
    private val EPSILON = 0.1f;
    private var dtRotationVector = FloatArray(3);
    private var timestamp = 0L;
    private var acceleration = FloatArray(3);
    private var gravity = FloatArray(3);
    private var magnet = FloatArray(3);
    private var dtRotationMatrix = FloatArray(9);

    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
    }
    
    override fun onSensorChanged(event: SensorEvent) {
        val type = event.sensor.getType();
        when (type) {

            // 加速度センサーイベント
            Sensor.TYPE_ACCELEROMETER -> {
                // 得られる加速度には重力値が含まれているので取り除く
                // ローパスフィルタで余分なノイズを除去し重力値を取得する
                gravity.set(0, lowpassFactor * gravity.get(0) + (1.0f - lowpassFactor) * event.values[0]);
                gravity.set(1, lowpassFactor * gravity.get(1) + (1.0f - lowpassFactor) * event.values[1]);
                gravity.set(2, lowpassFactor * gravity.get(2) + (1.0f - lowpassFactor) * event.values[2]);

                // 取得した重力値を引いて加速度を取得する
                acceleration.set(0, event.values[0] - gravity.get(0));
                acceleration.set(1, event.values[1] - gravity.get(1));
                acceleration.set(2, event.values[2] - gravity.get(2));
            }

            // ジャイロセンサーイベント
            Sensor.TYPE_GYROSCOPE -> {
                if (timestamp != 0L) {
                    val dt = (event.timestamp - timestamp) * NS2S;
                    
                    var axisX = event.values[0];
                    var axisY = event.values[1];
                    var axisZ = event.values[2];

                    val omegaMagnitude = sqrt(axisX * axisX + axisY * axisY + axisZ * axisZ);

                    // 小さすぎる変化量であれば無視する
                    if (omegaMagnitude > EPSILON) {
                        axisX /= omegaMagnitude;
                        axisY /= omegaMagnitude;
                        axisZ /= omegaMagnitude;
                    }

                    val thetaOverTwo = omegaMagnitude * dt / 2.0f;
                    val sinThetaOverTwo = sin(thetaOverTwo);
                    val cosThetaOverTwo = cos(thetaOverTwo);
                    dtRotationVector.set(0, sinThetaOverTwo * axisX);
                    dtRotationVector.set(1, sinThetaOverTwo * axisY);
                    dtRotationVector.set(2, sinThetaOverTwo * axisZ);
                    dtRotationVector.set(3, cosThetaOverTwo);
                }
                timestamp = event.timestamp;
                SensorManager.getRotationMatrixFromVector(dtRotationMatrix, dtRotationVector);
            }

            // 磁気センサーイベント
            Sensor.TYPE_MAGNETIC_FIELD -> {
                magnet.set(0, event.values[0]);
                magnet.set(1, event.values[1]);
                magnet.set(2, event.values[2]);
            }

            else -> {}
        }
    }
}

```

今回のコードであればstartとstopが静的関数となり呼び出しが可能となります。
デコンパイルしてコード（一部抜粋）を見てみます。

```kotlin
public final class ***Kt
{
  public static final void start()
  {
  }

  public static final void stop()
  {
  }
}
```

クラス名はソースコードのファイル名＋KtとなるのでUnity側から使用する場合は注意が必要です。

次にUnity側のコード例です

```c#

public static void start() {
using (AndroidJavaClass ajc = new AndroidJavaClass("***.****.***Kt")) {
    try {
            ajc.CallStatic("start");
        } catch (AndroidJavaException jex) {
            Debug.LogError(jex.Message);
        }
    }
}
```

kotlinで書く場合にはJavaと違ってコード量も少なくすっきり書けるのが良いですね。
関数の呼び出しに失敗する場合や値の取得をする為のスコープが不明な場合はデコンパイルしてみることを推奨します。Javaとの比較をしてみるのもいいかもしれません。

##さいごに
VSCodeやkotlin自体のドキュメントは実に豊富ですが、あまりUnityと絡めた記事が見つからなかったので今回書いてみました。
これを機にVSCodeやkotlinでちょっとやってみようかなという方がいれば参考にしてみてください。