Unityでゲーム制作をしていると、ある日こんなエラーに遭遇することがあります。
MissingReferenceException:
The object of type 'Enemy' has been destroyed but you are still trying to access it.
最初に見ると、「え、nullじゃないの?」「Destroyしただけなのに?」とかなり混乱しやすいんですよね。
しかもこのエラー、厄介なのが“最初は普通に動いていた”ケースが多いことです。
- 敵を倒した瞬間だけエラーが出る
- シーン切り替え後に突然壊れる
- 数秒後にCoroutineで落ちる
- Listをループしていたら急にMissingになる
こういう症状が出ると、「とりあえずnullチェック」で乗り切りたくなる気持ちも分かります🙂
ただ、MissingReferenceExceptionは“参照の寿命管理”が原因になっていることが多く、表面的な修正だけだと再発しやすいです。
特にUnityは、普通のC#とは少し違う「Destroy済みオブジェクト」の扱いをするので、初心者ほど混乱しやすいポイントでもあります。
私も最初の頃、EnemyをDestroyしたあとに追尾処理だけ残っていて、「なんでnullじゃないのに壊れるの…?」とかなり悩みました。
このエラーは、仕組みを一度理解してしまうと原因を切り分けやすくなります。
この記事では、
- MissingReferenceExceptionの意味
- NullReferenceExceptionとの違い
- Destroy後に参照が残る理由
- Coroutine・List・イベントで再発しやすいケース
- 再発防止の考え方
を、実際によくある失敗パターンと一緒に整理していきます。
MissingReferenceExceptionとは?
まず最初に押さえておきたいのが、MissingReferenceExceptionは「オブジェクトが存在しなくなったのに、まだ使おうとしている」時に発生するエラーだということです。
特によくあるのが、Destroy()したオブジェクトへの参照が残っているケースです。
例えば、こんなコードがあります。
public class Enemy : MonoBehaviour
{
void Update()
{
transform.Translate(Vector3.forward * Time.deltaTime);
}
}
このEnemyを別スクリプトからDestroyしたあと、どこかでまだ参照していると、MissingReferenceExceptionが発生することがあります。
Destroy(enemy.gameObject);
enemy.transform.position = Vector3.zero;
この場合、Enemyはすでに削除済みです。
でも変数enemy自体は残っているため、「存在しているように見える」のが混乱ポイントなんですよね。
Unityでは、Destroyしたオブジェクトが即座に完全削除されるわけではなく、“Unity側で特殊な状態”になります。
そのため、普通のC#のnullとは少し違う挙動をします。
NullReferenceExceptionとの違い
初心者が特に混同しやすいのが、NullReferenceExceptionとの違いです。
| エラー | 原因 |
|---|---|
| NullReferenceException | そもそも参照が入っていない |
| MissingReferenceException | Destroy済みオブジェクトを参照している |
つまり、MissingReferenceExceptionは「昔は存在していた」という点が特徴です。
だからこそ、
- 最初は正常動作する
- 途中から壊れる
- 特定タイミングだけ落ちる
という“再現が不安定なエラー”になりやすいんです。
Destroyした直後でも変数が残る理由
Unity初心者が最初につまずきやすいのがここです。
例えば次のコード。
Destroy(gameObject);
Debug.Log(gameObject);
「Destroyしたならnullになるはず」と思いがちですが、Unity内部では少し特殊な管理がされています。
UnityEngine.Objectは独自のnull判定を持っているため、C#だけの知識だと違和感が出やすいんですね。
この仕様のせいで、
- Coroutine内で後から参照して落ちる
- Listに残っていて壊れる
- イベント購読解除し忘れてエラーになる
といった問題が発生しやすくなります。
特にゲーム開発では、「消えたオブジェクトを誰がまだ握っているか」を意識すると、かなり原因を追いやすくなります。
関連するエラーとして、オブジェクト取得そのものに失敗しているケースもあります。
MissingReferenceExceptionが発生しやすい原因4選
MissingReferenceExceptionは、「Destroy後の参照」が根本原因です。
ただ、実際の現場では発生パターンがいくつかあります。
ここを把握しておくと、「また同じエラーだ…」と気づきやすくなりますよ。
Destroy後に別スクリプトから参照している
いちばん多いのがこのケースです。
例えばEnemyを削除したあと、Player側やManager側がまだEnemyを触っているパターンですね。
Destroy(enemy.gameObject);
enemy.Move();
この場合、変数enemyは残っていますが、中身のUnityオブジェクトは破棄済みです。
その結果、MissingReferenceExceptionになります。
特に初心者のうちは、
- 「Destroyした場所」
- 「まだ参照している場所」
が別スクリプトに分かれていることに気づきにくいんですよね。
「誰がいつDestroyするのか」を整理すると、かなり防ぎやすくなります。
Coroutineが破棄後も動いている
次によくあるのがCoroutineです。
例えば、数秒待ってから処理するコード。
IEnumerator Attack()
{
yield return new WaitForSeconds(3f);
target.transform.position = Vector3.zero;
}
待機中にtargetがDestroyされると、3秒後にMissingReferenceExceptionが発生します。
これ、かなり遭遇率が高いです。
しかも「時間差で落ちる」ので、原因特定が難しくなりやすいんですよね。
もし、
- 数秒後だけエラーが出る
- 戦闘終了後に突然壊れる
- 非同期処理中だけ落ちる
という症状があるなら、Coroutineを疑ってみてください。
関連するCoroutineトラブルは、こちらでも詳しく整理しています。
Listや配列にDestroy済みオブジェクトが残っている
これはEnemy管理やBullet管理で本当によくあります。
例えば、
List<GameObject> enemies = new List<GameObject>();
にEnemyを保存していた場合、DestroyしてもListから自動削除はされません。
つまり、内部には「Missing状態」の参照が残ります。
そのままループすると、あとからエラーが出ます。
foreach(var enemy in enemies)
{
enemy.transform.position += Vector3.forward;
}
特にスポーン管理系ではかなり発生しやすいので、定期的な掃除が重要です。
enemies.RemoveAll(item => item == null);
このコードは実務でもかなりよく使います。
List管理に不慣れな場合は、こちらもかなり役立ちます。
イベント解除漏れで破棄済みオブジェクトが呼ばれる
中級者以上でもハマりやすいのがイベント解除漏れです。
例えば、Managerがイベントを発行しているとします。
manager.OnDamage += OnDamage;
この購読を解除しないままオブジェクトをDestroyすると、イベント発火時にMissingReferenceExceptionが出ることがあります。
特にSingletonやGameManagerを使っていると起きやすいですね。
対策としては、OnDisable()やOnDestroy()で解除するのが基本です。
private void OnDisable()
{
manager.OnDamage -= OnDamage;
}

イベント系は「動いている時は快適だけど、壊れた時の追跡が難しい」ので、早めに習慣化しておくとかなり楽になります。
MissingReferenceExceptionの対処法
MissingReferenceExceptionは、「Destroy済みオブジェクトをまだ触っている」状態を解消すると直ります。
ただ、やみくもにnullチェックを追加するだけだと、あとで別の場所から再発しやすいです。
ここでは、実際によく使う対処法を「優先順位順」で整理していきます。
まずはエラー行を確認する
最初にやるべきなのは、Unity Consoleのスタックトレース確認です。
Consoleをダブルクリックすると、エラー発生行へジャンプできます。
例えばこんなエラー。
MissingReferenceException:
The object of type 'Enemy' has been destroyed but you are still trying to access it.
EnemyController.Update()
この場合、EnemyController.Update()の中でDestroy済みオブジェクトを触っています。
ここで重要なのは、「どの変数がMissingなのか」を特定することです。
初心者のうちは、
- Destroyした側
- エラーが出た側
を別々に考えてしまいがちなんですが、実際は“参照が残っている側”が原因になっていることが多いです。
オブジェクト使用前にnullチェックする
もっとも基本的な対策です。
if(enemy != null)
{
enemy.Move();
}
UnityではDestroy済みオブジェクトも== nullで判定できるため、多くのケースで有効です。
特に、
- Coroutine
- Update
- イベントコールバック
など、「あとから呼ばれる処理」ではかなり重要になります。
ただし、ここで注意したいのが、“nullチェックは応急処置になりやすい”ことです。
例えば、Enemyを削除したあともPlayerがEnemyを追跡し続けている設計だと、毎回nullチェックを書くことになります。
こういう時は、
- ターゲット変更時に参照を切る
- Destroy時に通知する
- 管理クラスで寿命を統一する
など、設計側を見直したほうが再発しにくいです。
TryGetComponentを使って安全に取得する
GetComponent()周りの取得ミスも、あとからMissingReferenceExceptionにつながることがあります。
最近は、TryGetComponent()を使うほうが安全です。
if(TryGetComponent(out Rigidbody rb))
{
rb.AddForce(Vector3.up);
}
これなら、「取得失敗時にnullのまま進行する」事故を減らしやすくなります。
特にPrefab差し替えやコンポーネント構成変更が多いプロジェクトでは、かなり便利です。
List内のMissing参照を定期的に掃除する
Enemy管理やBullet管理では、Destroy済み参照がListに残りやすいです。
なので、定期的に掃除する処理を入れると安定しやすくなります。
enemyList.RemoveAll(item => item == null);
これはかなり定番の書き方ですね。
特に大量スポーン系では、Missing参照が蓄積するとデバッグが急に難しくなります。
Inspector上で参照状態を可視化したい場合は、Odin Inspectorのような拡張アセットも便利です。
Odin Inspector
✅アセットストアでチェックする
Destroyする責任を整理する
中規模以上のプロジェクトになると、「誰がDestroyするのか」が曖昧になりやすいです。
例えば、
- Enemy自身が自分をDestroyする
- GameManagerが削除する
- Player側が消す
が混在すると、参照管理がかなり壊れやすくなります。
私も昔、Enemy削除処理が複数箇所に分散していて、「たまにだけMissingReferenceExceptionが出る」という沼にハマりました…。
Destroy責任を1か所に寄せるだけでも、かなり追跡しやすくなります。
スクリプト責務の分離については、こちらもかなり参考になります。
Destroyのタイミングを理解すると再発しにくい
MissingReferenceExceptionを理解するうえで、かなり重要なのが「Destroyは即時削除ではない」という点です。
ここを誤解していると、「さっきまで動いていたのに次のフレームで壊れた…」という現象が起きやすくなります。
Destroyはその場で即削除されるわけではない
例えば次のコード。
Destroy(gameObject);
Debug.Log("まだ動く?");
これを見ると、「Destroyした瞬間に完全削除される」と思いやすいですよね。
でも実際には、Destroy()は“削除予約”に近い動きをします。
Unityでは通常、現在のUpdate処理が終わったあとに破棄されます。
そのため、同じフレーム中では一部アクセスできてしまうことがあります。
これが、MissingReferenceExceptionをややこしくしている原因のひとつです。
「今は動く」が次フレームで壊れる
例えばこんなコード。
Destroy(enemy.gameObject);
Debug.Log(enemy.transform.position);
このコード、状況によってはエラーが出ないことがあります。
でも次フレームで同じことをすると、MissingReferenceExceptionになるケースがあります。
つまり、「今アクセスできたから安全」とは限らないんですね。
特に危険なのが、
- Coroutine
- Invoke
- 遅延処理
- イベント
など、“後から実行される処理”です。
Destroy後に参照が残っていると、時間差で突然壊れます。
「一定時間後だけ落ちる」場合は、かなり疑わしいポイントです。
DestroyImmediateは基本的に通常ゲーム処理では使わない
UnityにはDestroyImmediate()もあります。
DestroyImmediate(gameObject);
こちらは即時削除です。
ただし、Runtime中に多用すると予期しない挙動につながりやすいため、通常のゲーム処理ではあまり使いません。
主にEditor拡張や特殊用途向けですね。
初心者のうちは、「DestroyImmediateなら安全そう」と感じやすいんですが、むしろ事故が増えることもあります。
まずは通常のDestroy()のライフサイクルを理解するほうが大切です。
Destroy系の挙動は、Prefabやシーン管理とも密接に関係します。
シーン遷移でもMissingReferenceExceptionは起こる
Destroyを直接呼んでいなくても、シーン切り替え時にオブジェクトが消えるケースがあります。
例えば、
- Scene A のEnemyを保持
- Scene B に移動
- Enemy参照だけ残る
という流れですね。
この場合も、あとからアクセスするとMissingReferenceExceptionになります。
特に、
- Singleton
- DontDestroyOnLoad
- Manager系
を使い始めると発生しやすいです。
もし「シーン切り替え後だけ壊れる」なら、参照寿命を疑ってみてください。
DontDestroyOnLoad周りの落とし穴は、こちらでも詳しく整理しています。
MissingReferenceException診断チェックリスト
MissingReferenceExceptionは、「原因候補を順番に潰す」とかなり早く解決できます。
逆に、思いつきで修正し始めると、別の場所で再発しやすいです。
私も昔は、nullチェックを増やしまくって「一応動くけど原因不明」という状態によくなっていました…。
ここでは、実際によく使う確認順をまとめます。
まず確認するべき6項目
- Consoleのエラー行を確認したか
- Destroy後に参照していないか
- Coroutineが残っていないか
- Listや配列にMissing参照が残っていないか
- イベント解除漏れがないか
- シーン遷移後だけ発生していないか
特に重要なのは、「Destroyした場所」ではなく「まだ触っている場所」を探すことです。
MissingReferenceExceptionは、“消した側”より“参照が残っている側”が原因になっているケースが多いんですよね。
「最初は動く」のかを確認する
これはかなり強い判断材料です。
| 症状 | 疑うポイント |
|---|---|
| 最初から動かない | NullReferenceException系 |
| 途中から壊れる | MissingReferenceException系 |
| 数秒後だけ落ちる | Coroutine・Invoke |
| シーン切替後だけ壊れる | 参照寿命・DontDestroyOnLoad |
特に「最初は正常なのに途中で壊れる」は、Destroy後参照の典型パターンです。
この特徴を覚えておくだけでも、原因特定がかなり速くなります。
Hierarchyに「Missing」が残っていないか確認する
InspectorやHierarchyに「Missing」と表示されている場合、かなり分かりやすいサインです。
例えば、
- SerializedField
- UnityEvent
- List
- Prefab参照
などにMissingが残っていることがあります。
特にPrefab運用では、古い参照が残り続けるケースがよくあります。
「何もしてないのに突然壊れた」ように見えても、実はInspector側にMissingが潜んでいることはかなり多いです。
Debug.Logで「いつ壊れたか」を追う
原因が見つからない時は、参照の寿命を追跡すると分かりやすいです。
Debug.Log(enemy);
Destroy(enemy.gameObject);
Debug.Log("Destroy後");
Debug.Log(enemy);
このようにログを入れると、
- いつDestroyされたか
- どこでまだ参照されているか
- どのタイミングでMissingになるか
を追いやすくなります。
特に中規模以上のプロジェクトでは、「参照の流れを見える化する」だけでかなりデバッグが楽になります。
Debug系の可視化は、こちらの記事もかなり相性が良いです。
MissingReferenceExceptionでよくある誤解
MissingReferenceExceptionは、Unity特有の挙動が絡むので誤解されやすいです。
特に初心者のうちは、「null関連エラー」とひとまとめに考えてしまいがちなんですよね。
ここでは、実際によくある勘違いを整理しておきます。
「nullチェックすれば全部解決」は半分正しい
確かに、
if(enemy != null)
{
enemy.Move();
}
のようなnullチェックは有効です。
UnityではDestroy済みオブジェクトも== nullで判定できるため、多くのケースでエラー回避できます。
ただし、これは“応急処置”になりやすいです。
例えば、
- Enemy削除後もPlayerが追尾し続けている
- イベント解除漏れがある
- Managerが古い参照を保持している
といった状態では、毎回nullチェックを書くことになります。
本当に重要なのは、「誰が参照を持つべきか」を整理することです。
中規模以上のプロジェクトでは、ここを意識するだけでかなり安定します。
設計面を改善したい場合は、こちらもかなり役立ちます。
SetActive(false)はDestroyとは別物
かなり混同されやすいポイントです。
SetActive(false)は「非表示・停止」であり、Destroyではありません。
gameObject.SetActive(false);
この場合、オブジェクト自体は存在しています。
なので通常はMissingReferenceExceptionにはなりません。
一方、Destroyはオブジェクトそのものを破棄します。
Destroy(gameObject);
つまり、
| 処理 | オブジェクト状態 |
|---|---|
| SetActive(false) | 存在している |
| Destroy() | 破棄される |
という違いがあります。
「一時的に消したいだけ」なら、DestroyではなくSetActive(false)のほうが安全なケースもあります。
GetComponent失敗が直接MissingReferenceExceptionになるわけではない
これもかなり誤解されやすいです。
例えば、
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.up);
この時、Rigidbodyが存在しなければ通常はNullReferenceException寄りになります。
MissingReferenceExceptionになるケースは、「取得後にDestroyされた」場合です。
つまり、
- 最初から取得失敗 → NullReferenceException
- 途中で破棄された → MissingReferenceException
というイメージですね。
この違いを理解すると、原因の切り分けがかなり速くなります。
「動いているから安全」は危険
Unityでは、Destroy直後でも一瞬アクセスできるケースがあります。
そのため、
- 今は動いている
- たまにしか落ちない
- 再現率が低い
という状態になりやすいです。
特にCoroutineやイベントは、時間差で問題が表面化します。
なので、「たまたま動いた」を信用しすぎないことが大切です。
私も昔、テストプレイ中は正常だったのに、敵を大量スポーンさせた瞬間だけMissingReferenceException祭りになったことがあります…。

参照寿命系の問題は、“小規模では見えにくい”のが本当に厄介なんですよね。
まとめ|MissingReferenceExceptionは「参照寿命」を意識すると解決しやすい
MissingReferenceExceptionは、Unity初心者がかなり高確率で遭遇するエラーです。
ただ、原因そのものは意外とシンプルで、「Destroy済みオブジェクトをまだ参照している」状態がほとんどです。
特に重要なのは、次の違いですね。
| エラー | 主な原因 |
|---|---|
| NullReferenceException | 最初から参照が入っていない |
| MissingReferenceException | 途中でDestroyされた |
この違いを理解するだけでも、原因調査の速度がかなり変わります。
特に、
- 最初は動くのに途中で壊れる
- Coroutine後に落ちる
- シーン切替後だけエラー
- Listループ中にMissingになる
という症状なら、Destroy後参照をかなり疑ってOKです。
また、実務では「nullチェックを書く」よりも、
- 誰がDestroyするのか
- 誰が参照を持つのか
- いつ参照を解除するのか
を整理するほうが、圧倒的に再発しにくくなります。
私も最初の頃は、「if(obj != null)を増やせば解決」と思っていました。
でも実際は、Enemy管理・イベント・Coroutineの責務整理をした瞬間、一気に安定した経験があります。
Unityはオブジェクト寿命の管理がかなり重要なので、MissingReferenceExceptionは“設計を見直すサイン”として捉えると、むしろレベルアップにつながりやすいです🙂
コード管理や責務分離をさらに改善したい場合は、Clean Code系の考え方もかなり役立ちます。
Clean Code
✅ Amazonでチェックする|✅ 楽天でチェックする
よくある質問(FAQ)
- QMissingReferenceExceptionとNullReferenceExceptionはどちらが危険?
- A
どちらが危険というより、「原因の種類」が違います。
NullReferenceExceptionは、最初から参照が存在しないケースが中心です。
一方、MissingReferenceExceptionは「途中でDestroyされた」のが特徴です。
そのため、MissingReferenceExceptionのほうが、
- 時間差で発生する
- 再現率が低い
- 特定条件だけ壊れる
など、デバッグが難しくなりやすい傾向があります。
特にCoroutineやイベントを多用し始めると、原因追跡が複雑になりやすいですね。
- QDestroyしたあとにif(obj != null)すれば安全?
- A
多くのケースでは有効です。
if(enemy != null) { enemy.Move(); }UnityではDestroy済みオブジェクトも
== nullで判定できるため、エラー回避には役立ちます。ただし、nullチェックを大量に書き始めた場合は、「参照設計そのもの」に問題がある可能性もあります。
例えば、
- Destroy後も追尾処理が残る
- Managerが古い参照を持ち続ける
- イベント解除漏れがある
といった状態ですね。
応急処置としては有効ですが、「なぜ参照が残っているのか」まで見ると再発しにくくなります。
- QDestroyせずSetActive(false)を使えば安全?
- A
ケースによります。
SetActive(false)はオブジェクトを破棄しないため、MissingReferenceExceptionは起きにくいです。gameObject.SetActive(false);そのため、
- 弾のオブジェクトプール
- UI表示切替
- 一時非表示
などではかなりよく使われます。
ただし、不要オブジェクトが増え続けると、別の管理問題が発生することもあります。
なので、
- 完全削除したい → Destroy
- 再利用したい → SetActive(false)
という使い分けが大切です。















※当サイトはアフィリエイト広告を利用しています。リンクを経由して商品を購入された場合、当サイトに報酬が発生することがあります。
※本記事に記載しているAmazon商品情報(価格、在庫状況、割引、配送条件など)は、執筆時点のAmazon.co.jp上の情報に基づいています。
最新の価格・在庫・配送条件などの詳細は、Amazonの商品ページをご確認ください。