本連載は、iOS/
前回までの記事でFlutterとUnityで連携するための具体的な解説は終了しています。
今回は少しでも開発を効率的に進めるための工夫を紹介を行い、本連載を締めさせていただきます。
前回までの記事をまだ読まれていない方はまず一通りお読みいただいてから今回の記事を見ていただけると幸いです。
- 4回目:FlutterとUnityを連携させる
- 3回目:Unity製のiOSアプリにFlutterを組み込む
- 2回目:Unity製のAndroidアプリにFlutterを組み込む
- 1回目:Flutter
「を」 Unity 「へ」 導入する
FlutterとUnityを連携させる開発における問題点
Flutter、Unityそれぞれの特徴として開発中の確認が容易という大きなメリットがあります。
Flutterであればホットリロード機能により書いたコードをリアルタイムに動作確認ができ、Unityもホットリロードほどではないですが専用Editorで動作確認ができるためすぐに動作を確認することが可能です。
しかし、FlutterとUnityを連携させて1つのアプリケーションを作成する場合、両者の連携をネイティブを介して行っているためFlutter、Unityお互いのコードをビルドし1つのアプリケーションになってないと確認ができないという状態になってしまいます
製品としてリリースするときはもちろん1つのアプリケーションである必要があるのですが、開発時であれば変更をすぐに確認ができる環境でなければFlutter、Unityの良さを享受できず確認するたびに両者のビルドが必須になってしまい開発効率が非常に悪くなってしまいます。
そこで、最後に少しでも開発効率を上げるために行った工夫を紹介していきます。
Flutter、Unityのプロセス間通信を行う
まず実現したいのは、FlutterはAndroidエミュレータなどで実行しホットリロードを使用し動作確認を行い、Unityは通常どおりUnityEditorで動作確認を行うことです。
冒頭で触れたとおり、前回までに紹介した方法での実装だとFlutterとUnityとのデータをネイティブを介して送り合うことになるので、FlutterとUnityがそれぞれ別プロセスとして動いている状態では当然ですがデータのやりとりを行うことができません。
ただ、考え方としては単純にFlutterとUnityでデータのやりとりを行いたいだけであり、ネイティブでやっていることはデータの橋渡しだけなので橋渡しの機能を別の実装で補ってあげれば動作としては問題ないはずです。
つまり、Androidエミュレータで動作しているFlutterプロセスと、UnityEditorで動作しているUnityプロセスで通信ができれば1つにビルドをせずにそれぞれの環境で動作させることが可能なはずです。
イメージ図
それぞれのイメージを図示化します
左右とも同じような図ですがデータの橋渡しをネイティブで行うかPCのプロセス間で行うかの違いだけです。
通信の実装
自分の環境ではもともとアプリケーションの実装時にgRPCを使用しており、FlutterにgRPCのpackageもあったのでそのままFlutter、Unity間の通信もgRPCで行いました。
とくにこれでないといけないということはないのでFlutterとUnityで動作する通信プロトコルであれば何でも大丈夫なはずです。
gRPCの実装については一般的な実装になるので実際のコードを書くのは省略しますが、Flutter側がServer、Unity側をClientとしてDuplex Streamingを行いデータのやりとりを行いました。
同じPC内のプロセス間で通信をするならServerが起動しているポート番号に対してClientから 127.
でアクセスすれば問題ないのですが、FlutterのプロセスがAndroidエミュレータで起動している場合はAndroidエミュレータ内で起動しているServerにアクセスをする必要があるためadbコマンドを使用しポートフォワードの設定が必要となります。
adb -s [エミュレータ名] forward tcp:[ポート番号] tcp:[ポート番号]
アプリ起動時に開発環境で起動しているのか、実際にビルドされたアプリケーションとして起動しているのかを判断してプロセス間通信を行うかネイティブを介して通信を行うかを切り替えることで実際にアプリビルドしなくても動作確認を行うことができます。
プロセス間通信を行ううえでの注意点
ポートフォワードの設定さえできていればFlutterとUnityで通信をするのはそこまで難しくはないのですが、FlutterのModuleプロジェクトを使用している場合に注意しなければならない点があります。
通常のFlutterプロジェクトの場合プロジェクトフォルダの中にios, androidというフォルダが存在しios/
FlutterでMethodChannelを使用しネイティブとの通信を行うためにはプロジェクト内に存在するネイティブコードにMethodChannelの受信コードが存在しなければエラーとなってしまうのでios/
しかし、FlutterModuleプロジェクトの場合ローカルビルドのためにプロジェクトフォルダが生成されるのですが .ios
, .android
という隠しフォルダの形式でプロジェクトが生成されます。
そもそも、Moduleプロジェクトの目的が既存のネイティブプロジェクトに対してModuleとして追加することを目的としているため、手元にあるプロジェクトを変更することは推奨されておらず隠しフォルダになっています。
つまり、今回のUnityプロジェクトに対してFlutterModuleを入れる想定の場合では、MethodChannelの受信コードはUnityプロジェクト側で生成されるエントリポイントに記述されており、FlutterModuleプロジェクト側には存在していないため、Moduleプロジェクト側に存在するネイティブコードにMethodChannelの受信コードが存在していなければFlutter単体で動作時にエラーが発生してしまいます。
実際にアプリで使用されるネイティブコードはUnityプロジェクト内に存在し、FlutterModuleプロジェクト側にあるネイティブコードは開発中のFlutterプロジェクト単体でビルドした時にしか使用されません。
なのでそれぞれにMethodChannelの受信コードを仕込んでおけば実際にビルドされた状態なのか、Flutter単体でコードを実行している状態なのかを判断することができ上述した通信の処理を分けるときの判定に有用だと思います。
まとめ
今回は少しでもFlutter、Unityの良さを活かすための開発環境構築のためのTipsでした。UnityとFlutterを連携することでUnityの表現力+Flutterの操作性を両立させることでユーザ体験を向上させるというのが目的ではありますが、同時に開発者にとっても開発しやすい環境かどうかも非常に重要だと思っています。
素直にビルドするよりは今回紹介した方法のほうが開発サイクルを早く回すことはできると思いますが、やはりそれぞれ単体の開発と比べるとどうしてもめんどくささがあるのでこれからも模索していくことになると思います。
今回で本連載は最終回となります。
正直なところ、あまり一般的ではないUnityにFlutterを組み込んで見るという内容でしたが少しでも何かの参考になっていれば幸いです。
これまで解説してきた内容は、技術的には可能なのですが、詳しい方が少しでも見れば保守コストが高そうだなと感じる部分も多い内容となっているかもしれません。
また、個人的にもあまり積極的に推奨するような実装ではないかなと感じる部分もありました。
それでも、さまざまな方針と実現方法の組み合わせでこうした手段もあるのだと思って、考え方の1つとその実装例として読んでいただけると幸いです。
個人的には初めてFlutterを触ってUnityと連携させるためにいろいろと調査を行いながら実装を行ったため多々躓いた部分もありました。
その中で、結果としてはこれまで曖昧な理解になっていたiOS/
最後に
最後に私が所属するココネ株式会社について簡単に紹介させていただきます。
ココネのエンジニアは、アバターサービスをはじめとした人々がつながるデジタルワールドを高い技術力で創り出すことをビジョンに掲げ、たくさんのサービスの開発を行っています。
それらの基礎となる技術はもちろん、最近話題の生成AIなども含めたものに取り組んでいます。
私たちと一緒に、こうした新技術やさまざまな技術的課題に一緒にチャレンジできる仲間を募集していますのでご興味のある方はぜひ下記をご参照ください。