← ブログへ

Editor は動くのにビルドが落ちる:Unity MCP で Console から参照問題を特定する

Unity で「エディターは正常、実機/Player ビルド失敗やシーン入場時 NRE」のとき:A/B/C で層分けし、Cursor + Unity MCP で Console を読み、Build Settings とシリアライズ参照を照合。スタック例、手動チェック、最小修正付き。

公開日
  • Unity MCP
  • Unity
  • Console
  • build
  • Android
  • Cursor
  • troubleshooting
  • AI game development

「Unity works in editor, fails on device / build」系のスレッドはいつも同じ:ローカル Play は正常、Player / 実機ビルド するとビルド赤字、クラッシュ、黒画面、またはシーン入場直後に NullReferenceException。エンジンが壊れたわけではなく、エディター経路が参照とプラットフォーム差を隠していることが多い——シーンがパッケージに入っていない、ディスク上のシリアライズフィールドが None、パス大文字小文字、UnityEditor API がランタイムアセンブリに漏れてパック時に爆発、など。

本稿の主軸は 3 つだけ(タイトルと一致):

  1. Console / スタック → スクリプトとシーンオブジェクトを特定
  2. Build Settings、Missing Script、シリアライズ参照を照合
  3. Unity MCP で Cursor 内読み取り専用診断 → 確認後に最小修正

プラグインインストールやストア出荷の全手順は繰り返しません。IL2CPP stripping やネイティブプラグインは文末の拡張へ。

関連記事:

まず層を切り分ける

A. ビルドウィンドウ赤字(コンパイル / パック段階の失敗)
        ↓
B. パッケージは作れるが、実機起動でクラッシュ / 黒画面
        ↓
C. ゲームは入れるが、特定シーンや機能で実行時に落ちる
見える症状層本稿で先に見る
Build 失敗、error CS… / 型が見つからないAランタイムアセンブリが UnityEditor を誤参照、条件コンパイル、asmdef
起動シーン入場直後 NRE / MissingReferenceB/Cシーンがビルドに入っているか、Missing Script、Inspector の None
特定の Resources.Load / アドティブシーンでのみCパス大文字小文字、リソースがパッケージ内か、シーンがロード済みか

Unity MCP は開いているエディターが得意:Console、Hierarchy、コンポーネントフィールド。実機の logcat / Xcode スタックは Cursor に貼る必要あり。または先に Development Build を打ち、可能なら Editor で同じ起動シーンを再現。

トラブルシュートループ(手動 + MCP)

失敗のたびに順番に。手動を飛ばして AI に Player を書き換えさせない。

ステップ 0:手動で現場を固定(約 2 分)

Cursor に頼る前に自分で:

  1. Unity バージョン、ターゲット(例 Android / IL2CPP)をメモ
  2. Window → General → Console をクリアし、Build または Development Build を 1 回
  3. 最初の関連 Error(スタック付き)をコピー——Warning の壁だけは不可
  4. File → Build Settings を開く:疑いのシーンにチェックがあるか、リスト 0 番が起動シーンか
  5. Prefab を編集した場合:インスタンスで Apply したか(未 Apply のオーバーライドは他人のパッケージに入らず、想定ディスク資産にも入らないことが多い)

ステップ 1:MCP 読み取り専用で Console 分類

今回のビルド / 直前の Play に関連する Console の Error を要約(Info は無視)。
「コンパイルエラー / Missing Script / NullReference / リソースロード」に分類。
各エラーについてスクリプトパス、行番号、GameObject 名(ログにあれば)を列挙。
アセットは一切変更しない。

ステップ 2:手動 + MCP で「参照三つセット」を照合

ランタイム NRE(B/C)では、エディターでスタックのオブジェクトを開き、MCP でクロスチェック:

手動MCP プロンプト
Build Settings シーン一覧(スクショまたは口述)「Build Settings のシーンパスと有効フラグを列挙;起動シーンを指摘。」
疑いオブジェクトを選択、Inspector で Missing Script / None 参照「シーン <Name> で Missing Script を列挙;<Object> の <Component> の各シリアライズフィールドが None か確認。表のみ。編集しない。」
Prefab モード vs シーンインスタンスの一致「<X> は Prefab 資産かシーンインスタンスか;未 Apply オーバーライドがあれば報告(見える範囲)。報告のみ。」

原則: パックに使われるのはディスク上のシーン/Prefab。Editor で OnValidate が一時的に埋めて「見えている」参照はカウントしない——MCP/Inspector のシリアライズ結果を信頼。

ステップ 3:診断テンプレート、確認後に変更

【制約】
- 先に診断と最小案;私の確認後に変更
- 広範な GameObject.Find で修復を偽装しない
- この Error に直接関係するスクリプト/Prefab/シーンのみ触る

【現場】
- Editor Play:正常 / 異常(事実どおり)
- プラットフォームとビルド:…
- Build Settings 起動シーン:…
- Console / デバイススタック(原文):
<貼り付け>

【回答してほしいこと】
1. A / B / C のどの層か
2. 最もありそうな根因 1~2 個(未パッケージ / None 参照 / Missing Script / 大文字小文字 / Editor API…)
3. 私が確認すべきオブジェクトとフィールド名
4. 最小修正手順(まだ実行しない)

ステップ 4:確認後の最小書き込み + 再確認

確認済み案で最小修正:この Error に関係する <オブジェクト.フィールド> または <スクリプトパス> の数行のみ。
シーン/Prefab を保存。
その後読み取り専用で再確認:関連フィールドはまだ None か;Console に同種 Error は残っているか。

完全例:NRE スタックからフィールドへ

以下はリアルに近いデバイス/Player ログ(構造は典型的;パスはプロジェクトに合わせて置換)。

ログ

NullReferenceException: Object reference not set to an instance of an object
  at HudHealthView.HandleHealthChanged (System.Single current, System.Single max) [0x00000] in Assets/Scripts/UI/HudHealthView.cs:42
  at PlayerHealth.TakeDamage (System.Single amount) [0x00000] in Assets/Scripts/Combat/PlayerHealth.cs:28
  at DebugDealDamage.Update () [0x00000] in Assets/Scripts/Debug/DebugDealDamage.cs:15

層判定

  • Play/パッケージで Update まで到達 → A ではない(コンパイル済み)
  • ダメージで即落ち → C(ランタイム);疑いは UI 参照、「戦闘の書き換え」ではない

手動

  1. Build Settings に HUD のシーンが含まれるか
  2. シーンを開き HudHealthView のオブジェクトを選択、healthFill / playerHealth が None か
  3. Prefab で変更した場合:Apply したか

MCP へ

読み取り専用:HUD を含むシーンを開く。
1. HudHealthView が付いたオブジェクトを探す
2. healthFill、playerHealth(および同種)が None / Missing か報告
3. 変更しない
結果が None なら:最小案は参照を再指定して保存——TakeDamage ロジックの変更ではない。

最小修正(確認後)

  • HUD_HealthFill の Image を healthFill に、Player の PlayerHealth を playerHealth にドラッグ
  • シーン保存;Development Build で再度ダメージ

誤った「修正」(やらない)

// 反例:Find で None を隠す —— 実機で名前変更やアドティブロード後も落ちる
healthFill = GameObject.Find("HUD_HealthFill").GetComponent<Image>();

正しい配線は HUD → HP イベント。本稿が求めるのは:スタックでフィールドを固定してから再接続。

症状対照(主軸)

症状 1:ビルド赤字、型が見つからない / UnityEditor

想定根因手動MCP へ
ランタイムスクリプトが Editor API を参照エラーファイルが Editor/ 外か「Editor フォルダ外の UnityEditor. を検索;パスのみ列挙。」
#if が逆で Player に型がないUNITY_EDITOR ラップを確認「このファイルで Editor と Player それぞれに見える型を説明。」
asmdef 未参照エラーアセンブリの .asmdef を開く「その asmdef の参照を列挙;不足しているものを指摘。」

反例(ランタイムアセンブリで Editor API 誤用 → Player ビルド必ず失敗):

using UnityEngine;
using UnityEditor; // Editor アセンブリでなければビルド失敗

public class BadBake : MonoBehaviour
{
    [MenuItem("Tools/Bad")] // さらに UnityEditor 依存
    static void Run() { }
}

正解 A:ファイル全体を Editor フォルダへ

Assets/Scripts/Editor/BakeTools.cs   ← エディターのみコンパイル

正解 B:同一ファイル内で隔離(同ファイルが必須のときのみ)

using UnityEngine;

public class RuntimeSafe : MonoBehaviour
{
    public void DoGameplay() { /* Player に見える */ }

#if UNITY_EDITOR
    [ContextMenu("Debug/Fill Refs")]
    void EditorOnlyFill()
    {
        // エディターのみ;Awake でここの結果をパックデータとして扱わない
    }
#endif
}

よりきれいなのは Editor 専用スクリプト + asmdef で、ランタイムアセンブリが Editor 参照に引きずられないようにすること。

症状 2:実機でシーン入場 NRE、Editor Play は「なんとなく正常」

順番に調べ、並列の大改修はしない:

  1. シーンが Build Settings にない、または起動シーンの認識違い
  2. Missing Script / シリアライズ None(Prefab 未 Apply 含む)
  3. アドティブシーン未ロードのまま Find / アクセス
  4. Resources.Load パス大文字小文字(macOS Editor はしばしば非区別、Android は区別)
1. Build Settings のシーン一覧、有効状態、起動シーンインデックスを報告
2. シーン <X> の全 Missing Script を列挙
3. 起動パス上の GameManager / Player / HUD の公開参照が None か確認
報告のみ、変更しない。
エディター内パッケージ内よくある原因
参照は見える実行時 Noneインスタンスオーバーライド未 Apply;別シーンファイルを編集
Find で取得実機 null未ロードシーンのオブジェクト;名前不一致
Resources.Load 成功実機 nullパス大文字小文字;Resources/ 外の資産
// ディスク:Assets/Resources/UI/HealthBar.png
// Android でよく失敗(大文字小文字不一致):
Resources.Load<Sprite>("ui/healthbar");
// Resources 下の相対パスと一致させる、例:
Resources.Load<Sprite>("UI/HealthBar");
プロジェクト内の Resources.Load / Addressables.LoadAssetAsync のパス文字列を検索し、
実ファイルの相対パスと大文字小文字対照表を作成。コードはまだ変更しない。

症状 3:Console が Missing Script で埋まる

シーン <X> と Prefab <path> をスキャン:
Missing Script のあるオブジェクトパスを列挙。
各項目について「元スクリプトを再アタッチ」または「空コンポーネント削除」を提案(残存スクリプト名/GUID が見えれば)。
一括自動削除はまだしない。

手動:Project でスクリプトファイルが残っているか、asmdef/GUID 断裂がないか確認;空コンポーネント削除は一つずつ確認してから。

受け入れチェックリスト

  1. 同種の Error が Console から消える
  2. Editor Play で同じ落ちる操作(ステージ入場、ダメージ、UI 開く等)を再現
  3. ターゲット(またはローカル Player)へ Development Build を 1 回
  4. MCP 読み取り専用再確認:疑いフィールドが None / Missing でない
  5. Git diff は想定どおりの小さなシーン/Prefab/スクリプト変更のみ——「ついでリファクタ」なし
読み取り専用再確認:<オブジェクト一覧> の関連フィールドに None または Missing Script は残っているか?例外を列挙。編集しない。

プロンプトの赤線

やらないやる
「とにかく Play できるように」先に A/B/C 判定 + スタックでフィールド固定
参照の代わりに大量 Findシリアライズフィールド修正または明示的注入
未確認で Build Settings 順序変更先に現在の一覧と起動シーンを報告
「NRE がある」だけスタック先頭 20~40 行を貼る

プロダクト境界

  • Unity MCP:エディター内 Console、Hierarchy、スクリプトと参照——本稿の主軸に適合。
  • AI Studio:UI 構造をエンジンへ;再エクスポートで参照が消えたら配線記事どおり再接続——ゲームプレイから UI を Find しない。
  • 証明書、ストア、ベンダー ROM:ログテキストを Cursor に貼れば読み取り支援;MCP のシーン編集範囲外。

拡張:主軸外だがよくある

以下は「参照問題」と混ざりやすい——本稿の主軸を先に除外してから:

現象方向
Development OK、Release で落ちるManaged Stripping / link.xml;MCP へ:「クラッシュスタックから strip された型を推測;link.xml を提案;まだファイルは書かない。」
Android ネイティブクラッシュのみ(非マネージドスタック).so / 権限 / Gradle;logcat を貼って読み取り
メインスレッド外で Unity API非同期コールバックがメインスレッドに戻っているか確認

stripping と Missing Script を一度の「大リファクタ」にまとめない。

まとめ

  1. Editor OK / パッケージ NG → まず 参照 / ビルド入りシーン / パス / Editor API として調べる
  2. 手動で Build Settings と最初の Error を固定し、MCP 読み取り専用分類
  3. スタック → スクリプト行 → コンポーネントフィールド;確認後に最小再接続または空コンポーネント削除
  4. Find やゲームプレイ書き換えで偽装修正しない
  5. Development Build + 読み取り専用再確認で受け入れ

「現場固定 → 層分け → 三つセット → サンプル同様にフィールド固定」で回せば、実機の参照バグの多くはレビュー可能な小さな diff に収まる。

こちらの記事もおすすめです