UnityEngineのシミュレーションで学習

2026年4月1日

広告

学習用シーン

次のような、3DプロジェクトにCubeを1つ配置したシーンで考えましょう

Hierarchy

シーンの管理のために使われます
シーンに初期状態で配置するゲームオブジェクトのリストになります

public class Scene
{
    public string name { get; set; }

    List<GameObject> gameObjects;
}

このコードは、C#で作られた「Scene」という名前のクラスを定義しています。

このクラスには、2つのフィールドがあります。

1つ目は、名前を表す「name」というプロパティです。このプロパティは、外部から読み取りや書き込みが可能であり、クラス内部でも使用されることがあります。

2つ目は、ゲームオブジェクトを格納するための「gameObjects」というList<T>型のフィールドです。このフィールドは、Sceneクラス内部で使用されるため、publicなアクセス修飾子は付けられていません。

このコードが定義されている場所が、名前空間(namespace)やその他のクラス定義があるファイルに依存しますが、一般的にはゲーム開発などのコンピュータグラフィックス分野において、シーン管理やオブジェクト管理などの用途で使用されます

Inspector

Cubeのインスペクターを個別にみていきましょう

Cubeが所有しているコンポーネントのリストが宣言されます
1つ1つのコンポーネントを継承しているインスタンスが代入されます

public class GameObject
{
    List<Component> components;
}

このコードは、GameObjectという名前のクラスを定義しています。このクラスには、List<Component>型のcomponentsというフィールドが含まれています。

Listは、複数のオブジェクトを保持するためのデータ構造であり、Componentは、GameObjectの構成要素を表すための基本クラスです。つまり、GameObjectには複数のComponentを持つことができます。

List<Component>は、ジェネリックスと呼ばれる機能を使用しています。ジェネリックスを使用することで、Listクラスは、指定された型のオブジェクトを保持できるようになります。ここでは、Component型のオブジェクトを保持するために使用されています。

Transformコンポーネント

Cubeのインスペクター

サンプルでは、Positionプロパティのみピックアップしています

public class Transform : Component
{
    public  Vector3 position { get; set; }
}

このコードは、Unityエンジンで使用されるスクリプトの一部です。Transformクラスは、Unityオブジェクトの位置、回転、スケール情報を表すために使用されます。

このTransformクラスは、Componentクラスを継承しており、positionという名前のVector3型のプロパティを持っています。プロパティは、外部からアクセス可能であり、値を取得および設定することができます。したがって、他のスクリプトからTransform.positionにアクセスして、オブジェクトの位置を取得または変更できます。

なお、Vector3はUnityエンジンにおいて、3次元のベクトルを表すために使用される型です。Vector3型の変数は、x、y、およびzの3つの浮動小数点数値を持ちます。したがって、Transform.positionは、オブジェクトの3次元空間上での位置を表します

public struct Vector3
{
    public float x;
    public float y;
    public float z;

    public Vector3(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

このコードは、3次元ベクトルを表すための構造体を定義しています。構造体は、値型の一種であり、メモリ効率がよく、単純なデータ構造を表すのに適しています。

この構造体は、x、y、zの3つのfloat型のフィールドを持ちます。これらのフィールドは、Vector3構造体のインスタンスが作成されたときに初期化されます。

また、この構造体には、x、y、zの値を引数として受け取り、それらの値をフィールドに設定するパブリックなコンストラクタがあります。このコンストラクタは、Vector3構造体のインスタンスを作成するときに使用されます。

このように、Vector3構造体は、3つのfloat型の値をまとめて扱うための単純なデータ構造を提供します。これは、3Dグラフィックスや物理シミュレーションなどの分野でよく使われます。

BoxColliderコンポーネント

Cubeのインスペクター

サンプルでは、Centerプロパティのみピックアップしています

public class BoxCollider : Component
{
    public  Vector3 center { get; set; }
}

このコードは、BoxColliderというクラスを定義しています。

BoxColliderはComponentというクラスを継承しており、BoxColliderのインスタンスはGameObjectにアタッチされ、物理的な衝突判定を実現するための情報を保持するコンポーネントです。

BoxColliderクラスには、centerというpublicなVector3型のプロパティが定義されています。このプロパティは、BoxColliderが設定されたGameObjectの中心座標を表します。

また、centerプロパティはgetとsetメソッドが定義されているため、外部から値の取得と設定が可能です。

GetComponent<T>メソッドについて

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

    public T? GetComponent<T>() where T : Component
    {
        return components.FirstOrDefault(comp => comp is T) as T;
    }
}

このコードは、ゲームオブジェクトのコンポーネントを管理するためのクラス GameObject を定義しています。このクラスは、ジェネリックメソッド GetComponent<T> を公開し、指定された型 T のコンポーネントを返します。

GetComponent<T> メソッドは、where T : Component という制約付きジェネリックメソッドであり、T 型が Component クラスを継承していることが要件となっています。メソッドの戻り値は、T 型のnullableなオブジェクトで、components フィールドに格納されているコンポーネントリストから最初に見つかった型が T であるコンポーネントを取得します。

このコードでは、components フィールドは List<Component> 型のリストであり、ゲームオブジェクトにアタッチされているすべてのコンポーネントが格納されます。FirstOrDefault メソッドは、components リストの最初に見つかった要素を取得します。comp => comp.GetType() == typeof(T) は、ラムダ式で、comp を引数として取り、comp の型が T であるかどうかをチェックします。最後に、as T を使用して、取得したオブジェクトを T 型にキャストします。見つからなかった場合、GetComponent<T> メソッドは null を返します。

このコードは、Unityなどのゲームエンジンでよく見られる、コンポーネントベースのアーキテクチャにおいて、オブジェクトのコンポーネントを管理するために使用される一般的な手法です。

public class Component
{
    public GameObject? gameObject;

    public Transform transform
    {
        get
        {
            return GetComponent<Transform>();
        }
    }

    public T? GetComponent<T>() where T : Component
    {
        return gameObject.GetComponent<T>();
    }
}

このコードは、Unityエンジンで使われる Component クラスの定義を示しています。

Component クラスは、Unityのオブジェクトにアタッチされるすべてのコンポーネントのベースクラスであり、gameObjecttransform の2つの公開プロパティを持っています。

gameObject は、このコンポーネントがアタッチされているゲームオブジェクトへの参照を持ちます。gameObject プロパティは GameObject 型のNullableなプロパティで、Nullableにすることでアタッチされているゲームオブジェクトがない場合に null を返すことができます。

transform プロパティは、このコンポーネントがアタッチされているゲームオブジェクトのTransformコンポーネントを返します。transform プロパティは、getアクセサでGetComponent<Transform>()メソッドを呼び出し、その結果を返します。

GetComponent<T>() メソッドは、指定された型Tに一致するコンポーネントを返します。これは、このコンポーネントがアタッチされているゲームオブジェクトから GetComponent<T>() メソッドを呼び出すことによって実現されます。ここでは、ジェネリック制約 where T : Component を使用しているため、Tは Component クラスかそのサブクラスである必要があります。また、GetComponent<T>() メソッドは、T型のコンポーネントが見つからない場合はnullを返します。ここでも、ジェネリック型パラメータTはNullableとして宣言されているため、nullを返すことができます。

全コード

このコードは、UnityのGameObjectとTransform、Rigidbodyなどの概念を模倣して、C#のコードで表現しています。以下は、各クラスとその役割についての説明です。

  • GameObject:Unityのゲームオブジェクトに相当するクラスで、コンポーネントを追加したり、ゲームオブジェクト自体の状態を管理します。
  • Transform:UnityのTransformコンポーネントに相当するクラスで、ゲームオブジェクトの位置、回転、スケールなどを管理します。
  • Rigidbody:UnityのRigidbodyコンポーネントに相当するクラスで、ゲームオブジェクトの物理的な動きを管理します。
  • BoxCollider : ゲームオブジェクトに物理的な境界を与えるためのコンポーネントの1つです。Box Colliderは、直方体の形状を持ち、そのオブジェクトの形状を定義するために使用されます。
  • MonoBehaviour:UnityのMonoBehaviourクラスに相当するクラスで、Unityのライフサイクルメソッド(Start、Update、Awakeなど)を実装するための基底クラスです。

このコードでは、MainメソッドでGameObjectを作成し、Transform、Rigidbody、そしてManagerコンポーネントを追加しています。Managerコンポーネントは、MonoBehaviourを継承しており、StartメソッドでRigidbodyの使用重力を有効にし、Transformの位置を変更し、Debug.Logを使用して位置を出力しています。Debugクラスは、単純なデバッグログをコンソールに出力するために使用されます。

このコードは、Unityの開発における基本的な概念を理解するのに役立つでしょう。

using UnityEngine;

internal class Program
{
    private static void Main()
    {
        var car1 = new GameObject();
        car1.AddComponent<Transform>();
        car1.AddComponent<Rigidbody>();
        car1.AddComponent<Manager>().Start();
    }
}

public class Scene
{
    public string? name { get; set; }

    List<GameObject>? gameObjects;
}

public class Manager : MonoBehaviour
{
    private Rigidbody? rigidBody;

    public void Start()
    {
        rigidBody = GetComponent<Rigidbody>();
        rigidBody.useGravity = true;

        transform.position = new Vector3(1, 2, 3);

        transform.Translate(new Vector3(10, 0, 0));

        Debug.Log($"PosX = {transform.position.x}");

        gameObject.AddComponent<BoxCollider>();

        BoxCollider boxCollider = GetComponent<BoxCollider>();
        boxCollider.center = new Vector3(10, 20, 30);

        Debug.Log($"BoxCollicerCenterX = {boxCollider.center.x}");
    }
}

public class Debug
{
    public static void Log(object value)
    {
        Console.WriteLine(value);
    }
}

namespace UnityEngine
{

    public class Debug
    {
        public static void Log(object value)
        {
            Console.WriteLine(value);
        }
    }
    public class GameObject
    {
        private readonly List<Component> components = new List<Component>();

        public T? GetComponent<T>() where T : Component
        {
            return components.FirstOrDefault(comp => comp.GetType() == typeof(T)) as T;
        }

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

    public class Component
    {
        public GameObject? gameObject;

        public Transform transform
        {
            get
            {
                return GetComponent<Transform>()!;
            }
        }

        public T? GetComponent<T>() where T : Component
        {
            return gameObject!.GetComponent<T>();
        }
    }

    public class MonoBehaviour : Component
    {
    }

    public class Transform : Component
    {
        public Vector3 position { get; set; }

        public void Translate(Vector3 translation)
        {
            position += translation;
        }
    }

    public struct Vector3
    {
        public float x;
        public float y;
        public float z;

        public Vector3(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public static Vector3 operator +(Vector3 a, Vector3 b)
        {
            return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
        }
    }

    public class BoxCollider : Component
    {
        public Vector3 center { get; set; }
    }

    public class Rigidbody : Component
    {
        public bool useGravity { get; set; }
    }
}

機能説明

演算子のオーバーロード

public static Vector3 operator +(Vector3 a, Vector3 b)
{
    return new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
}

このコードは、3次元ベクトル(Vector3)の加算演算子(+)のオーバーロード実装を示しています。

Vector3型の引数aとbを取り、それらのx、y、zの成分を足し合わせた新しいVector3を作成しています。具体的には、x成分はa.xとb.xを加算し、y成分はa.yとb.yを加算し、z成分はa.zとb.zを加算した新しいVector3を返します。

このようにオーバーロード演算子を実装することで、2つのVector3を直感的に足し合わせることができます。例えば、以下のように記述することができます。

Vector3 a = new Vector3(1, 2, 3);
Vector3 b = new Vector3(4, 5, 6);
Vector3 c = a + b; // c = (5, 7, 9)

クラス図

このコードは、Unity game engine におけるゲームオブジェクトの作成と操作を行うためのサンプルコードです。以下に、各部分の説明を示します。

最初の部分は、Unity Engine の名前空間である「UnityEngine」を使用しています。そして、Program クラスの Main メソッドでは、新しい GameObject インスタンスを作成し、その上に Transform、Rigidbody、Manager コンポーネントを追加しています。

Scene クラスは、シーンの名前とそのシーン内に存在するすべての GameObject を格納するために使用されるクラスです。

Manager クラスは、MonoBehaviour クラスを継承しています。MonoBehaviour は、Unity game engine 内でスクリプトを動作させるための基底クラスであり、ゲームオブジェクト上で実行されます。Manager クラスでは、Start メソッドが定義されており、ゲームオブジェクトの Rigidbody コンポーネントの使用重力フラグを有効にしたり、Transform コンポーネントの位置を変更するなどの動作を行います。また、BoxCollider コンポーネントを追加し、その中心座標を変更するなどの動作も行っています。

Debug クラスは、単純なログ出力を行う静的メソッド Log を定義しています。

UnityEngine 名前空間には、GameObject、Component、MonoBehaviour、Transform、BoxCollider、Rigidbody の各クラスが定義されており、ゲームオブジェクトの作成と操作を行うための機能を提供しています。GameObject クラスは、コンポーネントを持つ Unity ゲームオブジェクトを表します。Component クラスは、GameObject 上のコンポーネントを表します。Transform クラスは、GameObject の座標、回転、拡大縮小を制御するコンポーネントを表します。BoxCollider クラスは、物理エンジンとの衝突判定のための矩形のコリジョン形状を定義します。Rigidbody クラスは、ゲームオブジェクトに物理挙動を付加するコンポーネントです。そして、MonoBehaviour クラスは、ゲームオブジェクト上でスクリプトを実行するための基底クラスです。

実行結果

PosX = 11
BoxCollicerCenterX = 10

おまけ

ゲームオブジェクトを見つける(GameObject.Findメソッド)を追加しています

このコードはUnityのシーンを模倣するための簡単なコード例です。このコードでは、GameObject、Transform、Rigidbody、BoxCollider、そして自作のManagerクラスが定義されています。

まず、ProgramクラスのMainメソッドで、新しいSceneオブジェクトを作成し、名前を"Test"に設定しています。次に、2つの車両を表すGameObjectを作成し、それぞれにTransformとRigidbodyコンポーネントを追加します。car2には名前を設定しています。最後に、car1にManagerコンポーネントを追加しています。

ManagerクラスのStartメソッドでは、car1のRigidbodyコンポーネントを取得して、useGravityプロパティをtrueに設定しています。transformプロパティを使用して、car1の位置を変更し、Translateメソッドを使用して、さらに移動しています。Debugクラスを使用して、コンソールに現在の位置を表示しています。

また、car2にBoxColliderを追加し、centerプロパティを変更しています。GameObject.Findメソッドを使用して、名前が"car2″のGameObjectを検索しています。car2の位置を変更し、Translateメソッドを使用して、さらに移動しています。Debugクラスを使用して、コンソールに現在の位置を表示しています。

このコードはUnityの機能の一部を模倣していますが、実際のUnityプロジェクトとは異なることに注意してください。また、このコードはC#言語を使用しており、UnityのAPIとは異なることにも注意してください。

using UnityEngine;

/// <summary>
/// UnityEngineの内部コントロールのイメージです
/// 実行前の初期画面のデザイン設計と考えましょう
/// </summary>
internal class Program
{
    private static void Main()
    {
        // シーンを作成しアクティブに設定
        var scene = new Scene { name = "Test" };
        SceneManager.SetActiveScene(scene);

        // car1 を作成してシーンに登録
        var car1 = new GameObject { name = "Car1" };
        car1.AddComponent<Transform>();
        car1.AddComponent<Rigidbody>();
        scene.AddGameObject(car1);

        // car2 を作成してシーンに登録
        var car2 = new GameObject { name = "Car2" };
        car2.AddComponent<Transform>();
        car2.AddComponent<Rigidbody>();
        scene.AddGameObject(car2);

        // car1 に Manager をアタッチして Start を呼び出す
        car1.AddComponent<Manager>().Start();
    }
}

/// <summary>
/// car1 オブジェクトにアタッチするスクリプトのイメージです
/// </summary>
public class Manager : MonoBehaviour
{
    private Rigidbody? rigidBody;

    public void Start()
    {
        // Rigidbody を取得して重力を有効化
        rigidBody = GetComponent<Rigidbody>();
        if (rigidBody != null)
            rigidBody.useGravity = true;

        // 位置を設定して移動
        transform.position = new Vector3(1, 2, 3);
        transform.Translate(new Vector3(10, 0, 0));
        Debug.Log($"PosX = {transform.position.x}");

        // BoxCollider を追加して中心座標を設定
        gameObject.AddComponent<BoxCollider>();
        var boxCollider = GetComponent<BoxCollider>();
        if (boxCollider != null)
        {
            boxCollider.center = new Vector3(10, 20, 30);
            Debug.Log($"BoxColliderCenterX = {boxCollider.center.x}"); // タイポ修正
        }

        // GameObject.Find でシーンから car2 を検索(Unityのシミュレート)
        var car2 = GameObject.Find("Car2");
        if (car2 != null)
        {
            Debug.Log(car2.name);
            car2.transform.position = new Vector3(20, 21, 22);
            car2.transform.Translate(new Vector3(0, 2, 3));
            Debug.Log($"car2PosZ = {car2.transform.position.z}");
        }
    }
}

// ============================================================
// UnityEngine 名前空間
// ============================================================
namespace UnityEngine
{
    /// <summary>
    /// コンソール出力(Unity の Debug.Log に相当)
    /// </summary>
    public class Debug
    {
        public static void Log(object value) => Console.WriteLine(value);
    }

    /// <summary>
    /// シーン(Unity の Scene に相当)
    /// gameObjects はインスタンスフィールドにして複数シーンを正しく扱えるようにした
    /// </summary>
    public class Scene
    {
        public string name { get; set; } = string.Empty;

        // ★改善: static をやめてインスタンスフィールドに変更
        private readonly List<GameObject> gameObjects = new List<GameObject>();

        public void AddGameObject(GameObject gameObject)
        {
            gameObjects.Add(gameObject);
        }

        /// <summary>
        /// 名前でゲームオブジェクトを検索する(SceneManager 経由で呼び出される)
        /// </summary>
        public GameObject? Find(string name)
        {
            return gameObjects.FirstOrDefault(go => go.name == name);
        }
    }

    /// <summary>
    /// アクティブシーンを管理する(Unity の SceneManager に相当)
    /// GameObject.Find が static のまま使えるようにするための仕組み
    /// </summary>
    public class SceneManager
    {
        private static Scene? _activeScene;

        public static Scene ActiveScene => _activeScene
            ?? throw new InvalidOperationException("アクティブなシーンがありません");

        public static void SetActiveScene(Scene scene)
        {
            _activeScene = scene;
        }
    }

    /// <summary>
    /// ゲームオブジェクト(Unity の GameObject に相当)
    /// </summary>
    public class GameObject
    {
        public string name { get; set; } = string.Empty;

        private readonly List<Component> components = new List<Component>();

        public Transform transform => GetComponent<Transform>()!;

        /// <summary>
        /// ★改善: サブクラスも取得できるよう comp is T に変更
        /// </summary>
        public T? GetComponent<T>() where T : Component
        {
            return components.FirstOrDefault(comp => comp is T) as T;
        }

        /// <summary>
        /// ★改善: 同じコンポーネントの二重追加を防止
        /// </summary>
        public T AddComponent<T>() where T : Component, new()
        {
            if (GetComponent<T>() != null)
                throw new InvalidOperationException($"{typeof(T).Name} は既にアタッチされています");

            var component = new T { gameObject = this };
            components.Add(component);
            return component;
        }

        /// <summary>
        /// ★改善: SceneManager のアクティブシーンに委譲することで
        ///         static のまま維持しつつ複数シーン問題を解決
        /// </summary>
        public static GameObject? Find(string name)
        {
            return SceneManager.ActiveScene.Find(name);
        }
    }

    /// <summary>
    /// コンポーネントの基底クラス(Unity の Component に相当)
    /// </summary>
    public class Component
    {
        public GameObject? gameObject;

        public Transform transform => GetComponent<Transform>()!;

        /// <summary>
        /// ★改善: Nullable を統一(T? を返す)
        /// </summary>
        public T? GetComponent<T>() where T : Component
        {
            return gameObject?.GetComponent<T>();
        }
    }

    /// <summary>
    /// MonoBehaviour(Unity の MonoBehaviour に相当)
    /// </summary>
    public class MonoBehaviour : Component { }

    /// <summary>
    /// Transform コンポーネント(位置・移動を管理)
    /// </summary>
    public class Transform : Component
    {
        public Vector3 position { get; set; }

        public void Translate(Vector3 translation)
        {
            position += translation;
        }
    }

    /// <summary>
    /// 3次元ベクトル構造体
    /// </summary>
    public struct Vector3
    {
        public float x;
        public float y;
        public float z;

        public Vector3(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public static Vector3 operator +(Vector3 a, Vector3 b)
            => new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
    }

    /// <summary>
    /// BoxCollider コンポーネント
    /// </summary>
    public class BoxCollider : Component
    {
        public Vector3 center { get; set; }
    }

    /// <summary>
    /// Rigidbody コンポーネント
    /// </summary>
    public class Rigidbody : Component
    {
        public bool useGravity { get; set; }
    }
}

実行結果

PosX = 11;
BoxCollicerCenterX = 10
car2
car2Posz = 25

クラス図

基本のシーン構成とCube1

Cube2

Manager.cs

using UnityEngine;

/// <summary>
/// UnityEngineの内部コントロールのイメージです
/// 実行前の初期画面のデザイン設計と考えましょう
/// </summary>
internal class Program
{
    private static void Main()
    {
        // シーンを作成しアクティブに設定
        var scene = new Scene { name = "Test" };
        SceneManager.SetActiveScene(scene);

        // car1 を作成してシーンに登録
        var car1 = new GameObject { name = "Car1" };
        car1.AddComponent<Transform>();
        car1.AddComponent<Rigidbody>();
        scene.AddGameObject(car1);

        // car2 を作成してシーンに登録
        var car2 = new GameObject { name = "Car2" };
        car2.AddComponent<Transform>();
        car2.AddComponent<Rigidbody>();
        scene.AddGameObject(car2);

        // car1 に Manager をアタッチして Start を呼び出す
        car1.AddComponent<Manager>().Start();
    }
}

// ============================================================
// UnityEngine 名前空間
// ============================================================
namespace UnityEngine
{
    /// <summary>
    /// car1 オブジェクトにアタッチするスクリプトのイメージです
    /// ★修正: namespace UnityEngine 内に移動
    /// </summary>
    public class Manager : MonoBehaviour
    {
        private Rigidbody? rigidBody;

        public void Start()
        {
            // Rigidbody を取得して重力を有効化
            rigidBody = GetComponent<Rigidbody>();
            if (rigidBody != null)
                rigidBody.useGravity = true;

            // 位置を設定して移動
            transform.position = new Vector3(1, 2, 3);
            transform.Translate(new Vector3(10, 0, 0));
            Debug.Log($"PosX = {transform.position.x}");

            // BoxCollider を追加して中心座標を設定
            gameObject.AddComponent<BoxCollider>();
            var boxCollider = GetComponent<BoxCollider>();
            if (boxCollider != null)
            {
                boxCollider.center = new Vector3(10, 20, 30);
                Debug.Log($"BoxColliderCenterX = {boxCollider.center.x}");
            }

            // GameObject.Find でシーンから car2 を検索(Unityのシミュレート)
            var car2 = GameObject.Find("Car2");
            if (car2 != null)
            {
                Debug.Log(car2.name);
                car2.transform.position = new Vector3(20, 21, 22);
                car2.transform.Translate(new Vector3(0, 2, 3));
                Debug.Log($"car2PosZ = {car2.transform.position.z}");
            }
        }
    }

    /// <summary>
    /// コンソール出力(Unity の Debug.Log に相当)
    /// </summary>
    public class Debug
    {
        public static void Log(object value) => Console.WriteLine(value);
    }

    /// <summary>
    /// シーン(Unity の Scene に相当)
    /// gameObjects はインスタンスフィールドにして複数シーンを正しく扱えるようにした
    /// </summary>
    public class Scene
    {
        public string name { get; set; } = string.Empty;

        // ★改善: static をやめてインスタンスフィールドに変更
        private readonly List<GameObject> gameObjects = new List<GameObject>();

        public void AddGameObject(GameObject gameObject)
        {
            gameObjects.Add(gameObject);
        }

        /// <summary>
        /// 名前でゲームオブジェクトを検索する(SceneManager 経由で呼び出される)
        /// </summary>
        public GameObject? Find(string name)
        {
            return gameObjects.FirstOrDefault(go => go.name == name);
        }
    }

    /// <summary>
    /// アクティブシーンを管理する(Unity の SceneManager に相当)
    /// GameObject.Find が static のまま使えるようにするための仕組み
    /// </summary>
    public class SceneManager
    {
        private static Scene? _activeScene;

        public static Scene ActiveScene => _activeScene
            ?? throw new InvalidOperationException("アクティブなシーンがありません");

        public static void SetActiveScene(Scene scene)
        {
            _activeScene = scene;
        }
    }

    /// <summary>
    /// ゲームオブジェクト(Unity の GameObject に相当)
    /// </summary>
    public class GameObject
    {
        public string name { get; set; } = string.Empty;

        private readonly List<Component> components = new List<Component>();

        public Transform transform => GetComponent<Transform>()!;

        /// <summary>
        /// ★改善: サブクラスも取得できるよう comp is T に変更
        /// </summary>
        public T? GetComponent<T>() where T : Component
        {
            return components.FirstOrDefault(comp => comp is T) as T;
        }

        /// <summary>
        /// ★改善: 同じコンポーネントの二重追加を防止
        /// </summary>
        public T AddComponent<T>() where T : Component, new()
        {
            if (GetComponent<T>() != null)
                throw new InvalidOperationException($"{typeof(T).Name} は既にアタッチされています");

            var component = new T { gameObject = this };
            components.Add(component);
            return component;
        }

        /// <summary>
        /// ★改善: SceneManager のアクティブシーンに委譲することで
        ///         static のまま維持しつつ複数シーン問題を解決
        /// </summary>
        public static GameObject? Find(string name)
        {
            return SceneManager.ActiveScene.Find(name);
        }
    }

    /// <summary>
    /// コンポーネントの基底クラス(Unity の Component に相当)
    /// </summary>
    public class Component
    {
        public GameObject? gameObject;

        public Transform transform => GetComponent<Transform>()!;

        /// <summary>
        /// ★改善: Nullable を統一(T? を返す)
        /// </summary>
        public T? GetComponent<T>() where T : Component
        {
            return gameObject?.GetComponent<T>();
        }
    }

    /// <summary>
    /// MonoBehaviour(Unity の MonoBehaviour に相当)
    /// </summary>
    public class MonoBehaviour : Component { }

    /// <summary>
    /// Transform コンポーネント(位置・移動を管理)
    /// </summary>
    public class Transform : Component
    {
        public Vector3 position { get; set; }

        public void Translate(Vector3 translation)
        {
            position += translation;
        }
    }

    /// <summary>
    /// 3次元ベクトル構造体
    /// </summary>
    public struct Vector3
    {
        public float x;
        public float y;
        public float z;

        public Vector3(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public static Vector3 operator +(Vector3 a, Vector3 b)
            => new Vector3(a.x + b.x, a.y + b.y, a.z + b.z);
    }

    /// <summary>
    /// BoxCollider コンポーネント
    /// </summary>
    public class BoxCollider : Component
    {
        public Vector3 center { get; set; }
    }

    /// <summary>
    /// Rigidbody コンポーネント
    /// </summary>
    public class Rigidbody : Component
    {
        public bool useGravity { get; set; }
    }
}
**想定実行結果:**
PosX = 11
BoxColliderCenterX = 10
Car2
car2PosZ = 25

実行結果

Cub1の位置(InspectorのTransformのPosition Yの値がどんどんマイナスに大きくなっていっているのがわかります(落下しています)

これは、RigidbodyのuseGravityプロパティをtrueにしたためです

Consoleの表示されるデバッグ情報にも注意を向けましょう

Consolウィンドウ

訪問数 58 回, 今日の訪問数 1回

広告

C#,Unity,学習

Posted by hidepon