🤝

【Android】KotlinネイティブアプリからUnityアプリを呼び出してみた

に公開

Kotlin のネイティブアプリから Unity アプリを呼び出してみた内容を記載します。

※ 備忘録 & とりあえずの動作検証であったため、細かい部分の解説はありません。かなり "えいやっっ!" で動作させてみた内容です。


【検証環境】

  • Unity 6.2(6000.2.9f1)
  • Android Studio Narwhal Feature Drop | 2025.1.2
    • Kotlin:: 2.0.2
  • Android, Unity Export: build.gradle
    • compileSdk: 36
    • minSdk: 31
    • targetSdk: 36


【目次】

  1. Unity アプリの Export
  2. Kotlin アプリの作成
  3. Kotlin アプリと Unity アプリの連携


Unity アプリの Export

Unity をネイティブアプリから呼び出すには、Unity 公式のUnity as a Library機能を使用します。

https://unity.com/ja/features/unity-as-a-library


今回はシンプルに Unity のAR Mobileテンプレートでアプリを新規作成して、Kotlin アプリから呼び出す形とします。

テンプレアプリを作成できたら、以下の設定を確認します。

  1. Edit > Project Settings > Player > Configuration

    • Scriptiing Backend が IL2CPP になっているか
    • Target Architectures に ARM64 がチェックされているか
    • ※ 新しめの Unity バージョンだとデフォルト設定されてそう。
  2. File > Build Profiles

    • Platforms の Android を有効化する
    • Export Project にチェックを入れる
  3. Export ボタンを押下して実行 - Export 先のフォルダを選択できるので、"NewFolder"で適当に Export 用のフォルダを作成(ここでは"build"として作成)し、"Choose"を押下。

    ↑Export 後の中身


※ いろんな記事を参考に実施しました。他に必要そうな設定があれば適宜設定してください。
https://qiita.com/norihirosunada/items/f26e77c7555b0f38ac8b
https://qiita.com/cha84rakanal/items/4a1e4b242ec40e3e8121
https://zenn.dev/arsaga/articles/ede728a794a553


Android プロジェクトの立ち上げ

こっちはシンプルに Android Studio で新規プロジェクトを立ち上げるだけです。

AndroidStudio で New Project / Empty Activity で作成したテンプレ新規状態のアプリで試していきます。

後ほど差分を確認したいので、このまま一旦コミットしておきます。


Kotlin アプリと Unity アプリの連携

ここからが本題です。
先ほど Export した Unity アプリの中身を、Kotlin アプリに組み込んでいきます。

ここの対応が記事を参考にしても中々うまくいかず & 最適解が分からず、結局最後は AI による力技で動かせるところまで持っていきました。

  1. Export した Unity アプリのunityLibrarySharedフォルダを、Kotlin アプリのホームディレクトリに配置

    • Sharedも配置しないと build 時に怒られました。
  2. unityLibraryをそのままコミット履歴に含めると、"3K+" もの差分になってしまうので、こちらの記事を参考に、unityLibraryをコミット履歴から対象外に設定

    • unityLibrary/.gitignore
      • /build
      • /src
      • /symbols
      • /xrmanifest.androidlib/build
  3. Kotlin アプリの画面レイアウトを Unity を呼び出せるように修正

    • 今回はシンプルに呼び出すだけの画面構成としました。既存の UI 部分の定義は全て削除
MainActivity.kt
+ package com.example.hoge
+ import android.os.Bundle
+ import com.unity3d.player.UnityPlayerGameActivity
+
+ class MainActivity : UnityPlayerGameActivity() {
+     override fun onCreate(savedInstanceState: Bundle?) {
+         super.onCreate(savedInstanceState)
+     }
+ }

- class MainActivity : ComponentActivity() {
-     override fun onCreate(savedInstanceState: Bundle?) {
-         super.onCreate(savedInstanceState)
-         enableEdgeToEdge()
-         setContent {
-             UnityImportTheme {
-                 Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
-                     Greeting(
-                         name = "Android",
-                         modifier = Modifier.padding(innerPadding)
-                     )
-                 }
-             }
-         }
-     }
- }-
- @Composable
- fun Greeting(name: String, modifier: Modifier = Modifier) {
-     Text(
-         text = "Hello $name!",
-         modifier = modifier
-     )
- }-
- @Preview(showBackground = true)
- @Composable
- fun GreetingPreview() {
-     UnityImportTheme {
-         Greeting("Android")
-     }
- }
  1. 以降、記事を参考に色々設定したがうまくビルドできなかったので、ClaudeCode にぶん投げました。
    変更点などの詳細は ClaudeCode に別途まとめてもらったので、気になる方はご確認ください。

    ↓ 変更量としてはこれぐらいです。

【変更点詳細: ClaudeCode 作】

Unity Export を Android(Kotlin)プロジェクトに統合する手順

概要

本ドキュメントは、Unity から Export した Android プロジェクト(unityLibrary)を既存の Kotlin + Jetpack Compose プロジェクトに統合し、ビルドを成功させるまでの手順をまとめたものです。

前提条件

  • Unity 6000.2.x(IL2CPP バックエンド使用)
  • Android Studio
  • Gradle 8.13 + Android Gradle Plugin 8.12.3
  • Kotlin 2.0.21
  • Compile SDK: 36

1. プロジェクト構成

AndroidCallUnity/
├── app/           # メインアプリケーションモジュール
├── unityLibrary/  # Unity から Export したライブラリ
│ ├── libs/        # unity-classes.jar, AAR ファイル
│ ├── src/main/
│ │ ├── Il2CppOutputProject/  # IL2CPP ソースコード
│ │ ├── jniLibs/              # ネイティブライブラリ (.so)
│ │ └── java/                 # Unity Player Java クラス
│ └── xrmanifest.androidlib/  # XR マニフェストモジュール
├── shared/                   # 共有 Gradle スクリプト
├── settings.gradle.kts
└── local.properties

2. 変更が必要なファイル

2.1 settings.gradle.kts

Unity モジュールを Gradle プロジェクトに追加します。

dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)  // FAIL_ON_PROJECT_REPOSから変更
    repositories {
        google()
        mavenCentral()
+        flatDir {
+            dirs("unityLibrary/libs")  // AARファイル用のフラットディレクトリを追加
        }
    }
}

rootProject.name = "UnityCallSample"
+ include(":app", ":unityLibrary", ":unityLibrary:xrmanifest.androidlib")

ポイント:

  • repositoriesModePREFER_SETTINGSに変更(unityLibrary 内の flatDir リポジトリを許可)
  • flatDirで AAR ファイルのディレクトリを指定
  • unityLibraryxrmanifest.androidlibを include

2.2 gradle/libs.versions.toml

android-library プラグインを追加します。

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
+ android-library = { id = "com.android.library", version.ref = "agp" }  # 追加
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

2.3 build.gradle.kts(ルート)

plugins {
    alias(libs.plugins.android.application) apply false
+     alias(libs.plugins.android.library) apply false  // 追加
    alias(libs.plugins.kotlin.android) apply false
    alias(libs.plugins.kotlin.compose) apply false
}

2.4 app/build.gradle.kts

unityLibrary 依存関係を追加します。

dependencies {
+     implementation(project(":unityLibrary"))  // 追加
    // その他の依存関係...
}

2.5 gradle.properties

Unity Streaming Assets 設定を追加します。

+ # Unity Streaming Assets
+ unityStreamingAssets=.unity3d

2.6 local.properties

Unity SDK/NDK パスを設定します。

+ sdk.dir=/Users/xxx/Library/Android/sdk
+ unity.androidSdkPath=/Users/xxx/Library/Android/sdk
+ unity.androidNdkPath=/Applications/Unity/Hub/Editor/6000.2.9f1/+ PlaybackEngines/AndroidPlayer/NDK

3. unityLibrary/build.gradle の修正

3.1 フラットディレクトリリポジトリの追加

repositories {
    flatDir {
        dirs 'libs'
    }
}

3.2 依存関係をapiに変更

app モジュールから Unity クラスにアクセスできるよう、implementationapiに変更します。

dependencies {
    api fileTree(dir: 'libs', include: ['*.jar'])
    api(name: 'arcore_client', ext:'aar')
    api(name: 'ARPresto', ext:'aar')
    api(name: 'UnityARCore', ext:'aar')
    api(name: 'unityandroidpermissions', ext:'aar')
    api project(':unityLibrary:xrmanifest.androidlib')
    api 'androidx.appcompat:appcompat:1.6.1'
    api 'androidx.core:core:1.9.0'
    api 'androidx.games:games-activity:3.0.5'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}

3.3 local.properties からプロパティを読み込む関数を追加

def getLocalProperty(String key) {
    Properties local = new Properties()
    local.load(new FileInputStream("${rootDir}/local.properties"))
    return local.getProperty(key)
}

3.4 IL2CPP ビルド時のプロパティ参照を修正

getProperty()getLocalProperty()に変更します。

commandLineArgs.add("--tool-chain-path=" + getLocalProperty("unity.androidNdkPath"))
// ...
execCommand(workingDir, [command, *commandLineArgs], [
    "ANDROID_SDK_ROOT": getLocalProperty("unity.androidSdkPath"),
    "ANDROID_NDK_ROOT": getLocalProperty("unity.androidNdkPath"),
    "NDK_ROOT": getLocalProperty("unity.androidNdkPath"),
    "ANDROID_NDK_HOME": getLocalProperty("unity.androidNdkPath"),
])

3.5 IL2CPP ビルドエラー修正(ファイル存在チェック)

シンボルファイルが存在しない場合のエラーを回避するため、存在チェックを追加します。

def fileToRemove = file("${workingDir}/src/main/jniLibs/${abi}/libil2cpp${extensionToRemove}")
if (fileToRemove.exists()) {
    delete "${workingDir}/src/main/jniLibs/${abi}/libil2cpp${extensionToRemove}"
}

def fileToMove = file("${workingDir}/src/main/jniLibs/${abi}/libil2cpp${extensionToKeep}")
if (fileToMove.exists()) {
    ant.move(file: "${workingDir}/src/main/jniLibs/${abi}/libil2cpp${extensionToKeep}",
             tofile: "${workingDir}/symbols/${abi}/libil2cpp.so")
}

4. xrmanifest.androidlib/AndroidManifest.xml の修正

package属性を削除します(namespace は build.gradle で指定)。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
  <application>
    <activity android:name="com.unity3d.player.UnityPlayerGameActivity" />
  </application>
</manifest>

5. app/AndroidManifest.xml の Unity 対応設定

<activity
    android:name=".MainActivity"
    android:exported="true"
    android:label="@string/app_name"
    android:theme="@style/BaseUnityGameActivityTheme"
    android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
    android:hardwareAccelerated="false"
    android:launchMode="singleTask"
    android:screenOrientation="portrait">
    <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" />
    <meta-data android:name="android.app.lib_name" android:value="game" />
</activity>

重要な設定:

  • android:theme="@style/BaseUnityGameActivityTheme" - Unity 専用テーマ
  • android:configChanges - 画面回転等で Activity を再生成しない
  • android:hardwareAccelerated="false" - Unity が OpenGL ES を直接使用するため
  • android:launchMode="singleTask" - 単一インスタンスで管理
  • meta-data - Unity プレイヤーとしての Activity 宣言

8. トラブルシューティング

8.1 AAR ファイルが見つからない

エラー: Could not find :arcore_client:

解決策:

  • settings.gradle.kts にflatDirを追加
  • repositoriesModePREFER_SETTINGSに変更

8.2 Unity クラスにアクセスできない

エラー: Cannot access 'UnityPlayerGameActivity' which is a supertype...

解決策:

  • unityLibrary/build.gradle の依存関係をimplementationからapiに変更

8.3 unity.androidNdkPath が見つからない

エラー: Could not get unknown property 'unity.androidNdkPath'

解決策:

  • getLocalProperty()関数を追加
  • local.properties にunity.androidNdkPathを設定

8.4 libil2cpp.sym.so が見つからない

エラー: IL2CPP ビルド中にファイルが見つからない

解決策:

  • ファイル存在チェックを追加(本ドキュメント 3.5 参照)

9. 生成物

ビルド成功後、以下のファイルが生成されます:

  • APK: app/build/outputs/apk/debug/app-debug.apk

含まれるネイティブライブラリ:

  • libil2cpp.so - Unity IL2CPP ランタイム
  • libunity.so - Unity エンジンコア
  • libgame.so - ゲームロジック
  • libmain.so - メインエントリーポイント
  • lib_burst_generated.so - Burst Compiler による最適化コード


最終的に添付のように、Android アプリから Unity アプリが呼び出せることが確認できました。


↑Android アプリアイコンを Tap して起動


終わり

テンプレ状態の Unity アプリと Android アプリだとしても、連携するにはややこしい設定対応が必要になりました。

これが実際の開発しているプロジェクト同士だと、もっと複雑な設定や競合が発生し、build できるまでが大変そうだなぁ。という印象を受けました。

とはいえ、ネイティブアプリに Unity アプリを組み込めるのは場合によっては強力な手段となると思うので、次は少し作り込んだアプリ同士で連携を試してみたいと思いました。


備考

※ 実案件でUnity as a Libraryを使用するのかどうか、みたいな観点は以下の記事も参考にしてみてください。

https://zenn.dev/mirrativ_blog/articles/f1e9dc9e55d43c
https://tech.cygames.co.jp/archives/3597/


※ ネイティブ完結の AR 機能等についてはこちらもよければご参照ください。
https://zenn.dev/ncdc/articles/android_ar_sample

GitHubで編集を提案
NCDC テックブログ

Discussion