はじめに
Unity2017.3でAssembly Definition Filesという機能が追加されました。
これは、アセンブリを任意の単位に分割するための機能です。
Unityではコードのコンパイルはアセンブリ単位で行われるのですが、デフォルトでは基本的に単一のアセンブリ(Assembly-CSharp.csproj)しか生成されません。そのため、コードを変更するとプロジェクト内のほぼ全てのコードがコンパイルの対象になります。
例えば、ボタンを押した時の挙動を変更した場合でも、その度にライブラリのコードまでコンパイルしなおす必要があり、コンパイルに時間がかかります。さらに、プロジェクトが進むにつれコンパイル対象のコードが増え、コンパイルにかかる時間は長くなる傾向にありました。
Assembly Definition Filesではアセンブリを分割することにより、このコンパイルされる範囲を定義することができます。
つまり、上手く活用すればコンパイルにかかる時間を大幅に削減することが可能になるのです。
Assembly Definition Files
繰り返しになりますが、UnityはデフォルトでAssembly-CSharp.csprojというアセンブリを生成します。
これはAssets以下の全てのコードを含みます。(ただしPluginsやEditorなど一部の特殊なフォルダを除く(後述))
Assembly Definition Filesを利用すると、これ細かい単位に分割することができます。

画像の引用元:https://blogs.unity3d.com/wp-content/uploads/2017/10/Script-Compilation-Assembly-Definition-Files-manual-edition.pdf
使い方
利用方法は非常に簡単で、分割したいコードを含むルートディレクトリにファイルを追加するだけです。
まずはスクリプトに現在適用されているアセンブリを確認してみましょう。
Projectビューでスクリプトを選択すると、Inspecterビューで確認することができます。
デフォルトで生成されるAssembly-CSharp.csprojが適用されていることが分かります。
Assembly Definition Filesの作成
Projectビューで分割したいコードを含むルートディレクトリを開き、CreateからAssembly Definitionを選択します。右クリックからのCreate>Assembly Definitionとかでも大丈夫です。
ファイルに任意の名前をつけます。ここで指定した名前がアセンブリ名になります。他のアセンブリと重複してはいけない点に注意してください。
ファイル名とInspecterで確認できるNameは別物なので、後から変更する場合は気をつけてください。生成時にファイル名をNameが設定されるだけで、アセンブリを定義するのはNameの方です。
Inspecterでスクリプトを選択すると、先ほど作成したアセンブリが適用されていることが確認できると思います。
参照関係の定義
注意したいのは、アセンブリを分割すると全体のコンパイル時間は短くなりますが、クラス間の参照は原則同じアセンブリ内でしか行えません。
そこで、他のアセンブリのクラスを参照するためには、参照関係を定義する必要があります。Assembly Definition Filesを選択した状態で、InspecterからReferencesに参照したいアセンブリを追加してください。
上記は、先ほど作成したスクリプトから、UniRxを参照するための例です。
(UniRx 6.1.2ではAssembly Definition Filesが既に定義されています)
なお、Assembly-CSharp.csprojでは全てのアセンブリが自動的に参照されてるため、どのクラスでも参照することが可能です。
_`Assembly-CSharp.csproj`の一部のスクショ_
また、循環参照(双方向への参照)はできないようなので注意してください。
コンパイルの範囲
コンパイルはアセンブリ単位で行われると言いましたが、参照先に変更があった場合、全ての参照元もコンパイルの対象に含まれます。
こちらの図をご覧ください。矢印は他のアセンブリへの参照関係を表しています。
例えばMain.dllは他のアセンブリから参照されていません。そのため、Main.dllに変更が加わっても、Main.dllのみがコンパイルの対象となります。
次にThirdParty.dllをみてください。ThirdParty.dllはMain.dllに参照されています。(Main.dllのReferencesにThirdParty.dllが指定されている状態)
そのため、ThirdParty.dllに変更が加わった場合のコンパイルの範囲は、ThirdParty.dllだけでなくMain.dllにも及ぶことになります。
イメージとしては、上流に変更があった場合、下流にも影響が及ぶといった感じです。CanvasGroupと同じような感じです。
通常プロジェクトで最も頻繁に変更が発生するのは下流のMain.dllに当たる部分だと思うので、その部分を小さくすることでコンパイル時間が減っていきます。しかし、アセンブリの分割には参照が一方向になっている必要があります。そのため、コンパイル時間を最適化するためには、疎結合な設計にしておくことが非常に重要です。
特殊なフォルダ(PluginsやEditor)について
Unityでは一部のフォルダは特殊な扱いとなり、事前定義されたアセンブリが適用されています。
特殊フォルダーとスクリプトのコンパイル順 - Unity マニュアル
Unity gives priority to the assembly definition files over the [Predefined Compilation System(ScriptCompileOrderFolders).
Script-Compilation-Assembly-Definition-Files-manual-edition.pdf
しかしマニュアルによると、**「あらかじめ定義されている特殊フォルダのコンパイル順よりも、Assembly Definition Filesが優先される」**という記述があります。つまり、PluginsやEditorが意図通り動作しなくなる可能性があります。
Pluginsはおそらく問題ないのですが、Editor以下は他のプラットフォームではコンパイルされると困る場合が多いかと思います。
そこで、Assembly Definition Filesのプラットフォーム設定を利用した方法で上記の問題を回避します。EditorフォルダにAssembly Definition Filesを追加し、PlatformsのInclude PlatformsでEditorのみを指定した状態にしてください。
注意して欲しいのが、デフォルトのAny Platformにチェックが入っている状態だとラベルがExclude Platformsになっています。Include Platformsを設定する場合はAny Platformをオフにする必要があるので、気をつけてください。
Any Platformがオンの状態で、Editor以外のプラットフォームを全て選択する方法でも大丈夫かと思います。
TestRunnerへの適用
TestRunnerを利用している場合にも注意が必要です。
マニュアルによると、TestRunnerの動作には以下のいずれかを満たす必要があります。
- Editorフォルダ以下に入れる
-
test assembliesを参照したアセンブリに含める
Unity Test Runner - Unity マニュアル
ただし前者は他のAssembly Definition Files以下にある場合動作しないため、注意してください。
後者の場合は、アセンブリを選択してUnity ReferencesのTest Assembliesにチェックを入れればokです。
ちなみにPlayModeの場合は上記のみでokですが、EditModeで動作させたい場合はEditor-onlyのアセンブリである必要があります。つまり、上記のような設定になるかと思います。
ベストプラクティス?
It is highly recommended that you use assembly definition files for all the scripts in the Project, or not at all.
プロジェクトの全てのスクリプトにAssembly Definition Filesを使用することか、全く使用しないことが推奨されているようです。
(訳間違ってたら教えてください)
動作確認環境
- Mac (macOS 10.13.6)
- Unity2018.2.1f1
参考
- 【Unity】Assembly Definition Filesという神機能 - テラシュールブログ
- 【Unity】Unity2017.3で追加されたAssembly Definitionをちょっと触ってみた - Qiita
- Unity Test Runner - Unity マニュアル
最後に
間違ってる箇所があれば是非ご指摘ください。


