はじめに
この記事はUnityゆるふわサマーアドベントカレンダー 2018の7日目の記事になります。
6日目の記事は@Nitudonさんの「IncrementalCompiler時代のUnityC#Tips(C#7.2)」でした!
私の記事では、Assembly Definition Files を使う上での地雷ポイントをダラダラと書き殴ってみたいと思います。
🤔 Assembly Definition Files is 何?
もの凄く粗く言うと「Unity における C# スクリプトのコンパイル単位を分割する機能」って感じです。
詳しくは Gotanda.unity #7 にて LT をした際の資料がありますので、そちらをご参照くださいませ。
また、テラシュールブログさんの記事もとても分かりやすいので、あわせて紹介しておきます。
💣 地雷ポイント
この記事では、上記 LT 資料の後半に書いた内容を深掘りしてみたいと思います。
1: AssetBundle と組み合わせるとヤヤコシイ
Scene / Prefab / ScriptableObject などを AssetBundle から読み込んでいる場合に、それらの Asset から参照されるスクリプトを Assembly Definition Files 対応する場合は、AssetBundle の再構築が必須になります。
詳しい顛末はこちらの記事にまとめてありますので、ご参照くださいませ。
2: static メソッドを用いている場合に参照の追加でハマりがち
そもそも Assembly Definition Files を適用した場合、 .asmdef
ファイルの単位で dll が分割されることになるので、そのままでは dll 外のクラスなどが参照できなくなります。
この問題を回避するために、References という機能が用意されており、「この .asmdef
から生成される dll は、References に定義されている .asmdef
から生成される dll 達を参照する」という設定を施すことができます。
この機能を設定する必要がある時というのは The type or namespace name 'FooBar' could not be found. Are you missing an assembly reference?
といった類いのコンパイルエラーが出るので、その型を含む .asmdef
を探して References に追加するワケです。
で、この節での地雷ポイントとしては「親クラスで定義されている static メソッドを用いる場合には親クラスが属する .asmdef
も References に追加する必要がある」という点になります。
例えば以下のようなコード群があるとします。
// Parent.cs (A.asmdef) public abstract class Parent { public static void Foo() { // Do something } } // Child.cs (B.asmdef) public class Child : Parent { } // Usage.cs (C.asmdef) public class Usage { public void Bar() { Child.Foo(); // ココで、本来は Parent のメソッドである Foo() を Child 経由でコールしている } }
このような構造の場合、 B.asmdef
から A.asmdef
を参照し、 C.asmdef
から B.asmdef
を参照するだけで問題無さそうにも見えますが、実際には Usage.cs
から Parent.cs
に含まれる static メソッドを実行しているため C.asmdef
からは B.asmdef
に加えて A.asmdef
も参照しないと NG というコトになります。
static が不必要に濫用されているプロジェクトの場合、この辺の管理が相当大変になることが予想されます。
3: partial クラスが使いづらくなる
元々 C# には partial という仕組みがあり、あるクラスの定義を複数のファイルに分割定義することができます。
// Foo1.cs public partial class Foo { public void Bar() { } } // Foo2.cs public partial class Foo { public void Baz() { this.Bar(); } }
この分割されたクラスを別々の Assembly として定義し、dll を分割した場合にはお互いに参照ができなくなるため、たとえ References の設定をしていたとしても this.Bar();
の行でコンパイルエラーが発生します。
partial は、Assembly の中での分割定義をサポートする機能に過ぎないため、Assembly Definition Files を用いる時点で殆どその機能を有効に使えなくなると言えます。
4: IDE のサポートが薄い
Visual Studio や JetBrains Rider などの IDE 側のサポートがまだ薄いため、2018年8月上旬時点では「自動的に import
を追加する際に、 .asmdef
も修正する」といった気の利いた機能がありません。*1
毎回 Unity Editor と IDE とを行き来するのがちょっとだけ面倒だったりするので、IDE 側の手厚いサポートが待たれますね。*2
5: 時々「全スクリプトのコンパイルが無効になる」などの謎エラーが発生する
原因がサッパリ分からないのですが、時々以下のような現象が発生します。
- 全ての Scene, Prefab, ScriptableObject が参照するスクリプトが Missing になる
-batchmode
引数を付けてコマンドラインから起動する際に-executeMethod
で指定するクラスを見つけてくれなくなる
これらの現象が発生すると、マシンを再起動するか別のバージョンの Editor で開き直すかをしないと現象が改善しません。*3
Library/ScriptAssemblies/
以下を全消ししても改善しないことから、マシンレベルで何らかのキャッシュをしてしまっているものと思われますが、今の所詳細は不明です。
🎉まとめ
適切に使えばコンパイル時間が短くなるなどのメリットがある Assembly Definition Files ですが、未だに多少の問題点が残っていたり、これまで使えていたハックが使えなくなる可能性があったりします。
各プロジェクト毎に、「導入すべきかどうか?」は適宜判断する必要がありそうです。
さて、Unityゆるふわサマーアドベントカレンダー 2018の8日目は@baba_s_さんの「TextMesh Pro で文字列中に表示したい画像を Sprite Atlas に簡単にまとめられる「Simple Sprite Packer」紹介」です。楽しみですね!