[Unity] コード上でのコンポーネント取得方法まとめ

説明

主に下記の5つがあります。

  1. インスペクターで直接設定([SerializeField] や public 変数)
  2. GetComponentとFindObjectOfType
  3. 子オブジェクトや親オブジェクトから取得
  4. シングルトンパターンを使用
  5. 依存性注入(Dependency Injection)

インスペクターで直接設定([SerializeField] や public 変数)

Unityエディターの インスペクター で直接オブジェクトやコンポーネントの参照を設定する方法です。この方法では、コード内で明示的に取得する必要がなく、あらかじめエディターから指定しておくことで参照を保持します。

特徴

  • エディター上で見える形で設定されるので、デバッグしやすい。
  • 初期化時に自動的に参照が設定されるため、パフォーマンスに優れる。

使用例

C#
public class MyScript : MonoBehaviour
{
    [SerializeField] private Rigidbody rb; // インスペクターから設定
    // もしくは public にすることでインスペクターに表示される
}
C#

Unityエディター内で該当するオブジェクトをドラッグ&ドロップして、この Rigidbody への参照を設定します。

使用のケース

  • 依存関係が少ないスクリプトやコンポーネントを簡単に取得したいとき。
  • 初期化時に一度だけ参照を設定すればよい場合。

GetComponentとFindObjectOfType

この2つがコンポーネントを取得する最も基本的なメソッドだと思います。

GetComponent

同じゲームオブジェクトにアタッチされている対象のコンポーネントを取得するメソッドです。

C#
// このゲームオブジェクトにアタッチされたRigidbodyコンポーネントを取得
Rigidbody rb = GetComponent<Rigidbody>();
rb.AddForce(Vector3.forward);
C#

FindObjectOfType

シーン内の最初に見つかった対象のコンポーネントを取得するメソッドです。

C#
// シーン内で最初に見つかったGameManagerを取得
GameManager gameManager = FindObjectOfType<GameManager>();
gameManager.StartGame();
C#

使用するケース

GetComponent を使うケース:

  • 同じゲームオブジェクト内のコンポーネントにアクセスする場合。
  • 依存関係のあるコンポーネントにアクセスする場合。
  • カスタムスクリプト間での通信。

FindObjectOfType を使うケース:

  • シーン全体にわたって特定のコンポーネントを検索する必要がある場合。
  • シングルトンパターンのオブジェクトにアクセスする場合。
  • 複数シーンにまたがるオブジェクトを検索する場合。

子オブジェクトや親オブジェクトから取得

オブジェクトの 親子関係 を利用して、子オブジェクトや親オブジェクトから対象のコンポーネントを取得する方法です。GetComponentInChildrenGetComponentInParent を使うことで、オブジェクトの階層内のコンポーネントを探索できます。(対象のコンポーネントを複数取得したい場合は GetComponentsInChildren や GetComponentsInParent が使えます。詳細は下記の注意点。)

GetComponentInChildren

子オブジェクトにアタッチされた対象のコンポーネントを取得します。

C#
// このオブジェクトの子オブジェクトからRigidbodyを取得
Rigidbody rb = GetComponentInChildren<Rigidbody>();
C#

GetComponentInParent

親オブジェクトにアタッチされた対象のコンポーネントを取得します。

C#
// このオブジェクトの親オブジェクトからRigidbodyを取得
Rigidbody rb = GetComponentInParent<Rigidbody>();
C#

使用するケース

  • 親子関係がある場合に、その階層内のコンポーネントにアクセスする必要がある場合。
  • 複数のオブジェクトで同じコンポーネントを使う場合に便利。

注意点

  • 自分自身も対象になる
  • 対象のコンポーネントが複数ある場合
  • 非アクティブなオブジェクトは対象外

GetComponentInChildren自分自身も対象に含まれるため、対象のコンポーネントが自身のゲームオブジェクトに存在している場合、そのコンポーネントが返される可能性があります。

解決策:
  • 自分自身を除いて取得するには、transform を使用して、特定の子オブジェクト階層のみからコンポーネントを取得するようにします。
C#
// 自分自身を除いて子オブジェクトのみから取得
Rigidbody rb = GetComponentInChildren<Rigidbody>(true); // trueで非アクティブな子も検索可能

if (rb.gameObject != gameObject) // 自身のオブジェクトではないことを確認
{
    rb.AddForce(Vector3.forward);
}
C#

GetComponentInChildrenGetComponentInParent は、最初に見つかったコンポーネントのみを返します。そのため、対象の子や親オブジェクト、加えて自分自身がもつコンポーネントの中に複数の対象のコンポーネントがある場合、意図したものが取得されない可能性があります。

解決策:

複数のコンポーネントが存在する場合、GetComponentsInChildren または GetComponentsInParent を使用して、すべてのコンポーネントを配列で取得し、特定の条件に基づいて適切なコンポーネントを選択します。

C#
// 子オブジェクトからすべてのRigidbodyコンポーネントを取得
Rigidbody[] allRigidbodies = GetComponentsInChildren<Rigidbody>();

foreach (Rigidbody rb in allRigidbodies)
{
    if (rb.CompareTag("Target")) // 特定の条件(タグなど)で絞り込む
    {
        rb.AddForce(Vector3.forward);
    }
}
C#

デフォルトでは、GetComponentInChildrenGetComponentInParent非アクティブのオブジェクトを検索しません。つまり、ゲームオブジェクトが非アクティブ状態の場合、そのオブジェクトにアタッチされているコンポーネントは取得できません。

解決策:

true を引数に渡すことで、非アクティブのオブジェクトも検索対象に含めることができます。

C#
// 非アクティブな子オブジェクトも検索対象にする
Rigidbody rb = GetComponentInChildren<Rigidbody>(true);
C#

シングルトンパターンを使用

ゲーム全体で1つだけ存在するオブジェクト(例えば、GameManagerなど)を参照するために、シングルトンパターンを使用することができます。この方法では、どのスクリプトからでも簡単にアクセスできるグローバルなインスタンスを作成します。

使用例

C#
public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private void Awake()
    {
        if (Instance == null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            Destroy(gameObject);
        }
    }
}
C#

他のスクリプトからは、以下のようにアクセスできます:

C#
GameManager.Instance.SomeMethod();
C#

使用のケース

  • グローバルにアクセスする必要がある管理オブジェクト(GameManagerやAudioManagerなど)を使いたい場合。
  • シーンを跨いで永続的に存在するオブジェクトが必要な場合。

依存性注入(Dependency Injection)

Unityでは標準で依存性注入(DI)はサポートされていませんが、外部ライブラリ(Zenjectなど)を使用して、オブジェクトやコンポーネントの参照を注入することができます。依存性注入を使うと、コードのモジュール性やテスト性が向上します。

使用例 (Zenject)

C#
public class Enemy : MonoBehaviour
{
    private Player player;

    [Inject]
    public void Construct(Player player)
    {
        this.player = player;
    }

    private void Start()
    {
        // 依存性注入されたplayerを使う
        Debug.Log("Player injected: " + player.name);
    }
}
C#

使用のケース

  • 大規模なプロジェクトで、モジュール間の依存関係を整理したい場合。
  • テスト可能で拡張性のある設計を重視したい場合。

最後に

Unityの必須項目であるコンポーネント取得ですが、自分の中で整理されていなかったのでまとめてみました。

コメント

タイトルとURLをコピーしました