Xamarin.Forms for Unityの概要の追記

前回記事の後、実装の見直しをしたのでちょっと状況が変わりました。

tan-y.hatenablog.com

Xamarin.Forms の VisualElementRenderer は標準的な手続きに従う場合、デフォルトコンストラクタでインスタンス生成ができるようにし、ExportRendererAttribute で VisualElement と VisualElementRenderer の関連付けを定義する事になっているようですが、これに従うことができないので今のところは UnityFormsApplicationActivity というクラスに集約しています。

と書きました。
これの原因は Xamarin.Forms.Internals.Registrar クラスの GetHandler メソッド

object handler = Activator.CreateInstance(handlerType);

のためだったのですが、よくよく考えたら VisualElementRenderer を MonoBehaviour 継承にしなければいいんじゃね?てことでほぼ通常の Platform 実装の構造にすることができました。現状では通常の Platform 実装と同じように ExportRendererAttribute で関連付けを指定できますので Custom Renderer もいけるはず。

ExportRendererAttribute はアセンブリ属性として定義しますが、 WebGL ビルド (IL2CPP) が実はアセンブリ属性をサポートしてないかとちよっと疑いましたが、特に問題なく動作しました。 XAML Loader が WebGL で動作しないのはこの辺かなあと思ったのですが。

VisualElementRenderer の実装クラスでは UI を描画するためのリソースを生成する処理の実装が必要になります (CreateBaseComponent をオーバーライド) 。標準の実装では予め対応を定義づけておいた Prefab からインスタンス生成します。

単純にコンストラクタで処理が記述できないのは

  • VisualElementRenderer が Unity 的には画面 (Scene) 上で管理する "GameObject" に適用する MonoBehaviour (直接的には MonoBehaviour ではなくなりましたが論理的には) であること。
  • MonoBehaviour は本体である GameObject が存在しないとインスタンス生成すらできないこと (GameObject.AddComponent メソッドを参照)
  • MonoBehaviour, GameObject は Unity.Object 継承であり、直接 new でインスタンス化できないこと

あたりが理由です。 Unity だけやってる時は「そういうものだ」で受け入れてしまってますがこういう時面倒になってきます。

描画リソース (コンポーネント) 生成に Prefab を利用しているのは、コードで生成処理を記述するのが実質不可能なための苦肉の策だったのですが、 Prefab を利用することで Custom Renderer がなくなても独自にデザインした UI コンポーネントをテンプレート的に設定できるので、このやり方は結果的によかったかなあと思います。

次の課題

NavigationPage の対応かなーというのはあるのですが、実は下記二つをどうやって対応するか悩み中。

  • ImageView
  • Opacity

「え、 Unity だから簡単じゃないの?」と思うでしょう。
Unity だからこそ逆に面倒なんですよ・・・

Xamarin.Forms for Unityの概要

技術ブログ始めてみました。

一発目として、鋭意 (?) 開発中の Xamarin.Forms for Unity の概要を解説していきたいと思います。

先日の .NET Conf 2017 Tokyo の Unconference で概要と簡単なデモを発表させてもらいましたが、駆け足だったので改めて。

Xamarin.Forms for Unity とは

Xamarin.Forms の Platform を Unity (Game Engine の方) の API で実装した (しようとしている) ものです。

Xamarin.Forms はアプリケーション開発者から見た場合、マルチプラットフォーム対応のアプリケーションフレームワークですが、内部的にはプラットフォーム共通の Core と、各プラットフォームに依存した Platform に分かれていて、実行対象としているプラットフォーム向けの Platform 実装があれば、そのプラットフォーム向けのアプリケーションが開発できます。

Xamarin.Forms では標準で Android, iOS, WinRT(UWP) などの Platform 実装が提供されています (その他にも公式 Platform はありますが割愛) が、ないなら作ってしまえば新しいプラットフォームに対応できるわけです。

・・・という感じで作っているのが Xamarin.Forms for Unity なわけです。 Xamarin とは全く無関係に勝手に作っている、 Unofficial Platform になります。

f:id:tan-y:20171028103507p:plain
Unity Editor 上での動作

Unity Editor 上ではこんな感じで動いています。

これは最近追加したサンプルの "XAMLPad もどき" です。 この XAMLPad アプリ自体の UI も XAML で記述しています。

結構 "Xamarin.Forms らしい" 記述ができるようになっているかなと思います。

やり始めた背景

Xamarin.Forms の Platform 実装を調査、勉強したかったのが第一です。
GitHub の Xamrin.Forms のソースを読みながら、 とりあえず扱いやすい Windows Forms 向けの実装をざっくり行ってみて 雰囲気を掴んだ後、それを踏まえて Unity をターゲットとして一通りの実装をすることを目標に開発を始めました。 Unity でやれば Unity の研究にもなりますしね。

加えて技術的な変化により実装を行うことが可能になってきたというのもあります。

Unity の .NET 4.6 対応

Unity 自体が .NET Framework 4.6 に対応した事が大きいです。 Unity は長い間 .NET 3.5 ベースでしたが、 Unity 5.5 くらいから段階的に .NET 4.6 のランタイムが組み込まれ、現時点でもまだ expermintal ですが正式バージョンの Unity で .NET 4.6 が選択可能になりました。

これにより Task など .NET 4 から追加された機能や様々な .NET 4 向けのライブラリ、フレームワーク (Xamarin.Forms もそうですね) が使えるようになったので、いろいろ試してみるとよいのではないかと思います。

Xamarin.Forms の Unofficial Platform 実装の解禁

Xamarin.Forms は従来 Unofficial Platform の実装は非常に困難で、 Core ライブラリ自体に手を加えるか、トリックを使う必要がありましたが 、 2.4.0 よりこの制限がなくなり正式な Core ライブラリの Pre Build バイナリを使用しての開発が可能になりました。

※結局 2.3.5 は stable にならず 2.4.0 になりました。

実際、 Xamarin.Forms for Unity も当初は NuGet から取得した DLL を組み込んで作業をしていましたが、 Unity に若干特殊なオプション指定を追加しなくてはならなかったことと、Unity でアプリケーションの Build をしようとすると必ず失敗するため、現状では Core ライブラリはソースコードで組み込んでいます。興味がある方はコミット履歴の最初の方を参照してください。

実際やってみて

結構しっかり動く

当然かもしれませんが、自分としては意外なほど普通に Xamarin.Forms の機能が動作してびっくりしました。

  • Binding
  • Layout
  • Animation
  • XAML Compiler

これらがしっかり機能していましたので、 Xamarin.Forms の流儀を Unity に持ち込む事はできそうです。
Navigation の実装ができたら Prism など MVVM フレームワークも使ってみたいですね。

XAML Compiler については動的にパースさせるのは本当はよくないので xamlc を使えるようにするのも課題です。

Unity ならではの問題

Unity はアプリケーションのランタイムに Mono を採用していて確かに .NET ベースなのですが、他の一般的な .NET 環境と比べて独特な面が多々あります。
公式の Platform 実装のコードはかなり読みましたが、公式で行っている共通的な手順に Unity では沿えない事も多々あり、そのような場合は仕方がないので独自の手法をとっています。

例えば Unity の機能を直接組み込むクラスは UnityEngine.Object というクラスを継承していないといけないわけですが、このクラスを継承したクラスは直接 new でインスタンス生成をすることができません。
Xamarin.Forms の VisualElementRenderer は標準的な手続きに従う場合、デフォルトコンストラクタでインスタンス生成ができるようにし、ExportRendererAttribute で VisualElement と VisualElementRenderer の関連付けを定義する事になっているようですが、これに従うことができないので今のところは UnityFormsApplicationActivity というクラスに集約しています。

終わりに

ドキュメントもペラ一枚程度しかないような状態ですので、とりあえずブログでちょっとずつでも書いていきたいと思います。

Xamarin.Forms か Unity のどちらかしかやってこられていない方はこれをベースにもう一方の技術にも興味もってもらえたら幸いだなあと思います。