はじめに
この記事は突然仕事でUnity向けのSDKやPluginを作ることになった人向け資料<Android実践編>の続編です。
最近、Unityはほぼほぼやっていないのですが
受託開発でも自社開発でもKotlinを使っており、
UnityPlugin開発に使えそうな気づきがあったのでまとめます。
環境は以下です
- Unity2017.2
- Android Studio 3.0.1 (Kotlin 1.2.21)
1 開発方法について
aarの作り方、UnityのPlugin仕様は前回記事でJavaで行った方法と同じなので省略。
New Module > Android Library でPlugin用Moduleを作成します。
2 Android Studio3/KotlinのPlugin開発で変わったこと
以下、サンプル開発に使用したPlugin ModuleのGradleファイルです。
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
//コメントアウト
//implementation fileTree(dir: 'libs', include: ['*.jar'])
//implementation 'com.android.support:appcompat-v7:26.1.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
//追加
compileOnly fileTree(dir: 'libs', include: ['unity-classes.jar'])
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}
repositories {
mavenCentral()
}
2.1 Gradleファイルの変更点: Kotlin
Kotlinを有効にしたので以下が増えています。
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
プロジェクトのGradleにはKotlinのバージョンが追加されます。

2.2 Gradleファイルの変更点: Android Studio 3
unityのjarをコンパイル時のみ使用して、ビルドしたaarに含まない必要があります。
以下のAかBを行います。
A. libsフォルダ以下のjarを全部いれてunityのjarだけ引っこ抜く
:
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
android.libraryVariants.all { variant ->
variant.outputs.each { output ->
output.packageLibrary.exclude('libs/unity-classes.jar')
}
}
B. unityのjar以外にjarがなければコンパイル時のみ参照するcompileOnly
を使用
:
//コメントアウト
//implementation fileTree(dir: 'libs', include: ['*.jar'])
compileOnly fileTree(dir: 'libs', include: ['unity-classes.jar'])
:
3. KotlinでUnityPluginを書く
サンプルとしてKotlin側(Plugin側)をざっくり書くと以下のようになります。
package red.torch.nantokaplugin
import android.app.Activity
import com.unity3d.player.UnityPlayer
import com.unity3d.player.UnityPlayer.UnitySendMessage
@Suppress("unused")
class UnityBridge {
//3.1 通常のメソッド呼び出し
fun callUnityCode() {
UnitySendMessage("ReceivedGameObject", "callbackMethod", "hogehoge")
}
//Javaと同じくUI操作はメインスレッドで
fun someUIAction() {
val activity = UnityPlayer.currentActivity
activity.runOnUiThread {
//UI操作の処理
}
}
companion object {
//3.2 companion objectメソッド呼び出し
fun companionMethod(a: Int, b:Int) {
UnitySendMessage("ReceivedGameObject", "callbackMethod", (a + b).toString())
}
//3.3 @Jvmstaticのcompanion objectメソッド呼び出し
@JvmStatic fun jvmCompanionMethod(a: Int, b: Int) {
UnitySendMessage("ReceivedGameObject", "callbackMethod", (a * b).toString())
}
}
}
Unity側のサンプルコードは以下のようになります
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class NewBehaviourScript : MonoBehaviour {
//確認用
[SerializeField]
private Text text;
public void onButton1()
{
//3.1 通常のメソッド呼び出し
using (AndroidJavaObject jo = new AndroidJavaObject("red.torch.nantokaplugin.UnityBridge"))
{
jo.Call("callUnityCode");
}
}
public void onButton2()
{
//3.2 companion objectメソッド呼び出し
using (AndroidJavaClass jc = new AndroidJavaClass("red.torch.nantokaplugin.UnityBridge"))
{
jc.GetStatic<AndroidJavaObject>("Companion").Call("companionMethod", 2, 3);
}
}
public void onButton3()
{
//3.3 @Jvmstaticのcompanion objectメソッド呼び出し
using (AndroidJavaObject jo = new AndroidJavaObject("red.torch.nantokaplugin.UnityBridge"))
{
jo.CallStatic("jvmCompanionMethod", 2, 4);
}
}
//確認用: UnitySendMessageの第2引数にメソッド名を合わせる
void callbackMethod(string str) {
text.text = str;
}
}
3.1 通常のメソッド呼び出し
Javaとあまり変わりませんがKotlin側のメソッドはpublic省略可、void省略可より以下のようになります。
Javaと同じくUnitySendMessageをゲームオブジェクト名, メソッド名, 引数文字列で呼び出します
fun callUnityCode() {
UnitySendMessage("ReceivedGameObject", "callbackMethod", "hogehoge")
}
Unity側ではAndroidJavaObject/AndroidJavaClassがそのまま使えます。
aar化したKotlinのコードJavaとして動作します。
using (AndroidJavaObject jo = new AndroidJavaObject("red.torch.nantokaplugin.UnityBridge"))
{
jo.Call("callUnityCode");
}
:
void callbackMethod(string str) {
text.text = str;
}
3.2 companion objectメソッド呼び出し
Kotlinにはstatic methodがなくcompanion objectで代用します。
Kotlin上では下記メソッドはUnityBridge.Companion.companionMethod
として
あたかもstatic methodのように呼び出すことが出来ます。
companion object {
:
fun companionMethod(a: Int, b:Int) {
UnitySendMessage("ReceivedGameObject", "callbackMethod", (a + b).toString())
}
ただし、Unity側から呼び出す場合は..
using (AndroidJavaClass jc = new AndroidJavaClass("red.torch.nantokaplugin.UnityBridge"))
{
jc.GetStatic<AndroidJavaObject>("Companion").Call("companionMethod", 2, 3);
}
上記のように面倒になります。companionはあくまでオブジェクトなので
- 対象クラスを
AndroidJavaClass
として取得 - 1からstaticのobjectである
Companion
を取得 - 2から非staticのメソッド
companionMethod
呼び出し
となります。
# 上記危ういのですが、このパターンで実際に動作しました
3.3 Jvmstaticのcompanion objectメソッド呼び出し
Kotlinでstatic method実現は難しい..!
のですが@Jvmstatic
をつけるとCompanion
を経由せず
UnityBridge.jvmCompanionMethod
として呼び出すことが出来ます。
companion object {
:
@JvmStatic fun jvmCompanionMethod(a: Int, b: Int) {
UnitySendMessage("ReceivedGameObject", "callbackMethod", (a * b).toString())
}
Unity側でもJavaのstaticのように呼び出すことが出来ます。楽!
using (AndroidJavaObject jo = new AndroidJavaObject("red.torch.nantokaplugin.UnityBridge"))
{
jo.CallStatic("jvmCompanionMethod", 2, 4);
}
4. 動作確認
実際にKotlinで作ったaarを組み込んで動作確認を行いました。3.1, 3.2, 3.3が正常に動作することを確認済みです。
- 3.1で"hogehoge"の文字列が
UnitySendMessage
で返ってくる

- 3.2 companion objectへのアクセスで2+3の答えが返ってくる

- 3.3 jvmstaticのcompanion objectへのアクセスで2*4の答えが返ってくる

まとめ
- KotlinでUnity Pluginは普通に作れる。
- AndroidJavaObjectも普通に使える。
- static methodはcompanion objectに
@JvmStatic
をつけると良い