こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • Assembly Definitionって何?
  • 使いどころが知りたい
  • コンパイルが早くなるの?
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    Assembly Definition(アセンブリディフィニション)」って聞いたことはあるけど使ったことはないという方いらっしゃるのではないでしょうか。

    Assembly Definitionを使うことでソースコード同士の依存関係を簡単に設定することができます。

    ソースコード的に絶対に依存関係を作ってはいけないものってりますよね。そのようなルールはAssembly Definitionを使うとシステムとして作ることが可能です。具体的に言うと、 意図しない依存関係を作ろうとするとエラーになる ということです。

    そこで本記事では Assembly Definition入門 ということでAssembly Definitionを使ったことがない方に向けた内容となっています。

    このような方にはピッタリの記事になっているのでぜひ最後まで読んでみてください。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    Unityエディタが作り出すアセンブリとは何か?

    Unityエディタ起動してソースコードを作成すると生まれるファイルがあります。それが「アセンブリ」です。アセンブリとはC#ソースコードをコンパイルしたDLLファイルです。

    具体的には Library/ScriptAssemblies フォルダ内にコンパイルされるたびに更新されます。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_0

    このアセンブリファイルはAssembly Definition以前は非常に大雑把な管理でした。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_1

    という形で大きなDLLファイルが作成されていたのです。

    全てのランタイムコードが含まれるためクラス間の依存関係が必然的に分離できず事故りやすい状況になっていました。またちょっとしたコード修正でも全ソースコードのコンパイルが走るため、待機時間も長くなってしまいます。

    そこでAssembly Definitionが登場したのです。

    Assembly Definitionによって何が改善されるのか?

    Assembly Definitionによって何が改善されたのかと言うと、一言で表すと 「アセンブリの分割」 です。

    今まで1つの巨大なアセンブリに詰め込まれていたソースコードが、 開発者が自由に決めることができるようになった のです。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_2

    これにより依存関係の明確化とコンパイル時間が改善されます。

    ※ある程度大きなプロジェクトにならないとコンパイル時間の恩恵は気づきづらいかもしれません

    Assembly Definitionの具体的な使い方

    実際にAssembly Definitionを使ってみないと理解しづらいため、さっそく具体的な使い方について解説していきます。既存プロジェクトへ導入を前提に話を進めていきます。

    Assembly Definitionの導入方法

    今回は既存のプロジェクトであるQRコードシステムにAssembly Definitionを導入します。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_3

    QRコードシステムにはランタイムコード(Runtimフォルダ)とEditor拡張コード(Editorフォルダ)が混在。そして外部ライブラリとしてUniTaskを使用しています。

    早速Assembly Definitionを導入します。導入のためにAssembly Definitionファイルを作成します。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_4

    メニューAssets -> Create -> Scripting -> Assembly Definition からAssembly Definitionファイルを作成しました。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_5

    ランタイムコードにAssembly Definitionファイルを追加し、QRCode.Runtimeと名付けました。

    すると次のようなUniTaskのエラーがConsoleに表示されます。

    Assets/Project/QRCode/Runtime/QRReader.cs(1,7): error CS0246: The type or namespace name 'Cysharp' could not be found (are you missing a using directive or an assembly reference?)  
    

    これは今回作成したAssembly Definitionによってアセンブリが分離し、 UniTaskへアクセスができなくなった ことを指したエラーです。

    今まで「Assembly-CSharp」にまとめられていたQRCodeが独自のアセンブリとして独立したために起きました。

    つまり UniTaskへの依存関係を作る必要が出てきた ということです。Assembly Definitionを使う際は、モジュール間の 依存関係を手動で設定 する必要があることを覚えておきましょう。

    Assembly Definition同士の依存関係の作成

    ではAssembly Definition同士の依存関係を構築する方法を解説していきます。依存関係の構築はAssembly DefinitionのInspectorウィンドウから行います。具体的にはAssembly Definition Referencesに依存したいAssembly Definitionを選択するだけです。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_6

    UniTaskに依存する場合は、Assembly Definition Referencesの「+」ボタンからUniTaskのAssembly Definitionを選択します。するとコンパイルエラーがなくなりました。無事にQRCodeがUniTaskに 依存できた ことができたということです。

    重要設定「Auto Referenced」とは?

    Assembly DefinitionのInspectorには 「Auto Referenced」 という設定があります。実はかなり重要なチェックボックスです。というのもAuto ReferencedはON/OFFで大きな違いがあるからです。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_7

    Auto ReferencedがONの状態では Assembly-CSharp、Assembly-CSharp-Editorからアクセスすることができます。 OFFにするとできません。

    つまりUnityプロジェクト内の一部の機能をAssembly Definitionで別アセンブリにしたけど、そのままアクセスを継続したい場合「Auto Referenced」のチェックが必要です。

    Auto ReferencedをOFFにするとアクセスできなくなりコンパイルエラーを起こしてしまいます。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_8

    上図の通り、自作アセンブリ「Hoge」のAuto ReferencedをOFFにすると、Assembly-CSharp、またはAssembly-CSharp-Editorからアクセスできなくなります。

    ぜひAuto Referencedの特徴をしっかりと理解しておきましょう。

    お悩みさん
    お悩みさん
    Auto Referencedは必ずONにしておけばよいのでは?

    Assembly-CSharp、またはAssembly-CSharp-Editorからアクセスしないアセンブリは 積極的に「Auto Referenced」はOFFにした方が良い です。

    理由は2つあります。

    1. 設計的にAssembly-CSharpまたはAssembly-CSharp-Editorから依存しない事を明示的に示せること
    2. Assembly-CSharpまたはAssembly-CSharp-Editorにコード修正が入った場合にコンパイルされコンパイル時間が伸びるため

    コンパイル対象から外れるとその分コンパイル時間は短縮するのです。設計的な安全性とコンパイル時間が短縮のためにも可能な限りAuto Referencedは外した方が良さそうです。

    Assembly Definitionの性質を理解しよう

    Assembly Definitionを適当に設定していると、本来アクセスしたいオブジェクトにアクセスできない、エラーが直らないといったトラブルが発生します。

    Assembly Definitionの性質、挙動は正しく理解しておく必要があるのです。

    ここでは重要なAssembly Definitionの性質を3つ紹介します。

    1.Assembly Definitionで設定したコードからAssembly-CSharpはアクセス不可

    Unityプロジェクト内で一部だけAssembly Definitionを導入するケースで起きがちなトラブルです。Assembly Definitionで独立させたは良いが、独立した側のソースコードから Assembly-CSharpにアクセスできなくて困る ケースです。

    Assembly Definitionの性質上、Assembly Definitionで独立させたコードからAssembly-CSharp内のコードにアクセスする方法はありません。設計から見直すか、Assembly Definitionをやめるかといった選択が求められるでしょう。

    今回途中からAssembly Definitionを導入を前提に話を進めていますが、 理想は開発初期から導入 しておくことをオススメします。

    2.Assembly-CSharp、Assembly-CSharp-EditorからはAssembly Definitionへアクセス可能

    もう1つの性質はAssembly-CSharp、Assembly-CSharp-Editorからは独立したAssembly Definitionのコードにアクセスできるということです。

    Assembly Definitionからはアクセスできませんが、Assembly-CSharpからはアクセスすることはぜひ覚えておいてください。

    しかしAssembly-CSharpからAssembly Definitionにアクセスできる条件があります。それは「Auto Referenced」がtrueのときだけです。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_9

    Assembly DefinitionのInspectorウィンドウ内「Auto Referenced」にチェックが入っていないとAssembly-CSharp、Assembly-CSharp-Editorからアクセスできなくなるため注意してください。

    もちろん「Auto Referenced」をOFFにした方が、参照が少なくなる分、コード修正時のコンパイル時間は短くなりやすいです。

    3.Assembly Definitionの適用範囲

    Assembly Definitionファイルがどの範囲までを同一アセンブリに含めるのか知っておく必要があります。

    結論、フォルダ配下がAssembly Definitionの対象となります。

    Assembly Definitionで依存関係の最適化!コンパイル時間も短縮?【Unity】_10

    上図の例ではRuntimeフォルダ内、Editorフォルダ内にそれぞれAssembly Definitionファイルが格納されているため、それぞれアセンブリとして分割されます。

    Assembly Definitionファイルが格納されたフ ォルダ配下が対象 になるということを覚えておきましょう。

    以上の性質を頭に入れつつAssembly Definitionと付き合っていきましょう。

    指定のソースコードがどのアセンブリに所属しているか調べる方法

    Assembly Definitionを使っていると指定のソースコードがどのアセンブリに所属しているのか確認したくなるときが出てきます。

    そんなときはリフレクションを使うと簡単に確認可能です。

    var assemblyName = typeof(SampleClass).Assembly.FullName;  
    Debug.Log(assemblyName);  
    

    上記のように Assembly.FullName でアセンブリ名を取得できます。

    Assembly Definitionまとめ

    本記事ではAssembly Definition入門として紹介してきました。記事の内容を簡単にまとめます。

    Assembly Definitionまとめ

    ①Assembly Definitionはアセンブリを分離する

    ②Assembly Definitionはフォルダ配下をまとめる

    ③Auto ReferencedはAssembly-CSharp、Assembly-CSharp-Editorからのアクセス可否を決める

    ④Assembly Definition同士で参照関係を構築して参照を解決する

    ⑤Assembly Definitionで分離した分コンパイル時間が短くなるかも

    こんな感じです。

    Assembly Definitionを使用することでソースコード内の依存関係のルール化をシステムとして導入することが可能になりました。

    これによりチーム開発のトラブルを下げることができます。また、アセンブリが分離したことでソースコードの修正によるコンパイル時間短縮に繋がります。

    小規模プロジェクトにAssembly Definitionはあまり効果を発揮しづらいですが、大規模プロジェクトになればなるほどAssembly Definitionの恩恵は大きくなるため、一度導入を検討してみてはいかがでしょうか。

    この記事があなたのゲーム開発の役に立ったら嬉しいです。

    Unityオブジェクトの描画順の制御って難しいですよね。
    この度、Unityの描画順を体系的に学べる「Unity描画順の教科書」を執筆しました。

    Unityの描画順を基礎から学びたい方はぜひ確認してみてください!
    Unity描画順の教科書

    最後まで読んでいただきありがとうございました!
    すばらしいAssembly Definitionライフをお過ごしください。

    オススメ記事
    検証環境