Unity C#|GetComponentがnullになる理由と対処法【初心者向け完全解説】
UnityでC#を書いていると、ほぼ確実に一度は遭遇するのが
「GetComponent が null になる問題」です。
var rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.up);
一見すると正しそうなコードなのに、
実行すると NullReferenceException が発生してしまう。
「ちゃんとGetComponentしているのに、なぜ?」
と混乱した経験がある方も多いのではないでしょうか。
実は、GetComponentがnullを返すのは
珍しいことでも、特別なバグでもありません。
Unityの仕組みを正しく理解していれば、
「なるほど、そういうことか」と納得できるケースがほとんどです。
この記事では、Unity初心者の方に向けて、
- GetComponentがnullになる仕組み
- よくある原因と具体例
- nullを防ぐための安全な書き方
- 設計レベルでの対処法
を、実際のコード例を交えながら分かりやすく解説します。
前回の記事
「NullReferenceExceptionとは何か?」
の内容を踏まえつつ、
今回はその中でも特に質問の多い
GetComponentに特化して解説していきます。
この記事を読み終える頃には、
「GetComponentがnullで落ちるのが怖い」状態から、
「原因を切り分けて冷静に対処できる」状態になっているはずです。
それでは、まず
GetComponentとは何か?
から見ていきましょう。
目次
- GetComponentとは何か?
- なぜGetComponentはnullになるのか?
- よくある原因① 同じGameObjectに付いていない
- よくある原因② コンポーネントを付け忘れている(Inspector)
- よくある原因③ Awake / Start の初期化順問題
- よくある原因④ GetComponentInChildren / GetComponentInParent の誤解
- よくある原因⑤ 実行中にDestroyされている
- nullを防ぐための安全な書き方
- GetComponentを減らす設計の考え方
- よくある質問(FAQ)
- まとめ
GetComponentとは何か?
GetComponent は、UnityでC#を書く上で最も基本的かつ重要なメソッドの一つです。
一言で言うと、
同じGameObjectに付いているコンポーネントを取得するためのメソッド
です。
GetComponentの基本的な使い方
最もよく見る形がこちらです。
Rigidbody rb = GetComponent<Rigidbody>();
この1行は、次の意味を持っています。
- このスクリプトが付いている GameObject
- そのGameObjectに付いている Rigidbodyコンポーネント
- それを取得して
rbに代入する
つまり、
「自分自身(同じGameObject)から探す」
という点が非常に重要です。
GetComponentは「探している」だけ
GetComponentは、
コンポーネントを生成しているわけではありません。
やっていることはシンプルで、
- 指定された型のコンポーネントを探す
- 見つかれば → それを返す
- 見つからなければ → nullを返す
という処理です。
var rb = GetComponent<Rigidbody>();
if (rb == null)
{
// Rigidbodyが見つからなかった
}
👉
エラーを出しているのではなく、
「存在しないので null を返している」
というだけなのです。
戻り値は「Component または null」
GetComponentの戻り値は、常に
- 対象のコンポーネント
- もしくは null
のどちらかになります。
そのため、次のようなコードは
非常に危険です。
GetComponent<Rigidbody>().AddForce(Vector3.up);
もし Rigidbody が付いていなければ、
- GetComponent<Rigidbody>() → null
- null.AddForce(…) → ❌ NullReferenceException
という流れでエラーが発生します。
GetComponentが使われる代表的な場面
GetComponentは、以下のような場面で頻繁に使われます。
- Rigidbody や Collider の取得
- Animator や AudioSource の操作
- 他の自作スクリプトへのアクセス
例:
Animator anim = GetComponent<Animator>();
Enemy enemy = GetComponent<Enemy>();
Unityのスクリプトは
「GameObjectに機能を追加する」設計になっているため、
GetComponentはほぼ必須の存在です。
重要ポイントまとめ
ここまでの内容をまとめます。
- GetComponentは「同じGameObject」から探す
- 見つからなければ null を返す
- 生成はしない
- nullは異常ではない
👉
GetComponentがnullになること自体は正常な動作
という点を、まず押さえておきましょう。
なぜGetComponentはnullになるのか?
GetComponentがnullを返す理由は、実はとてもシンプルです。
指定した型のコンポーネントが、
そのGameObjectに存在しないから
これだけです。
バグでも、Unityの不具合でもありません。
「見つからなかった」 という正常な結果として、
nullが返ってきています。
「失敗している」のではなく「見つからない」
GetComponentという名前から、
「失敗している」「取得に失敗した」
と感じる方も多いですが、実際には違います。
GetComponentは、
- 成功 → コンポーネントを返す
- 失敗 → nullを返す
という仕様です。
例:
Rigidbody rb = GetComponent<Rigidbody>();
if (rb == null)
{
Debug.Log("Rigidbodyは付いていません");
}
このコードは、
エラーを出さずに正常終了します。
👉
GetComponentは例外を投げません。
例外が出るのは、その後に
nullを使ってしまったときです。
NullReferenceExceptionが発生する本当の原因
多くの人が混同しているポイントですが、
- 原因 → GetComponentがnullを返したこと
- 実際に落ちる理由 → nullに対して処理をしたこと
です。
GetComponent<Rigidbody>().AddForce(Vector3.up);
この1行には、
- Rigidbodyを探す
- 見つからなければ null
- null に AddForce を呼ぶ
という流れが含まれています。
👉
GetComponent自体は悪くない
という点を理解しておきましょう。
Unityは「エディタ設定」と強く結びついている
Unityでは、
- GameObject
- コンポーネント
- Inspectorの設定
が密接に関係しています。
つまり、
- スクリプト上は正しく見える
- でも Inspector では付いていない
という状態が簡単に起こります。
これが、
「コードは合っているのにnullになる」
と感じる大きな理由です。
nullになる原因はほぼパターン化できる
GetComponentがnullになるケースは、
実際には以下のように ほぼ決まったパターン があります。
- 同じGameObjectに付いていない
- Inspectorで付け忘れている
- 初期化タイミングが早すぎる
- 子・親オブジェクトを誤解している
- 実行中にDestroyされている
👉
これらを1つずつ理解すれば、
GetComponentで悩むことはほとんどなくなります。
ここからは「具体例」で理解する
次の章からは、
実際によくあるミスを 1つずつ 見ていきます。
まずは最も多い原因、
👉 「同じGameObjectに付いていない」
から解説します。
よくある原因① 同じGameObjectに付いていない
GetComponentがnullになる原因として、
最も多く、最も基本的なケースがこれです。
取得しようとしているコンポーネントが、
同じGameObjectに付いていない
GetComponentは「自分自身」しか見ない
改めて強調しておきたいポイントですが、
GetComponentは
このスクリプトが付いているGameObject
からしかコンポーネントを探しません。
例:
public class Player : MonoBehaviour
{
void Start()
{
Rigidbody rb = GetComponent<Rigidbody>();
}
}
このコードは、
- Player スクリプト
- Rigidbody
が 同じGameObject に付いている場合のみ成功します。
別のGameObjectに付いていると必ずnull
例えば、次のような構成を考えてみましょう。
- Player(GameObject)
- Player.cs
- PlayerModel(子オブジェクト)
- Rigidbody
この場合、
GetComponent<Rigidbody>();
を呼んでも、
PlayerModel に付いている Rigidbody は見つかりません。
結果として、null が返ります。
よくある勘違い
初心者の方がよくやってしまうのが、
「子オブジェクトに付いているから取れるはず」
という思い込みです。
しかし、
このコードだけでは子オブジェクトは見ません。
GetComponent<Rigidbody>(); // 見ない
正しい対処法① 子オブジェクトから取得する
子オブジェクトに付いている場合は、
以下のようにします。
Rigidbody rb = GetComponentInChildren<Rigidbody>();
これで、
- 自分自身
- 子オブジェクト(すべて)
を対象に検索してくれます。
正しい対処法② 明示的に参照を持つ
もう1つの安全な方法は、
Inspectorから参照を設定するやり方です。
[SerializeField]
private Rigidbody rb;
InspectorでRigidbodyをドラッグすれば、
GetComponentを使わずに確実に取得できます。
👉
特に重要なコンポーネントは、
この方法が一番安全です。
正しい対処法③ Transform経由で取得する
特定の子オブジェクトが分かっている場合は、
Rigidbody rb = transform.Find("PlayerModel")
.GetComponent<Rigidbody>();
のように、
対象を限定して取得することもできます。
この原因のチェックリスト
GetComponentがnullになったら、
まずここを確認しましょう。
- スクリプトと対象コンポーネントは同じGameObjectか?
- 子オブジェクトではないか?
- Prefab構造は変わっていないか?
ここまでのまとめ
- GetComponentは同じGameObjectのみ対象
- 別オブジェクトなら必ずnull
- 子オブジェクトは明示的に指定する
よくある原因② コンポーネントを付け忘れている(Inspector)
GetComponentがnullになる原因として、
原因①と並んで非常に多いのがこのケースです。
そもそも、取得しようとしているコンポーネントが
GameObjectに付いていない
「コードは合っているのにnull」な理由
次のコードを見てみましょう。
Rigidbody rb = GetComponent<Rigidbody>();
文法的にも、書き方としても問題はありません。
それでも null になる場合、
Inspectorを確認すると…
- Rigidbody が付いていない
- Prefabには付いているが、シーン上のオブジェクトには付いていない
- 途中で削除してしまった
といったことがよくあります。
Prefabとシーンの差分に注意
Unityでは、
- Prefab
- シーン上のインスタンス
が 別物 です。
例えば、
- PrefabではRigidbodyが付いている
- しかしシーン上では削除されている
という状態でも、
見た目では気づきにくいことがあります。
👉
GetComponentがnullになったら、
必ずシーン上のオブジェクトのInspectorを確認しましょう。
スクリプトを付け替えた時も要注意
次のような流れも、よくあるミスです。
- 以前は Rigidbody を使っていた
- 途中で不要になり、Rigidbody を削除
- スクリプト内では GetComponent<Rigidbody>() が残っている
この場合、
当然 null が返ります。
RequireComponentで事故を防ぐ
このミスを防ぐために、
Unityには便利な属性があります。
[RequireComponent(typeof(Rigidbody))]
public class Player : MonoBehaviour
{
}
これを付けておくと、
- スクリプトをアタッチした瞬間に
- Rigidbody が自動で追加される
ため、
付け忘れを物理的に防止できます。
👉
必須コンポーネントには積極的に使いましょう。
Inspectorで必ず確認したいポイント
GetComponentがnullになったら、
以下をチェックしてください。
- 対象のコンポーネントは付いているか?
- 無効(チェックOFF)になっていないか?
- Prefabとシーンで差分がないか?
ここまでのまとめ
- GetComponentは付いていなければ必ずnull
- Inspector確認は最優先
- RequireComponentで事故を防げる
よくある原因③ Awake / Start の初期化順問題
GetComponentがnullになる原因として、
意外と見落とされがちなのが
初期化タイミングの問題です。
コード自体は正しいのに、
「タイミングが早すぎて取得できない」
というケースがUnityではよく起こります。
Awake と Start の違い
まずは基本を整理しましょう。
- Awake
- オブジェクトが生成された直後に呼ばれる
- すべてのスクリプトが揃っているとは限らない
- Start
- すべてのAwakeが呼ばれた後に呼ばれる
- 初期化が一通り終わった後
この違いが、
GetComponentがnullになる原因になります。
Awakeでnullになる典型例
次のようなコードを見てみましょう。
private Enemy enemy;
void Awake()
{
enemy = FindObjectOfType<Enemy>();
enemy.Init(); // ❌ null の可能性
}
この場合、
- Enemyオブジェクトがまだ生成されていない
- もしくは Enemy の Awake が終わっていない
といった理由で、enemy が null になることがあります。
Startでは成功するケース
同じ処理を Start に移すと、
void Start()
{
enemy = FindObjectOfType<Enemy>();
enemy.Init(); // ✅ 安全になりやすい
}
Startは、
- すべてのAwakeが完了した後
に呼ばれるため、
参照取得が成功しやすくなります。
GetComponent自体はAwakeでも問題ない
誤解されがちですが、
GetComponent<Rigidbody>();
のような
同一GameObject内の取得は、
Awakeでも基本的に問題ありません。
問題になるのは、
- 他オブジェクト
- Find系
- 実行順に依存する処理
です。
実行順が関係する場合の対処法
どうしてもAwakeで取得したい場合は、
- Script Execution Order を調整する
- 初期化用メソッドを分離する
といった方法があります。
public void Init()
{
rb = GetComponent<Rigidbody>();
}
必要なタイミングで
明示的に呼ぶ設計にするのも有効です。
初期化順で悩まないための考え方
- 自分自身の初期化 → Awake
- 他オブジェクト参照 → Start以降
- 依存関係がある処理 → 明示的Init
このルールを決めておくと、
null問題は激減します。
ここまでのまとめ
- Awakeは早すぎることがある
- Startは参照取得が安定
- 他オブジェクトはStart以降が安全
よくある原因④ GetComponentInChildren / GetComponentInParent の誤解
「子オブジェクトに付いているから
GetComponentInChildren を使えば大丈夫」
そう思っているのに null になる場合、
このメソッドの仕様を誤解している
可能性があります。
GetComponentInChildren の基本動作
var rb = GetComponentInChildren<Rigidbody>();
このメソッドは、
- 自分自身
- 子オブジェクト(階層すべて)
を対象に、
指定した型のコンポーネントを探します。
しかし、
必ず見つかるわけではありません。
非アクティブなオブジェクトは対象外
重要な落とし穴がこちらです。
デフォルトでは、非アクティブな子オブジェクトは検索されない
例えば、
- 子オブジェクトが SetActive(false)
- Prefab内で非表示
この場合、
GetComponentInChildren<Rigidbody>();
は null を返します。
includeInactive を使う
非アクティブな子も含めたい場合は、
以下のように指定します。
var rb = GetComponentInChildren<Rigidbody>(true);
この true が、
includeInactive を意味します。
👉
これを知らないと、
「あるはずなのに取れない」
状態になります。
GetComponentInParent でも同様
親オブジェクトを探す場合も、
基本は同じです。
var rb = GetComponentInParent<Rigidbody>();
こちらも、
- 非アクティブな親
- 無効化されたオブジェクト
は、
デフォルトでは対象外です。
複数ある場合は「最初の1つ」だけ
GetComponentInChildren / Parent は、
- 複数見つかった場合
- 最初に見つかった1つ
だけを返します。
👉
どれが取得されるかは、
ヒエラルキー構造に依存します。
意図しないコンポーネントを
取得しているケースもあるので注意が必要です。
安全な設計の考え方
頻繁にこれらを使う場合は、
- 明示的にTransformを指定する
- Inspectorから参照を設定する
ほうが、
バグが入りにくくなります。
[SerializeField]
private Rigidbody childRb;
ここまでのまとめ
- 非アクティブな子は対象外(デフォルト)
- includeInactive を使う
- 複数ある場合は1つだけ返る
よくある原因⑤ 実行中にDestroyされている
「最初は取得できていたのに、
途中からGetComponentがnullになる」
このような場合、
実行中にオブジェクトがDestroyされている
可能性があります。
これはUnity特有の挙動なので、
初心者の方が特に混乱しやすいポイントです。
Destroyされたオブジェクトは「null扱い」
Unityでは、
Destroy(gameObject);
を実行すると、
- 見た目上は存在している
- 参照も残っているように見える
にもかかわらず、
nullとして扱われる
という特殊な挙動をします。
if (enemy == null)
{
// Destroyされている可能性あり
}
👉
C#の純粋なnullとは少し違う、
Unity独自の仕様です。
よくあるバグ例
Enemy enemy;
void Start()
{
enemy = GetComponent<Enemy>();
}
void Update()
{
enemy.Move(); // ❌ 途中でnullになる
}
別の処理で enemy が Destroy されると、
Update 内で突然
NullReferenceException が発生します。
Destroy後に処理しない設計にする
Destroyされる可能性がある場合は、
必ずガードを入れましょう。
if (enemy == null) return;
もしくは、
- フラグで生存管理する
- 参照を解除する
といった設計が有効です。
OnDestroy を活用する
void OnDestroy()
{
enemy = null;
}
Destroyされたタイミングで
明示的に参照を切ることで、
意図しないアクセスを防げます。
「見た目が残っている」は信用しない
Unityでは、
- フレーム終端まで描画が残る
- 参照があるように見える
ことがあります。
👉
見た目ではなく、参照状態を信じる
ようにしましょう。
ここまでのまとめ
- Destroyされたオブジェクトはnull扱い
- 途中からnullになるケースは要注意
- ガード処理が重要
nullを防ぐための安全な書き方
ここまでで、
GetComponentがnullになる原因は
ほぼパターン化できることが分かってきました。
この章では、
そもそもnullで落ちない書き方・設計
を紹介します。
基本① 必ず一度変数に受ける
まず大前提として、
次の書き方は避けましょう。
GetComponent<Rigidbody>().AddForce(Vector3.up); // ❌
代わりに、
必ず変数に受けてから使うようにします。
Rigidbody rb = GetComponent<Rigidbody>();
if (rb != null)
{
rb.AddForce(Vector3.up);
}
👉
これだけで、
NullReferenceExceptionの多くは防げます。
基本② TryGetComponentを使う(Unity 2019.2以降)
Unityには、
null対策として非常に便利なメソッドがあります。
if (TryGetComponent(out Rigidbody rb))
{
rb.AddForce(Vector3.up);
}
この書き方のメリットは、
- nullチェック込み
- 意図が分かりやすい
- コードが簡潔
という点です。
👉
「存在するなら使う」
という処理には最適です。
基本③ 参照はキャッシュする
Updateなどで
毎フレームGetComponentを呼ぶのは、
可読性・パフォーマンスの両面でよくありません。
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
一度取得したら、
使い回す設計にしましょう。
基本④ SerializeFieldで明示的に参照する
重要なコンポーネントほど、
Inspectorで明示的に設定する方が安全です。
[SerializeField]
private Rigidbody rb;
これなら、
- GetComponent不要
- Inspectorで未設定ならすぐ気づく
- 構造変更に強い
というメリットがあります。
基本⑤ RequireComponentを使う
必須コンポーネントには、
必ずこれを付けましょう。
[RequireComponent(typeof(Rigidbody))]
public class Player : MonoBehaviour
{
}
これにより、
- 付け忘れが起きない
- チーム開発でも安全
になります。
「nullにならない前提」を捨てる
大切な考え方として、
GetComponentはnullになるもの
という前提でコードを書くことが重要です。
- あるはず
- 取れるはず
という思い込みが、
NullReferenceExceptionを生みます。
ここまでのまとめ
- 直接メソッドチェーンしない
- TryGetComponentを活用
- 参照はキャッシュする
- Inspector設定を信頼する
GetComponentを減らす設計の考え方
GetComponentは便利ですが、
多用しすぎるとバグの温床になります。
ここでは、
「GetComponentに頼らない設計」
という視点で考えてみましょう。
GetComponentは「その場しのぎ」になりやすい
よくあるコードがこちらです。
void Update()
{
GetComponent<Rigidbody>().AddForce(Vector3.up);
}
この書き方は、
- 毎フレーム探索が走る
- nullチェックがない
- 構造変更に弱い
という問題を抱えています。
👉
動いているからOK
になりがちな危険なコードです。
設計① 参照は「最初にまとめて取得」
基本はこの形を目指しましょう。
private Rigidbody rb;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
こうすることで、
- 取得失敗が早期に分かる
- Updateがシンプルになる
- nullチェック箇所が限定される
というメリットがあります。
設計② Inspector注入を使う
Unityでは、
Inspectorは立派なDI(依存性注入)です。
[SerializeField]
private Rigidbody rb;
この方法の利点:
- 実行前に設定ミスに気づける
- GetComponent不要
- Prefab構造変更に強い
👉
「このスクリプトは何に依存しているか」
が一目で分かります。
設計③ 責務を分離する
GetComponentが多いクラスは、
責務が多すぎるサインです。
例:
- 移動
- 攻撃
- アニメーション
- サウンド
を1つのスクリプトでやっていませんか?
👉
それぞれを別クラスに分けることで、
GetComponentの数も自然に減ります。
設計④ 初期化専用メソッドを用意する
依存関係がある場合は、
初期化を明示的に行うのも有効です。
public void Init(Rigidbody rb)
{
this.rb = rb;
}
- どこで参照が渡されるか分かる
- 実行順トラブルを避けやすい
「GetComponentが多い=悪」ではない
誤解しがちですが、
- 少数
- 初期化時のみ
- 意図が明確
であれば、
GetComponentは問題ありません。
👉
無意識に増えていないか
を意識することが大切です。
ここまでのまとめ
- GetComponentは初期化時にまとめる
- Inspector参照を活用する
- クラスの責務を見直す
- 設計でnullを減らす
よくある質問(FAQ)
Q1. GetComponentは毎回呼んでも大丈夫ですか?
技術的には可能ですが、
推奨はされません。
- 可読性が下がる
- nullチェックを忘れやすい
- Updateで多用すると無駄が増える
👉
初期化時に1回取得してキャッシュする
設計がおすすめです。
Q2. TryGetComponentとGetComponentの違いは何ですか?
TryGetComponent(out Rigidbody rb);
- 成功 / 失敗がboolで分かる
- nullチェック込み
- 意図が明確
👉
「存在すれば使う」処理には
TryGetComponentが最適です。
Q3. GetComponentがnullでもエラーが出ないことがあるのはなぜ?
GetComponent自体は、
nullを返すだけでエラーを出しません。
エラーが出るのは、
null.AddForce(...)
のように、
nullに対して処理を行った瞬間です。
Q4. Destroyしたオブジェクトがnull判定になるのはバグですか?
バグではありません。
Unity特有の仕様です。
if (enemy == null)
は、
Destroyされたオブジェクトも
trueになります。
Q5. GetComponentが多いコードはダメな設計ですか?
必ずしもダメではありません。
- 初期化時のみ
- 数が少ない
- 意図が明確
であれば問題ありません。
👉
ただし、
無意識に増えている場合は設計見直しのサイン
と考えましょう。
まとめ
GetComponentがnullになる理由は、
ほとんどの場合 Unityの仕様を誤解している ことが原因です。
重要なポイントを振り返りましょう。
- GetComponentは同じGameObjectのみ対象
- 見つからなければ null を返すのが正常
- Inspector設定ミスは最優先で確認
- Awake / Start の初期化順に注意
- Destroyされたオブジェクトはnull扱い
そして何より大切なのは、
GetComponentはnullになる前提で書く
という考え方です。
nullを正しく扱えば、
NullReferenceExceptionは
怖いエラーではなく、原因が分かるエラーになります。
0件のコメント
コメントはまだありません。最初の一人になりましょう!