プロジェクトが中〜大規模化するにつれて、C#コード全体のコンパイル時間を劇的に短縮する「Assembly Definition (asmdef/アセンブリ定義)」の導入はほぼ必須のワークフローとなります。しかし、asmdefファイルを各フォルダに配置した途端、それまで完璧に動作していたカスタムインスペクターやEditorWindowなどの自作エディタ拡張クラスが「UnityEditor 名前空間が見つかりません」「Editor クラスを継承できません」などの大量のコンパイルエラーを出してビルドに失敗するという罠に多くの開発者が直面します。本記事では、このビルド崩壊が発生する内部的なアセンブリ境界の仕組みと、初心者にも分かりやすい例え話、そしてコンパイルエラーを完全に解決するための正しいアセンブリ設計をステップバイステップで徹底解説します。
1. 例え話で理解する:『輸出用アタッシュケース』と『工場専用の大型工具』
この「asmdef導入によるEditorコードのコンパイルエラー」を直感的に理解するために、「海外へ輸出する商品の箱詰めと、工場専用の特殊な工具」を例えに考えてみましょう。
あなたの工場(プロジェクト)では、出荷する「製品の部品(ランタイムコード)」と、工場の中で製品を組み立てたり検査したりするときにしか使わない「大型クレーンや特殊ドライバー(Editor拡張コード)」が、同じ棚(同じフォルダ)に混ざって置かれていました。
ある日、物流の効率を上げるために、部門ごとに専用のアタッシュケース(Assembly Definition)を用意して、製品アセットを綺麗に分類(モジュール化)して海外(実機ビルド)へ出荷することにしました。しかし、製品用のケースの中に、工場専用の大型工具(UnityEditorのAPI)を間違えて紛れ込ませてしまったのです。
アタッシュケースが税関(ビルドプロセス)に届いた際、税関職員は「おい!このケースは一般の街中(スマートフォンやゲーム機)へ出荷するものなのに、一般の人は使えない(実機には存在しない)工場専用の巨大なクレーンが同封されているぞ!密輸(コンパイルエラー)だ!出荷を即座に差し止める!」と判断します。これが、ビルド時に「UnityEditor が見つからない」と怒られるバグの正体です。工場専用の工具は、工場(エディタ環境)の中に置いておくための「別の専用ケース(Editor用asmdef)」に厳密に隔離し、輸出用ケースとは完全に区別してパッキングしなければなりません。
2. 根本的な原因:ランタイムとエディタのアセンブリ境界の壁
技術的に言うと、Unityのビルドシステムは製品バイナリ(実機用アセンブリ)を作成する際、UnityEditor 名前空間や UnityEditor.dll に含まれるエディタ専用機能のコードを完全にストリップ(排除)します。実機デバイス上にはエディタのエンジンが存在しないためです。
asmdefを導入する前は、Unityはプロジェクト内のすべてのスクリプトを暗黙的に Assembly-CSharp.dll という単一の巨大アセンブリに統合してコンパイルしていました。このとき、Editor フォルダ以下のスクリプトは自動的にエディタ専用の別アセンブリ(Assembly-CSharp-Editor.dll)に自動分類されてコンパイルされていたため、参照エラーが防げていました。
しかし、特定のフォルダ(例:MyFeature)に MyFeature.asmdef を配置すると、自動分類のルールが破綻します。そのフォルダ以下にあるすべてのスクリプト(Editorフォルダ内のものも含めて)が MyFeature アセンブリ(実機にも含まれるランタイムアセンブリ)の所属として扱われてしまうのです。これにより、実機コードの中に UnityEditor のコードが混入し、税関(コンパイラ)で弾かれることになります。
もう一つの問題として、アセンブリが分離されたことにより、新しく作った MyFeature.Editor アセンブリから、ランタイム側の MyFeature アセンブリのクラス(MonoBehaviourなど)を認識できなくなる「参照スコープの壁」も発生します。
3. 解決法:アセンブリ定義の正しい分離設定
このアセンブリ境界エラーを解消するためには、ランタイム用とエディタ用の2つのasmdefファイルを適切に作成・設定する必要があります。
手順1:フォルダ構成の整理とEditor用asmdefの作成
機能フォルダ内に、ランタイムコード用のフォルダと、エディタ拡張用の Editor フォルダを明確に分けます。
Assets/
└── MyFeature/
├── MyFeature.asmdef (ランタイム用アセンブリ定義)
├── PlayerMovement.cs (製品用コード)
└── Editor/
├── MyFeature.Editor.asmdef (エディタ専用アセンブリ定義) ★新規追加
└── MovementEditor.cs (エディタ拡張コード)
※ Editor フォルダの直下に、新しく MyFeature.Editor.asmdef を作成します。
手順2:Editor用asmdefのプラットフォーム制限の設定
- 作成した
MyFeature.Editor.asmdefアセットをインスペクターで開きます。 - Platforms セクションで、「Any Platform」から 「Include Platforms」 に変更します。
- リスト内の `Editor` のみにチェックを入れます(他のプラットフォーム、AndroidやiOS等のチェックはすべて外します)。
※これにより、このアセンブリ内のスクリプトはUnityエディタ上でのみコンパイルされ、実機ビルド時にはコンパイル対象から自動的に100%除外されるようになります。
手順3:アセンブリ参照(Assembly Definition References)の追加
Editorアセンブリは、ランタイムアセンブリにあるクラス(PlayerMovement.csなど)を参照してカスタムインスペクター等を描画するため、参照リンクを貼る必要があります。
MyFeature.Editor.asmdefのインスペクターを開きます。- Assembly Definition References の項目を展開し、`+` ボタンを押します。
- 入力スロットに、ランタイム用アセンブリである `MyFeature` を指定して追加します。
- 「Apply」 ボタンを押して設定を適用します。
[MyFeature.Editor アセンブリ設定]
▼ Platforms
☑ Editor (エディタ環境のみ有効化)
▼ Assembly Definition References
[+] MyFeature (ランタイム用アセンブリを参照)
4. アセンブリ分割設計の実務ベストプラクティス
プロジェクトのアセンブリ構造を健全に保つためのチェック項目です。
- 条件付きコンパイル(#if UNITY_EDITOR)との使い分け: スクリプトが数ファイルしかない小規模な機能では、わざわざEditorフォルダを作ってasmdefを分けるよりも、コード内に
#if UNITY_EDITOR〜#endifを記述してエディタAPIを囲むほうがシンプルで迅速な場合があります。プロジェクト全体の規模感に合わせてアセンブリの分割粒度を設計してください。 - Auto Referencingの無効化: 新しく作ったasmdefアセットの Auto Referenced オプションをOFFにすることで、不要なアセンブリ(例:テストコード用アセンブリなど)が暗黙的にプロジェクト全体から自動参照されるのを防ぎ、コンパイル速度をさらに向上させることができます。