【Unity】ゲームオブジェクトとコンポーネントの操作をスクリプトでシミュレート

Unityエディターで初期のシーンを構成し、実行(Playボタン)するとアタッチされているスクリプトが実行され、他のゲームオブジェクトにアタッチされているスクリプトが実行されるケースを考えてみます

シーン構成

2つのゲームオブジェクトを作成して、一方のスクリプトを実行して他方のスクリプトにアクセスするケースになります

イメージ

GemeObjectという名前のゲームオブジェクトにアタッチされているスクリプト内から、Playerという名前のゲームオブジェクトにアタッチされているShowMessageスクリプトのShowHelloメソッドにアクセス

他のゲームオブジェクトにアクセスする側

空のゲームオブジェクトの作成します(名前はデフォルトのGameObjectのまま)

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = GameObject.Find("Player");
        ShowMessage showMessage = obj.GetComponent<ShowMessage>();
        showMessage.ShowHello();
    }
}

GameObject obj = GameObject.Find("Player");

GameObject.Find()は、名前がPlayerのゲームオブジェクトを検索して取得します。そのオブジェクトをobj変数に代入します。

ShowMessage showMessage = obj.GetComponent<ShowMessage>();

obj変数が参照するゲームオブジェクトにアタッチされているShowMessageコンポーネントを取得し、showMessage変数に代入します。

showMessage.ShowHello();

ShowMessageコンポーネントのShowHello()メソッドを呼び出します。このメソッドは、"Hello!"という文字列をデバッグログに出力するものと仮定されます。

アクセスされる側

空のゲームオブジェクトをもう1つ作成します(名前をPlayer)とします

using UnityEngine;

public class ShowMessage : MonoBehaviour
{
    public void ShowHello()
    {
        Debug.Log("Hello");
    }
}

ShowHelloという公開されたメソッドは、このクラスに定義されています。このメソッドは、UnityのDebugクラスを使用して、"Hello"というメッセージをコンソールにログとして出力します。

このスクリプトは、Unityのゲームオブジェクトにアタッチされ、オブジェクトがアクティブ化されたときに、ShowHelloメソッドが呼び出されます。このスクリプトがアタッチされたオブジェクトを実行すると、"Hello"というメッセージがコンソールに表示されます。

実行結果

Hello

Playerゲームオブジェクト作成とスクリプトアクセス(コンポーネントアクセス)のシミュレート

通常は、プログラマーはゲームオブジェクトを直接作成することはありません(Instantiateメソッドを使って作成)

今回は、内部の動作を学習するためにあえて作成してみます

準備

上記で作成したシーンからPlayerゲームオブジェクトを削除します

Managerスクリプトの更新で、同様な結果(Helloと表示)を得るようにします

ゲームオブジェクトをNewキーワードで作成

テストコード

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
    }
}

実行結果

ヒエラルキーウィンドウに New Game Objectという名前のゲームオブジェクトが作成されています(コンポーネントはTransformだけ作成されています)

作成されたゲームオブジェクトの名前をPlayerに変更

テストコード

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.name = "Player";
    }
}

実行結果

ヒエラルキーウィンドウに New Game Objectという名前がPlayerに変更されています

PlayerゲームオブジェクトにコンポーネントとしてのShowMessageスクリプトを追加

テストコード

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.name = "Player";
        obj.AddComponent<ShowMessage>();
    }
}

実行結果

Playerゲームオブジェクトにコンポーネントが追加されました

Playerゲームオブジェクトにアタッチされたコンポーネント(ShowMessageスクリプト)の参照を取得し、ShowHelloメソッドを実行

テストコード

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.name = "Player";
        obj.AddComponent<ShowMessage>();
        ShowMessage showMessage = obj.GetComponent<ShowMessage>();
        showMessage.ShowHello();
    }
}

実行結果

実行前は、GameObjectしかありませんが、実行すると、Playerゲームオブジェクトが作成され、コンポーネントが追加され、さらに実行されることでHelloと表示されるのがわかります

おまけ

推奨するわけでありませんが、同じコードになります

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject player = new GameObject("Player");
        player.AddComponent<ShowMessage>().ShowHello();
    }
}
using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        new GameObject("Player", typeof(ShowMessage))
             .GetComponent<ShowMessage>()
             .ShowHello();
    }
}

C#コードでシミュレートしたら・・・

違いを見比べてみましょう

シンプルなサンプル

using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.name = "Player";
        obj.ShowHello();
    }
}

class GameObject
{
    public string name;

    public void ShowHello()
    {
        Debug.Log("Hello");
    }
}

コンポーネントのシミュレートを追加すると・・・

難しいですが、もう少し、テストコードに近づけてみましょう

using System.Collections.Generic;
using UnityEngine;

public class Manager : MonoBehaviour
{
    void Start()
    {
        GameObject obj = new GameObject();
        obj.name = "Player";
        obj.AddComponent<ShowMessage2>();

        ShowMessage2 showMessage2 = obj.GetComponent<ShowMessage2>();

        showMessage2.ShowHello();

    }
}

class GameObject
{
    List<Component> components = new List<Component>();

    public string name { get; set; }

    public T AddComponent<T>() where T : Component, new()
    {
        T component = new T();
        components.Add(component);
        return component;
    }

    public T GetComponent<T>() where T : Component
    {
        return components.Find(component => component is T) as T;
    }

}
class Component { }

class ShowMessage2 : Component
{
    public void ShowHello()
    {
        Debug.Log("Hello");
    }
}

Startメソッドは、新しいゲームオブジェクトを作成し、その名前を"Player"に設定し、ShowMessage2という新しいコンポーネントを追加し、ShowMessage2コンポーネントのShowHelloメソッドを呼び出して、"Hello"というメッセージを表示します。

GameObjectクラスはUnityで使用されるGameObjectクラスを簡略化したもので、コンポーネントを追加・削除するためのメソッドを提供しています。また、nameプロパティを持ち、GameObjectの名前を取得・設定できます。

ComponentクラスはUnityで使用されるコンポーネントクラスを簡略化したもので、派生クラスに継承させるためのクラスです。

ShowMessage2クラスは、Componentを継承しています。ShowMessage2クラスはShowHelloメソッドを持ち、その中で"Hello"というメッセージを表示するために、UnityのDebug.Logメソッドを使用しています。ShowMessage2コンポーネントをGameObjectに追加し、ShowHelloメソッドを呼び出すことで、このメッセージが表示されます。

AddComponentメソッドのシミュレート解説

public T AddComponent<T>() where T : Component, new()
{
    T component = new T();
    components.Add(component);
    return component;
}

このコードは、AddComponentというジェネリックメソッドを定義しています。このメソッドは、指定されたジェネリック型パラメータTを持つComponentの派生クラスのインスタンスを作成し、List<Component>componentsに追加し、最後に作成したインスタンスを返します。

このメソッドは、型パラメータTComponentの派生クラスであることを制約としているため、このメソッドを呼び出すときにTに指定できる型はComponentを継承している型に限定されます。また、where T : new()という制約があるため、Tはパラメータなしのコンストラクタを持っている必要があります。

このメソッドを呼び出すことで、指定した型のComponentを作成し、componentsに追加することができます。たとえば、AddComponent<Transform>()と呼び出すと、Transformクラスのインスタンスが作成され、componentsに追加されます。このように、ジェネリックメソッドを使用することで、コードの再利用性を高め、汎用的なコードを作成することができます。

GetComponentメソッドのシミュレート解説

public T GetComponent<T>() where T : Component
{
    return components.Find(component => component is T) as T;
}

このコードは、ジェネリックメソッドの GetComponent を定義しています。このメソッドは、指定された型 TComponent を検索して返します。

このメソッドは、where キーワードを使用して型制約を設定しています。T 型は Component クラスのサブクラスである必要があります。

Find メソッドは、リストの要素を検索し、指定された条件に一致する最初の要素を返します。このコードでは、components リストの要素をループして、型 T であるコンポーネントを検索しています。一致する要素が見つかった場合、as 演算子を使用してその要素を型 T にキャストして返します。

C#,Unity

Posted by hidepon