【Unity】複数のゲームオブジェクトやコンポーネントの取得

2023年5月29日

C#では、配列やListによってまとめて処理することができる仕組みが用意されています
もちろんUnityでもC#構文を使って実現できますが、UnityではGameObjectやComponentといった概念がありますのでそれを利用する方法を示します

サンプルシーン

GameObjectを取得してから処理

ヒエラルキーのルート下のルートゲームオブジェクトを全て取得(子オブジェクトを除く)

空のゲームオブジェクトを作成して、名前をサンプルシーンのように変更します
Managerオブジェクトに以下のスクリプトをアタッチします

using UnityEngine;
using UnityEngine.SceneManagement;

public class Manager : MonoBehaviour
{
    void Start()
    {
        // アクティブなシーンを取得
        Scene currentScene = SceneManager.GetActiveScene();

        // シーン内のすべてのGameObjectを取得
        GameObject[] allObjects = currentScene.GetRootGameObjects();

        foreach (var obj in allObjects)
        {
            Debug.Log(obj.name);
        }
    }
}

Scene currentScene = SceneManager.GetActiveScene();

SceneManager.GetActiveScene()メソッドを使用して、現在アクティブなシーンを取得しています。シーンはゲームの特定の場面やレベルを表します。

GameObject[] allObjects = currentScene.GetRootGameObjects();

currentScene.GetRootGameObjects()メソッドを使用して、現在のシーン内のすべてのルートゲームオブジェクトを取得しています。ルートゲームオブジェクトはシーン内のヒエラルキーのトップレベルのオブジェクトを指します。

foreach (var obj in allObjects)

取得したすべてのゲームオブジェクトに対して、以下の処理を繰り返します。

Debug.Log(obj.name);

Debug.Log()メソッドを使用して、各ゲームオブジェクトの名前をコンソールに出力します。デバッグ目的で使用されるため、実行中のゲームには直接的な影響はありません。

結果

全てのオブジェクが取得されました

Main Camera
Directional Light
Cat
Dog
Sparrow
Falcon
Manager

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject[] objects = FindObjectsByType<GameObject>(FindObjectsSortMode.None);

        foreach (var obj in objects)
        {
            Debug.Log(obj.name);
        }
    }
}

特定の基本クラスを継承している派生クラスへのアクセス(基本クラスのメソッド呼び出し)

各ゲームオブジェクトに同名のスクリプトをアタッチします

using UnityEngine;
using UnityEngine.SceneManagement;

public class Manager : MonoBehaviour
{
    void Start()
    {
        // アクティブなシーンを取得
        Scene currentScene = SceneManager.GetActiveScene();

        // シーン内のすべてのGameObjectを取得
        GameObject[] allObjects = currentScene.GetRootGameObjects();

        foreach (var obj in allObjects)
        {
            obj.GetComponent<Animal>()?.Eat();
        }
    }
}

obj.GetComponent<Animal>()?.Eat();

各ゲームオブジェクトからAnimalというコンポーネントを取得し、Eat()メソッドを呼び出しています。GetComponent<Animal>()メソッドは、指定したゲームオブジェクトがAnimalコンポーネントを持っている場合はそのコンポーネントを返し、持っていない場合はnullを返します。?.演算子は、前の式がnullである場合にはメソッド呼び出しをスキップするため、Animalコンポーネントが存在しないゲームオブジェクトを無視します。

using UnityEngine;

public class Animal : MonoBehaviour
{
    public void Eat()
    {
        Debug.Log(gameObject.name + "は、動物なので食べる");
    }
}
public class Cat : Animal
{
}
public class Dog : Animal
{
}
public class Sparrow : Animal
{
}
public class Falcon : Animal
{
}

このコードの目的は、アクティブなシーン内のすべてのゲームオブジェクトからAnimalコンポーネントを取得し、Eat()メソッドを呼び出すことです。各ゲームオブジェクトがAnimalを持っている場合にのみ食事の処理が行われます。このコードは、例えば動物のキャラクターを含むシーンで、すべての動物が食事をする必要がある場合に使用されます

全体のクラス図

結果

Animalクラスを継承しているコンポーネント(スクリプト)のみにアクセスして、メソッドが実行されます

FalconManagerCatは、動物なので食べる
Dogは、動物なので食べる
Sparrowは、動物なので食べる
Falconは、動物なので食べる

特定の基本クラスを継承している派生クラスへのアクセス(派生クラスのメソッド呼び出し)

各ゲームオブジェクトに同名のスクリプトをアタッチします

using UnityEngine;
using UnityEngine.SceneManagement;

public class Manager : MonoBehaviour
{
    void Start()
    {
        // アクティブなシーンを取得
        Scene currentScene = SceneManager.GetActiveScene();

        // シーン内のすべてのGameObjectを取得
        GameObject[] allObjects = currentScene.GetRootGameObjects();

        foreach (var obj in allObjects)
        {
            obj.GetComponent<Animal>()?.MakeSounds();
        }
    }
}

obj.GetComponent<Animal>()?.MakeSounds();

各ゲームオブジェクトからAnimalというコンポーネントを取得し、MakeSounds()メソッドを呼び出しています。GetComponent<Animal>()メソッドは、指定したゲームオブジェクトがAnimalコンポーネントを持っている場合はそのコンポーネントを返し、持っていない場合はnullを返します。?.演算子は、前の式がnullである場合にはメソッド呼び出しをスキップするため、Animalコンポーネントが存在しないゲームオブジェクトを無視します

using UnityEngine;

public class Animal : MonoBehaviour
{
    public virtual void MakeSounds()
    {
    }
}
using UnityEngine;

public class Cat : Animal
{
    public override void MakeSounds()
    {
        Debug.Log("にゃー");
    }
}
using UnityEngine;

public class Dog : Animal
{
    public override void MakeSounds()
    {
        Debug.Log("わん");
    }
}
using UnityEngine;

public class Sparrow : Animal
{
    public override void MakeSounds()
    {
        Debug.Log("チュン");
    }
}
using UnityEngine;

public class Falcon : Animal
{
    public override void MakeSounds()
    {
        Debug.Log("キー");
    }
}

このコードの目的は、アクティブなシーン内のすべてのゲームオブジェクトからAnimalコンポーネントを取得し、Eat()メソッドを呼び出すことです。各ゲームオブジェクトがAnimalを持っている場合にのみ食事の処理が行われます。このコードは、例えば動物のキャラクターを含むシーンで、すべての動物が食事をする必要がある場合に使用されます

全体のクラス図

結果

Animalクラスを継承しているコンポーネント(スクリプト)のみにアクセスして、メソッドが実行されます

にゃー
わん
チュン
キー

特定の基本クラスを継承している派生クラスへのアクセス(派生クラスのメソッド呼び出し。抽象クラスを使う)

Animalクラス以外は同じになります

抽象クラスにし、また抽象メソッドを宣言することで、Animalクラスを継承している派生クラスにMakeSoundsメソッドを強制的に実装することを求めます(派生クラスにMakeSoundsメソッドが存在しないとエラーになります)

抽象メソッドは、実装を持ちません

using UnityEngine;

public abstract class Animal : MonoBehaviour
{
    public abstract void MakeSounds();
}

public abstract void MakeSounds();

MakeSounds() という名前の抽象メソッドを宣言しています。public キーワードはアクセス修飾子で、このメソッドが外部からアクセス可能であることを示します。abstract キーワードは、このメソッドが実装を持たず、派生クラスやインターフェースで実装されることを意味します。void キーワードは、このメソッドが戻り値を返さないことを示します。

抽象メソッド MakeSounds() の実装は、派生クラスや実装クラスによって提供される必要があることを示唆しています。具体的なクラスやインターフェースでこのメソッドをオーバーライドすることで、各クラスや実装に固有の音を生成するためのコードを提供することが期待されています

全体のクラス図

結果

Animalクラスを継承しているコンポーネント(スクリプト)のみにアクセスして、メソッドが実行されます

にゃー
わん
チュン
キー

特定のインターフェースを実装しているクラスへのアクセス(実装されたクラスのメソッド呼び出し)

各ゲームオブジェクトに同名のスクリプトをアタッチします

using UnityEngine;
using UnityEngine.SceneManagement;

public class Manager : MonoBehaviour
{
    void Start()
    {
        // アクティブなシーンを取得
        Scene currentScene = SceneManager.GetActiveScene();

        // シーン内のすべてのGameObjectを取得
        GameObject[] allObjects = currentScene.GetRootGameObjects();

        foreach (var obj in allObjects)
        {
            obj.GetComponent<IFlyable>()?.Fly();
        }
    }
}

obj.GetComponent()?.Fly();

objという変数にアクセスし、そのゲームオブジェクトから IFlyable というインターフェースを持つコンポーネントを取得します。GetComponent<IFlyable>() メソッドは、指定したゲームオブジェクトが IFlyable インターフェースを実装している場合はそのコンポーネントを返し、実装していない場合は null を返します。

?.Fly();: ?. 演算子を使用して、前の式の結果が null でない場合に Fly() メソッドを呼び出します。つまり、ゲームオブジェクトが IFlyable インターフェースを実装している場合にのみ、Fly() メソッドが呼び出されます。

using UnityEngine;

public class Cat : MonoBehaviour
{
}
using UnityEngine;

public class Dog : MonoBehaviour
{
}
using UnityEngine;

public class Sparrow : MonoBehaviour, IFlyable
{
    public void Fly()
    {
        Debug.Log("スズメを飛ばす");
    }
}

public class Sparrow : MonoBehaviour, IFlyable

Sparrowという名前のクラスを定義しています。このクラスはIFlyableインターフェースを実装していることを示しています。

IFlyableインターフェースを実装したSparrowクラスを定義し、スズメの飛行動作を制御するためのコードを提供することです。IFlyableインターフェースの設定で飛行可能なオブジェクトやキャラクターにも同様のインターフェースが使用され、それぞれが異なる飛行動作を実装することができるようになります

using UnityEngine;

public class Falcon : MonoBehaviour, IFlyable
{
    public void Fly()
    {
        Debug.Log("鷹を飛ばす");
    }
}

全体のクラス図

結果

IFlyableクラスを実装しているコンポーネント(スクリプト)のみにアクセスして、メソッドが実行されます

スズメを飛ばす
鷹を飛ばす

全ての組み合わせ

実行結果

クリックしたオブジェクトごとに処理を分けるサンプルになります

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            // クリック位置からRayを作成
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            // Rayがオブジェクトに衝突したかをチェック
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                // 衝突したオブジェクトを取得
                GameObject clickedObject = hit.collider.gameObject;

                // 取得したオブジェクトに対する処理を実行
                clickedObject.GetComponent<Animal>()?.MakeSounds();
                clickedObject.GetComponent<IFlyable>()?.Fly();
            }
        }
    }
}

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

Camera.mainはシーン内のメインカメラを指します。ScreenPointToRayメソッドは、マウスの位置からレイ(光線)を作成します。このレイは、マウスクリック位置からカメラの方向に伸びる仮想的な光線です。

if (Physics.Raycast(ray, out RaycastHit hit))

Physics.Raycastメソッドは、作成したレイが他のオブジェクトと衝突したかどうかをチェックします。衝突がある場合、衝突したオブジェクトに関する情報がRaycastHitオブジェクトに格納されます

GameObject clickedObject = hit.collider.gameObject;

RaycastHitオブジェクトのcollider.gameObjectプロパティを使用して、衝突したオブジェクト自体を取得します

clickedObject.GetComponent<Animal>()?.MakeSounds();

衝突したオブジェクトがAnimalコンポーネントを持っている場合、MakeSoundsメソッドを呼び出します。GetComponentメソッドは、指定したコンポーネントの参照を取得します。?.は、Animalコンポーネントが存在しない場合にnull参照例外を回避するためのセーフナビゲーション演算子です

clickedObject.GetComponent<IFlyable>()?.Fly();

衝突したオブジェクトがIFlyableインターフェースを持っている場合、Flyメソッドを呼び出します。インターフェースを持つオブジェクトは、飛行可能な動作を実装していることを意味します

このコードの主な目的は、マウスクリック時にオブジェクトとの衝突を検出し、衝突したオブジェクトに対して特定の処理を実行することです。例えば、Animalコンポーネントがアタッチされているオブジェクトは音を鳴らし、IFlyableインターフェースを実装しているオブジェクトは飛行します

全体のクラス図

直接コンポーネントを取得して処理

全てのゲームオブジェクトの全てのコンポーネント内から特定の基本クラスから継承している派生クラスを取得

コンポーネントを直接取得しています

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        Animal[] animals = FindObjectsByType<Animal>(FindObjectsSortMode.None);

        foreach (var animal in animals)
        {
            animal.Eat();
        }
    }
}

Animal[] animals = FindObjectsByType<Animal>(FindObjectsSortMode.None);

FindObjectsByTypeメソッドを使用して、Animalという型のオブジェクトを検索し、Animalの配列であるanimals変数に格納しています。FindObjectsSortMode.Noneは、オブジェクトのソート方法を指定するための引数であり、ここではソートを行わないように指定しています。