Component
Component は GameObject にアタッチする独立した構成要素です。各 Component が振る舞いの一部だけを担い、深い継承より Composition(組み合わせ)でゲームを組み立てます。GetComponent のキャッシュや Interface による疎結合の書き方も学べます。
想像してみてください...
Component はレーシングカーのアップグレードモジュールのようです。シャーシ(GameObject)だけでは何もできず、エンジン(Rigidbody)、ホイール(Collider)、ステアリング(Script)、ライト(Light)を取り付けて初めて完成します。各モジュールは独立しており、ライトを外してもエンジンは動き続けます。
概念の詳細
Component は Unity アーキテクチャの中心です。巨大な継承ツリーに頼るのではなく、 Unity は 小さく独立した Component を組み合わせること—— Composition over Inheritance(継承より組み合わせ)を推奨します。
すべての Component は UnityEngine.Component を継承するため、
gameObject(ホスト)と transform にアクセスできます。
参照の取得:GetComponent<T>()。子を検索:
GetComponentInChildren<T>()。親を検索:
GetComponentInParent<T>()。
重要なスキルは
大きなスクリプトを複数の小さな Component に分けるタイミングを知ることです。
500 行の PlayerController ではなく → PlayerMovement、
PlayerCombat、PlayerHealth に分割します。
Composition vs Inheritance
❌ Inheritance(複雑)
✅ Composition (Unity)
ハンズオン手順
Inspector から Component をアタッチ
GO を選択 → Add Component → 名前で検索(Rigidbody、Audio Source…)。
Awake() で GetComponent をキャッシュ
_rb = GetComponent<Rigidbody>(); をプライベートフィールドに保存します。
Component の有効 / 無効
component.enabled = false/true —— 削除せず、一時的に無効化するだけです。
Interface で疎結合に通信
スクリプト同士のハード依存を避け、IDamageable、IInteractable… を使います。
インタラクティブシミュレーター
Component のオン/オフを切り替え、キャラクターの振る舞いがどう変わるか確認しましょう。
GameObject "Player" のコンポーネントを切り替え
現在の挙動
コード例
基本using UnityEngine;
public class ComponentBasics : MonoBehaviour
{
// Cache: fetch the component once, not every frame
private Rigidbody _rb;
private Collider _col;
private Renderer _renderer;
void Awake()
{
_rb = GetComponent<Rigidbody>();
_col = GetComponent<Collider>();
_renderer = GetComponent<Renderer>();
}
void Start()
{
// Null-check before use (safe)
if (_rb != null) _rb.mass = 2f;
_renderer.enabled = false; // hide the object
_col.enabled = false; // disable collisions
}
public void MakeInvincible()
{
// Disable the AI script (do not remove the component)
EnemyAI ai = GetComponent<EnemyAI>();
if (ai != null) ai.enabled = false;
}
}コード例
上級using UnityEngine;
// Interface: a contract for any component that can take damage
public interface IDamageable
{
void TakeDamage(int amount);
}
// Health component: only manages HP, knows nothing about combat
public class Health : MonoBehaviour, IDamageable
{
[SerializeField] private int maxHP = 100;
private int _currentHP;
void Awake() => _currentHP = maxHP;
public void TakeDamage(int amount)
{
_currentHP -= amount;
if (_currentHP <= 0) Destroy(gameObject);
}
}
// Bullet component: deals damage without knowing Health exists
public class Bullet : MonoBehaviour
{
[SerializeField] private int damage = 25;
void OnCollisionEnter(Collision col)
{
// Ask: does this object implement IDamageable? → loose coupling
col.gameObject.GetComponent<IDamageable>()?.TakeDamage(damage);
Destroy(gameObject);
}
}📌 要点
- ▸Component = GameObject にアタッチする独立モジュール
- ▸深い継承より Composition(小さな Component の組み合わせ)を優先
- ▸必ず
Awake()でプライベートフィールドにキャッシュ - ▸Interface で直接依存せずに Component 同士が会話する
⚠️ よくあるミス
❌ 1 つのスクリプトに仕事を詰め込みすぎる
移動・HP・武器・UI まで抱える 500 行スクリプト
✅ PlayerMovement、PlayerHealth、PlayerWeapon に分割する
❌ Update() 内で GetComponent<>() を呼ぶ
コンポーネント未アタッチだと NullReferenceException
✅ Awake() でキャッシュし、使う前に null チェック