タイトル: コンポーネントの取得
SEOタイトル: Unity Component 取得とキャッシュの完全ガイド(GetComponent/RequireComponent/TryGetComponent)
| この記事の要点 |
|
GetComponent の基本
using UnityEngine;
public class Sample : MonoBehaviour
{
void Start()
{
// ジェネリック版(推奨)
Rigidbody rb1 = GetComponent();
// 型引数なし(古い書き方、Cast 必要)
Rigidbody rb2 = (Rigidbody)GetComponent(typeof(Rigidbody));
// 文字列版(遅い、補完効かない、リフレクション)
Rigidbody rb3 = (Rigidbody)GetComponent("Rigidbody");
// 取得できなかったときは null
if (rb1 == null) Debug.LogWarning("Rigidbody missing");
}
}
3 つの書き方の中で、ジェネリック版が最速かつ最も保守的です。文字列版はリフレクションを使うため数倍遅く、リネーム時にコンパイラがミスを検知できません。
TryGetComponent(GC 回避)
Unity 2019.2 以降では TryGetComponent が利用できます。コンポーネント未存在時に null を返す代わりに false を返し、UnityEngine.Object との比較で発生する GC ゴミを回避します。
// ❌ 旧来: null 比較が偽 null 経由で GC を発生させる
var rb = GetComponent();
if (rb != null) rb.AddForce(Vector3.up);
// ✅ 推奨: TryGetComponent
if (TryGetComponent(out Rigidbody rb)) {
rb.AddForce(Vector3.up);
}
キャッシュは必須
GetComponent は毎呼び出しで内部リスト走査します。Update で呼ぶと毎フレームのコストが積み重なります。Awake / Start で取得して private フィールドに保持してください。
public class Mover : MonoBehaviour
{
private Rigidbody _rb;
private Animator _anim;
void Awake()
{
_rb = GetComponent();
_anim = GetComponent();
}
void FixedUpdate()
{
_rb.AddForce(transform.forward * 10f);
_anim.SetFloat("Speed", _rb.velocity.magnitude);
}
}
RequireComponent 属性
「このスクリプトには必ず Rigidbody が必要」と表現したい場合、[RequireComponent] 属性を使います。
- アタッチ時に未存在ならば自動追加
- 該当コンポーネントを Inspector から削除しようとすると警告が出る
- 依存関係を宣言的に表現でき、ドキュメントとしても機能
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(Collider))]
public class Bullet : MonoBehaviour
{
private Rigidbody _rb;
void Awake() { _rb = GetComponent(); }
// Rigidbody が必ず居るので null チェック不要
}
子・親からの取得
// 自身+子孫
var aud = GetComponentInChildren();
// 自身+祖先
var canvas = GetComponentInParent
コンポーネント追加と削除
// 追加
var rb = gameObject.AddComponent();
rb.mass = 2f;
// 削除(GameObject ではなく Component を渡す!)
Destroy(rb);
// すぐ削除(Editor 用)
DestroyImmediate(rb);
// 同型を複数つけられないコンポーネント(Rigidbody 等)に重複 AddComponent するとエラー
// 重複は AudioSource など限られた型のみ可
パフォーマンス指針
| 呼び方 | 速度 | 使い所 |
|---|---|---|
| キャッシュ済フィールド | ★★★★★ | Update / FixedUpdate |
TryGetComponent | ★★★★ | 初回 / 条件分岐 |
GetComponent | ★★★ | Awake / Start |
GetComponentInChildren | ★★ | 必要時のみ |
GetComponent("StringName") | ★ | 使わない |
ECS(DOTS)との比較
Unity DOTS の ECS(Entity Component System)では、コンポーネントは IComponentData 構造体としてエンティティに付与されます。MonoBehaviour とは設計思想が大きく異なり、キャッシュフレンドリで CPU SIMD 最適化がしやすい代わりに、API がまったく別物になります。大量オブジェクト(数千〜数万)を扱うときに検討してください。
// ECS の例(IJobEntity)
[BurstCompile]
partial struct MoveJob : IJobEntity
{
public float dt;
void Execute(ref LocalTransform t, in MoveSpeed s)
{
t.Position += new float3(0, 0, s.Value * dt);
}
}
よくあるトラブル
| 症状 | 原因 | 対処 |
|---|---|---|
| NullReferenceException | GetComponent が null を返した | RequireComponent / TryGetComponent |
| FixedUpdate が遅い | 毎回 GetComponent | Awake でキャッシュ |
| 同じ Rigidbody が 2 個ついた | AddComponent を 2 回呼んだ | 事前に TryGetComponent で確認 |
| Destroy(gameObject) しちゃった | Component 削除のつもりが GO ごと消失 | Component 変数を Destroy に渡す |
FAQ
Q: 同じ GameObject に同じコンポーネントを複数付けたい
A: AudioSource は複数可。Rigidbody や Collider 系は不可(特定の Collider のみ可)。複数アタッチが許容されるかは型ごとに違います。
Q: インターフェイス型で取得できる?
A: できます。GetComponent でインターフェイスを実装した MonoBehaviour が返ります。
Q: コンポーネント削除のタイミングは?
A: Destroy はフレーム終了時に実行されます。即時必要なら DestroyImmediate(ただしランタイムでは使わない)。