Flutter「を」Unity製Androidアプリ「へ」
本連載は、iOS/
前回の記事で、なぜUnity製アプリにFlutterを導入するのかという話をしているのでまだ見ていない方はそちらも合わせて読んでいただけると幸いです。
Unity製のアプリにFlutterを組み込むためにはいくつかの手順が必要となるので今回から複数回にわたり実際のコードを交えながら解説していきます。
2回目となる本記事はUnity製のAndroidアプリにFlutterを組み込む方法の解説となります。
なお、本連載はUnityとFlutterの連携がメインとなるのでUnityとFlutterの基本的な部分の説明は省略させていただきます。
開発環境
具体的な話に入る前にまずは今連載を通して使用するマシンやソフトウェアのバーションを紹介します。
OS(macOS:チップはApple M3 Pro)
$ sw_vers
ProductName: macOS
ProductVersion: 14.4.1
BuildVersion: 23E224
Unity
m_EditorVersion: 2022.3.24f1
m_EditorVersionWithRevision: 2022.3.24f1 (334eb2a0b267)
Flutter
$ flutter --version
Flutter 3.19.5 • channel stable • ssh://git@github.com/flutter/flutter.git
Framework • revision 300451adae (3 weeks ago) • 2024-03-27 21:54:07 -0500
Engine • revision e76c956498
Tools • Dart 3.3.3 • DevTools 2.31.1
Android Studio
Android Studio Iguana | 2023.2.1 Patch 2
Build #AI-232.10300.40.2321.11668458, built on April 4, 2024
Runtime version: 17.0.9+0-17.0.9b1087.7-11185874 aarch64
Xcode
Version 15.3 (15E204a)
各OSとUnity、Flutterの関係性
具体的な実装の話に入る前に、各OSでアプリ、Unity、Flutterがどのような関係になっているかを簡単に見ていきます。
通常であればAndroidもiOSもアプリの実行時にあらかじめデザインされたレイアウトをViewとして画面上に表示を行っています。
Unity/
つまり、内部的に起こっていることとしてはアプリを起動したときにUnityEngine/
ですから、UnityアプリでFlutterを動かすために必要なことはUnityからExportされたプロジェクトにFlutterを追加、アプリの初期化時にFlutterEngineを初期化しFlutter用のViewを生成することでUnityEngineとFlutterEngine両方が動くアプリができることになります。
Unity製AndroidアプリにFlutterを組み込む
では具体的な実装に入っていきます。
簡略的にはなりますが、手順としては以下となります。
- FlutterModuleプロジェクトを作成
- UnityプロジェクトからAndroid
(Gradle) プロジェクトをExport - ExportされたAndroid
(Gradle) プロジェクトに設定を追加 - 起動Activityクラスを作成しFlutterEngineを初期化しViewを作成
ここからは以下のようなフォルダ構成で進めていきます。
Androidビルド時にパスでFlutterプロジェクトを参照しているのでフォルダ構成が違うとうまくビルドできないので気をつけてください。
root(任意のworkフォルダ)/
├ unity/
├ module/
└ builds
└ android/
unity: Unityプロジェクトフォルダ。プロジェクト名は任意で問題なし。
module: Flutterプロジェクトフォルダ。Androidビルド時にパス参照されているので名前は設定と一致させる必要がある。
builds: UnityからのExport先。
1. FlutterModuleプロジェクトを作成
Unityプロジェクトに組み込むためのFlutterプロジェクトを作成します。
基本的にはFlutter公式のAdd-to-appのページの手順を実行していくだけになります。
flutter create
コマンドに -t module
オプションを付けることでモジュールプロジェクトを作成することができます。
--org com.
はパッケージ名、末尾のmoduleはプロジェクト名なので自由につけて大丈夫ですがこの連載ではcom.
$ flutter create -t module --org com.example module
$ flutter pub get
詳しくは以下ページを参照してください。
次に通常のプロジェクトとモジュールプロジェクトの違いについて説明します。
通常のプロジェクトであればプロジェクト作成するとフォルダ内にandroid、iosといった各プラットフォームのネイティブプロジェクトのフォルダが生成されそのプロジェクトを使用してビルドされることになります。
モジュールプロジェクトの場合は直接アプリをビルドすることを意図していないのでandroid/
プロジェクト作成後に flutter pub get
などのコマンドを実行するとプロジェクトフォルダ内に .android
、.ios
という隠しフォルダとしてプロジェクトフォルダが生成されます。
既存プロジェクトにflutterを組み込む際にこの隠しフォルダのプロジェクトが使われることになります。
2. UnityプロジェクトからAndroid(Gradle)プロジェクトをExport
Unity側の設定は、ScriptingBackendを IL2CPP
に設定し、Target Architectures で ARM64
を有効にします。
BuildSettingsの Export Project
にチェックを付けてExportを実行してください。
Export時に出力先を指定する必要があるため上述した、root/
を指定してください。
3. ExportされたAndroid(Gradle)プロジェクトに設定を追加
UnityからExportされたAndroidプロジェクトに手動でFlutter依存を追加していきます。
UnityからExportされたAndroidプロジェクトをAndroidStudioで開くと以下のような構造になっていると思います。
AndroidはGradle
android/
、android/
、android/
の3つに設定を追加していきます。
android/gradle.properties(プロジェクト全体の設定)
org.gradle.jvmargs=-Xmx4096M
org.gradle.parallel=true
unityStreamingAssets=
unityTemplateVersion=5
+// Flutter側でAndroidXを使用している
+android.useAndroidX=true
+// Flutter側にアプリのホスト名を知らせる必要がある
+flutter.hostAppProjectName=launcher
android/settings.gradle(gradleのマルチプロジェクト設定。このプロジェクトがunityLibraryとflutterに依存していることが記述される)
include ':launcher', ':unityLibrary'
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
google()
mavenCentral()
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
+ // 依存解決のためにflutter repositoryを追加
+ maven {
+ url 'https://storage.googleapis.com/download.flutter.io'
+ }
}
}
+// Localに存在するFlutterModuleプロジェクトをパス指定でプロジェクトに組み込み
+setBinding(new Binding([gradle: this]))
+evaluate(new File(
+ settingsDir.parentFile,
+ '../module/.android/include_flutter.groovy'
+))
android/unityLibrary/build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
+ // unityLibraryにからFlutterを参照できるように追加
+ implementation project(path: ':flutter')
}
4. 起動Activityクラスを作成しFlutterEngineを初期化しViewを作成
通常のUnityアプリであれば起動クラスはUnity側で生成されたものを使用しますが、そこにFlutterの処理を追加したいので自分で用意し起動時にこのクラスが呼ばれるように設定します。
自分で用意すると言っても基本的にはUnityが生成している com.
のクラスをベースにFlutterのために必要な処理を追加しただけです。
ソースコードの全体を書くと長くなってしまうので、記事では編集部分のみを紹介します。ソースコードは以下リンクよりDLしてください。
ソースコードの編集準備
android/
に com.
Packageを追加し、 android/
に MainActivity.
UnityPlayerActivityとの差分を見てもらうと何が追加されているのかがわかりやすいと思います。
それでは、すべてを解説すると長くなってしまうので初期化部分の重要なところだけを抜粋して解説していきます。
FlutterEngineの生成
Engineの生成でポイントとなるのが executeDartEntrypoint
です。
サンプルではdefault値をセットしていますが、default値は main()
になります。
Flutter側のサンプルを見るとエントリポイントとして main()
が存在しこの関数をエントリポイントとして指定しています。
private static final String FLUTTER_ENGINE_ID = "engine_1";
~~~
// FlutterEngineのインスタンスを生成
flutterEngine = new FlutterEngine(this);
// FlutterFragment生成時にCacheEngineを使用するのでCacheに登録しておく、FLUTTER_ENGINE_IDを使用してEngineを取得できる。
FlutterEngineCache.getInstance().put(FLUTTER_ENGINE_ID, flutterEngine);
// Flutter側のエントリポイントを指定。default = main()
flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
FlutterPluginのための設定
Flutter側に何のPackageも追加せずに使用するなら不要かもしれませんが、Flutterプロジェクトに追加でPackageを入れる場合この処理が必要になります。
// FlutterのPlugin管理のためEngineをセット
GeneratedPluginRegistrant.registerWith(flutterEngine);
FlutterViewを作成
既存アプリにFlutterを組み込み表示するためにはいくつかの選択肢がありますが今回はFragmentとして表示することにしました。
そのためUnityPlayerActivityではただのActivityが継承されていますが、FragmentActivityに変更しています。
詳細や他の選択肢に関しては公式ページを参照してください。
https://
FragmentActivityを継承することで使用することができる getSupportFragmentManager
でFragmentManagerを取得し、FlutterFragmentViewを生成、表示を行っています。
// FragmentActivityを継承するように変更
public class MainActivity extends FragmentActivity implements IUnityPlayerLifecycleEvents {
private static final String TAG_FLUTTER_FRAGMENT = "flutter_module";
~~~
// CacheしてあるEngineを使用しFlutterFragmentを作成する
flutterFragment = FlutterFragment.withCachedEngine(FLUTTER_ENGINE_ID).renderMode(RenderMode.surface).transparencyMode(TransparencyMode.transparent).build();
// FragmentManagerを使用し実際にViewを表示
getSupportFragmentManager().beginTransaction().add(rootViewId, flutterFragment, TAG_FLUTTER_FRAGMENT).commit();
~~~
}
AndroidManifest.xmlで起動クラスを変更
android/
に記述してある com.
を com.
に変更します。
これで先ほど作成したMainActivityが、アプリ起動時に呼び出されることになります。
この状態でアプリをビルドするとUnityプロジェクトにFlutterを追加された状態でビルドされ、初期化時にFlutter表示を行っているので起動するとFlutterの画面が表示されるはずです!
ビルド自動化
UnityからAndroidプロジェクトをExportしているのでUnity側を変更し、ビルドし直すたびにプロジェクトに変更がかかってしまいます。
なのでUnityプロジェクトにFlutterを組み込むにはビルドの自動化が非常に重要になってきますので自動化の知見も少し紹介します。
起動Activityクラスを追加
MainActivity.
のファイルをUnityの Assets/
に追加するだけで自動でAndroidプロジェクトに取りこまれます。
Gradleの設定追加
UnityにはAndroidManifestの設定やGradle設定を変更できる機能があるのでそこにFlutterの設定をあらかじめ設定しておきます。
PlayerSettings -> Android -> Android publishing
に各設定ファイルのTemplateを作成し任意の設定を追加できる機能があるので、必要な設定ファイルにチェックを入れFlutterの設定を記述すればExportした際にFlutterの設定が入った状態でプロジェクトが生成されます。
まとめ
かなり駆け足になってしまいましたがUnityからExportしたAndroidプロジェクトにFlutterを組み込む方法を紹介しました。
基本的には公式の Flutter Add-to-app
に書かれている既存アプリにFlutterを入れる方法をUnityからExportされたプロジェクトに対して実行しているだけとなります。
私自身がメインはUnityでAndroid/
冒頭の方でも書きましたが、UnityEngineとFlutterEngineがそれぞれのViewを制御していて、アプリ側は各EngineとViewの初期化とOS側のイベントを各Engineに通知しているだけです。
この関係を正しく理解できているとコードも読みやすいと思いますしそこまで難しいことはやっていないことがわかると思います。
次回はUnityからビルドされたiOS