Unity Term Book
コアとアーキテクチャ

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 ではなく → PlayerMovementPlayerCombatPlayerHealth に分割します。

Composition vs Inheritance

❌ Inheritance(複雑)

Entity
└─ Character
├─ Player
├─ PlayerWithGun
└─ PlayerWithSword
└─ Enemy
├─ EnemyRanged
└─ EnemyMelee

✅ Composition (Unity)

Movement+Health+GunShoot→ Player+Gun
Movement+Health+AIBrain→ Enemy
Movement+SwordSlash→ Player+Sword

ハンズオン手順

1

Inspector から Component をアタッチ

GO を選択 → Add Component → 名前で検索(Rigidbody、Audio Source…)。

2

Awake() で GetComponent をキャッシュ

_rb = GetComponent<Rigidbody>(); をプライベートフィールドに保存します。

3

Component の有効 / 無効

component.enabled = false/true —— 削除せず、一時的に無効化するだけです。

4

Interface で疎結合に通信

スクリプト同士のハード依存を避け、IDamageableIInteractable… を使います。

インタラクティブシミュレーター

Component のオン/オフを切り替え、キャラクターの振る舞いがどう変わるか確認しましょう。

GameObject "Player" のコンポーネントを切り替え

😐
👤
No components
Player(GameObject)

現在の挙動

// GetComponent calls needed:

コード例

基本
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 チェック