Unity-Technologies game-programming-patterns について勉強

# はじめに
Unity公式のデザインパターンデモについてまとめてみました。概要ではなく、具体的な詳細の理解を目的としているので、コードが多めです。

下は公式の資料です。
　

**サンプルプロジェクト**

https://github.com/Unity-Technologies/game-programming-patterns-demo

**公式資料**

https://resources.unity.com/games/level-up-your-code-with-game-programming-patterns

**日本語公式記事**

https://blog.unity.com/ja/games/level-up-your-code-with-game-programming-patterns


# 1~5　SOLID原則
　1~5のサンプルはSOLID原則と呼ばれている、ソフトウェア開発においてその開発の改良や最適化をよりよく行うための心がけのようなものです。オブジェクト指向プログラミングを行っていく際に、非常にためになる内容だと思います。

**すごくわかりやすい記事**

https://qiita.com/baby-degu/items/d058a62f145235a0f007

この記事を見れば、概要は理解できると思いますが、これをUnityで利用している例をまとめていきます。
#  1.　SingleResponsibility　単一責任の原則
## 内容
　**一つのクラスに持たせる機能は一つにしよう**という考え方。

　サンプルの場合は、プレイヤーの機能を`Audio`・`Input`・`Move`の3つに分割し、それを`Player`クラスでまとめるというような実装をしています。

~~~C#:Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.SRP
{
    [RequireComponent(typeof(PlayerAudio), typeof(PlayerInput), typeof(PlayerMovement))]
    public class Player : MonoBehaviour
    {
        [SerializeField] private PlayerAudio playerAudio;
        [SerializeField] private PlayerInput playerInput;
        [SerializeField] private PlayerMovement playerMovement;

        private void Start()
        {
            playerAudio = GetComponent<PlayerAudio>();
            playerInput = GetComponent<PlayerInput>();
            playerMovement = GetComponent<PlayerMovement>();
        }
    }
}

~~~
## 良くない例
　単一責任の原則においてあまり良くない例として、`UnrefactoredPlayer`というクラスも定義されています。Unrefactoredとは、ゲーム実際の挙動自体は変えずに、プログラムの整理を行う「リファクタリング」がされていないという意味です。

~~~C#:UnrefactoredPlayer.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.SRP
{
    public class UnrefactoredPlayer : MonoBehaviour
    {

        [SerializeField] private string _inputAxisName;

        [SerializeField] private float _positionMultiplier;

        private float _yPosition;

        private AudioSource _bounceSfx;

        private void Start()
        {
            _bounceSfx = GetComponent<AudioSource>();
        }

        private void Update()
        {
            float delta = Input.GetAxis(_inputAxisName) * Time.deltaTime;

            _yPosition = Mathf.Clamp(_yPosition + delta, -1, 1);

            transform.position = new Vector3(transform.position.x, _yPosition * _positionMultiplier, transform.position.z);
        }

        private void OnTriggerEnter(Collider other)
        {
            _bounceSfx.Play();
        }
    }
}
~~~
## まとめ・感想
* メリット
  * 問題が起きた際に、その場所が明確なので、より速い作業を行うことができる
  * 機能ごとにクラスの分割を行うため、分業がしやすくなる
* デメリット
  * どのくらい機能を分割したらいいか、基準が難しい


#  2.　OpenClosed　オープン・クローズドの原則
## 内容
　作成したクラスは、**拡張(追加)は可能**にすべきだが、**変更は不可能**にするべきという考え方。

　サンプルの場合は、`Shape` という抽象クラスで `CalculateArea()` メソッドを定義し、それを `Rectangle`・`Circle` クラスが継承して、`CalculateArea()` メソッドをオーバーライドしています。

　これによって、**新しい図形のクラス作成を行う際に、今までのクラスを書き換えることなく機能の追加ができる**ようになっています。
~~~C#:Shape
namespace DesignPatterns.OCP
{
    public abstract class Shape
    {
        public abstract float CalculateArea();
    }
}
~~~
~~~C#:Rectangle
namespace DesignPatterns.OCP
{
    public class Rectangle : Shape
    {
        public float Width { get; set; }
        public float Height { get; set; }

        public override float CalculateArea()
        {
            return Width * Height;
        }
    }
}
~~~
~~~C#:Circle
using UnityEngine;

namespace DesignPatterns.OCP
{
    public class Circle : Shape
    {
        public float Radius { get; set; }

        public override float CalculateArea()
        {
            return Radius * Radius * Mathf.PI;
        }
    }
}
~~~
## 良くない例
　良くない例として`AreaCalculator`クラスが定義されていて、ここでは`Rectangle`と`Circle`の**処理を一つのクラス**で行っています。
~~~c#:AreaCalculator 
using UnityEngine;

namespace DesignPatterns.OCP
{ 
    public class AreaCalculator 
    {
        //public float GetRectangleArea(Rectangle rectangle)
        //{
        //    return rectangle.Width * rectangle.Height;
        //}

        //public float GetCircleArea(Circle circle)
        //{
        //    return circle.Radius * circle.Radius * Mathf.PI;
        //}

        public float GetArea(Shape shape)
        {
            return shape.CalculateArea();
        }
    }
}  
~~~
## まとめ・感想
* メリット
  * 後々機能が追加されていく想定のものを制作するうえで、必須と言ってもいいと思った
  * 複数人で機能の作成をするときにも役立ちそう
  * 一度作ってしまえば、クラスの使用先でのみエラーが発生するので、修正がしやすそう
* デメリット
  * 逆に基底クラスに問題が起きた場合、全てに問題が起きてしまうかも？


#  3.　LiskovSubstitution　リスコフの置換原則
## 内容
 **派生クラスは、その基底クラスの仕様以外のことはできないようにすべき**というものです。

　サンプルでは、`IMovable`を継承した`RailVehicle`クラスと、`IMovable`と`ITurnable`を継承した`RoadVehicle`クラスを実装しています。

　これによって、線路を走る`Train`クラスと`Car`クラスを、それぞれに必要な機能だけ持たせて制作することができるようにしています。

~~~c#:RailVehicle
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.LSP
{
    public class RailVehicle : IMovable
    {

        public string Name;

        private float moveSpeed = 100f;
        private float acceleration = 5f;

        public float TurnSpeed = 5f;

        public float MoveSpeed { get => moveSpeed; set => moveSpeed = value; }
        public float Acceleration { get => acceleration; set => acceleration = value; }

        public virtual void GoForward()
        {
            
        }

        public virtual void Reverse()
        {
           
        }
    }
}
~~~
~~~c#:RoadVehicle
using UnityEngine;

namespace DesignPatterns.LSP
{
    public class RoadVehicle : IMovable, ITurnable
    {
        public string Name;

        private float moveSpeed = 100f;
        private float acceleration = 5f;

        public float TurnSpeed = 5f;

        public float MoveSpeed { get => moveSpeed; set => moveSpeed = value; }
        public float Acceleration { get => acceleration; set => acceleration = value; }

        public virtual void GoForward()
        {
            
        }

        public virtual void Reverse()
        {
            
        }

        public virtual void TurnLeft()
        {
            
        }

        public virtual void TurnRight()
        {
            
        }
    }
}
~~~
~~~c#:Car
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.LSP
{
    public class Car : RoadVehicle
    {

    }
}
~~~
~~~c#:Train
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.LSP
{
    public class Train : RailVehicle
    {

    }
}
~~~
## 良くない例
　置換原則が守れていない例として、`Vehicle`クラスが定義されています。このクラスでは
前に進む・反転する・曲がる　という処理をするメソッドが**まとめて実装**されてしまっています。
　これを`Train`と`Car`クラス両方に継承する使い方をしてしまうと、`Train.TurnRight()`のような`Train`クラスに必要のない機能を実行できてしまいます。

~~~c#:Vehicle
using UnityEngine;

namespace DesignPatterns.LSP
{
    public class Vehicle
    {
        public float speed = 100;
        public string name;
        public Vector3 direction;

        //public void GoForward()
        //{

        //}

        //public void Reverse()
        //{

        //}

        //public void TurnRight()
        //{

        //}

        //public void TurnLeft()
        //{

        //}
    }
}
~~~


## まとめ・感想
* メリット
  * クラスごとに必要な機能しか持たせないので、見通しがよくなる
  * 作成した基底クラスをほかの人に使ってもらう際に、問題が起きるのを防げる
* デメリット
  * クラスの数が膨大になる

#  4.　InterfaceSegregation　インターフェイス分離の原則
## 内容
 **インターフェースは機能ごとに細かく分けて実装しよう**という考え方。

　サンプルでは、`IDamageable` `IMovable` `IUnitStats`を継承した`EnemyUnit`クラスと、
`IExplodable` `IDamageable`を継承した`ExplodingBarrel`を定義しています。

 これによって`EnemyUnit`と`ExplodingBarrel`が持つ機能を明確にしています。
~~~c#:EnemyUnit
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.ISP
{
    public class EnemyUnit : MonoBehaviour, IDamageable, IMovable, IUnitStats
    {
        //長いので省略
    }
}
~~~
~~~c#:ExplodingBarrel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.ISP
{
    public class ExplodingBarrel : MonoBehaviour, IExplodable, IDamageable
    {
        //長いので省略
    }
}
~~~

## 良くない例
　インターフェイス分離の原則に則っていない例として、`IUnitStats`インターフェース内に記述があります。ここでは、**現在は他のインターフェースに分割している機能をまとめて持つ**という実装をしてしまっています。
~~~c#:IUnitStats 
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.ISP
{
    // Interface Segregation tells us to use smaller interfaces. Rather than
    // incorporate all of these Methods/Properties in one interface, use several.

    public interface IUnitStats 
    {
        //public float Health { get; set; }
        //public int Defense { get; set; }

        //public void Die();
        //public void TakeDamage();
        //public void RestoreHealth();

        //public float MoveSpeed { get; set; }
        //public float Acceleration { get; set; }

        //public void MoveForward();
        //public void Reverse();
        //public void TurnLeft();
        //public void TurnRight();

        public int Strength { get; set; }
        public int Dexterity { get; set; }
        public int Endurance { get; set; }

        //public float Mass { get; set; }
        //public float ExplosiveForce { get; set; }
        //public float FuseDelay { get; set; }
        //public float Timeout { get; set; }

        //public void Explode();

    }
}
~~~
## まとめ・感想
* インターフェースをクラスごとに使いまわすことができるので、効率がいい
* 機能の追加削除を行いやすい
* 不要な機能を持たせない程度に分割を行うのが難しそう


#  5.　DependencyInversion　依存性逆転の原則
## 内容
　**動作を実行させるクラス**（`Switch`）は、**動作を実行するクラス**（今回の場合は`Door`）に対し、**具体的な参照を持つべきではない**という考え方。

　サンプルの場合は、`Door`クラスに`ISwitchable`を継承させ、`Switch`クラスは`ISwitchable`に対して参照を持つことで、`Door`クラスに対する**具体的な参照**を防いでいます。

　また、これによって`ISwitchable`を継承した`Trap`クラスに対しても、同じ`Switch`クラスで処理を行うことができています。
~~~c#:Door
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.DIP
{
    public class Door : MonoBehaviour, ISwitchable
    {
        private bool isActive;
        public bool IsActive => isActive;

        public void Activate()
        {
            isActive = true;
            Debug.Log("The door is open.");
        }

        public void Deactivate()
        {
            isActive = false;
            Debug.Log("The door is closed.");
        }
    }

}
~~~
~~~c#:Trap
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.DIP
{
    public class Trap : MonoBehaviour, ISwitchable
    {
        //Doorと一緒なので省略
    }
}
~~~
~~~c#:Switch
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.DIP
{
    public class Switch : MonoBehaviour
    {
        public ISwitchable client;

        public void Toggle()
        {
            if (client.IsActive)
            {
                client.Deactivate();
            }
            else
            {
                client.Activate();
            }
        }
    }
}
~~~
## 良くない例
　依存性逆転の原則に則っていない例として、`Door`クラスと `Switch`クラス内に記述があります。ここでは、`Switch`が`Door`に対し具体的な参照を持ってしまっているので
* `Switch`が単独で動くことができない
* `Door`のようなギミックを追加する際に、`Switch`に追記しなければいけない

　のような問題が発生してしまいます。
~~~c#:Door
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.DIP
{
    public class Door : MonoBehaviour
    {
        public void Open()
        {
            Debug.Log("The door is open.");
        }

        public void Close()
        {
            Debug.Log("The door is closed.");
        }
    }
}
~~~
~~~c#:Switch
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.DIP
{
    public class Switch : MonoBehaviour
    {
        public Door Door;
        public bool IsActivated;

        public void Activate()
        {
            if (IsActivated)
            {
                IsActivated = false;
                Door.Close();
            }
            else
            {
                IsActivated = true;
                Door.Open();
            }
        }
    }
}
~~~
## まとめ・感想
* **それぞれの機能を独立させて制作を行える**
* チーム内でインターフェースの共有さえしておけば、分業がしやすくなる
* 新しい機能の追加やデバッグがやり易くなる

#  6.　Factory
## 内容
[![Image from Gyazo](https://i.gyazo.com/7e3bbffc72346dd21a649f641b35caee.gif)](https://gyazo.com/7e3bbffc72346dd21a649f641b35caee)
　このデモでは、共通のインターフェースを用いて**複数のオブジェクトの生成を１つのクラス**で行い、その**オブジェクトにはそれぞれ異なった処理**を行わせています。

　このような実装にすることで、**生成の指示**を行うクラスは、**生成を行う**クラスと**生成するもの**の処理や数に**影響を受けることなく**、実装ができるようになります。

コードの内容を4つのステップで説明します
1. `IProduct`を継承した`ProductA`・`ProductB`（生成する球の処理）を作成

~~~c#:ProductA
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.Factory
{
    public class ProductA : MonoBehaviour, IProduct
    {
        public void Initialize()
        {
            //ProductAで行う処理
        }
    }
}
~~~
　
2. `Factory`を継承した`ConcreteFactoryA`・`ConcreteFactoryB`を作成
3. 作成した`ConcreteFactory`クラスで、基底クラスの`Factory`に定義されている、`IProduct`型の`Product`を生成する**メソッド**をオーバーライドする
~~~c#:ConcreteFactoryA
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.Factory
{
    public class ConcreteFactoryA : Factory
    {
        //生成するプレハブ
        [SerializeField] private ProductA productPrefab;

        public override IProduct GetProduct(Vector3 position)
        {
            // プレハブを生成し、そのコンポーネントを取得
            GameObject instance = Instantiate(productPrefab.gameObject, position, Quaternion.identity);
            ProductA newProduct = instance.GetComponent<ProductA>();

            // プレハブの処理を実行
            newProduct.Initialize();

            return newProduct;
        }
    }
}
~~~
　
4.  `ClickToCreate`クラスで`Factory`型の変数を定義し、その**メソッド**を実行
~~~c#:ClickToCreate
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.Factory
{
    public class ClickToCreate : MonoBehaviour
    {
        [SerializeField] private LayerMask layerToClick;
        [SerializeField] private Vector3 offset;
        //ファクトリーを持っておく
        [SerializeField] Factory[] factories;

        private Factory factory;

        private void Update()
        {
            GetProductAtClick();
        }

        private void GetProductAtClick()
        {
            if (Input.GetMouseButtonDown(0))
            {
                //ファクトリーをランダムに選択
                factory = factories[Random.Range(0, factories.Length)];

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

                if (Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerToClick) && factory != null)
                {
                    //ファクトリーを生成
                    factory.GetProduct(hitInfo.point + offset);
                }
            }
        }
    }
}
~~~

というような実装方法になっています。

## まとめ・感想
・バリエーションの多い、RPGの敵などを制作するときに便利
・**生成する側も生成される側も、互いの詳細を知らなくていい**のがいい
・今後バリエーションの追加が予定されている場合にも役立つ
・生成処理の間に条件を挟みやすくなる


#  7.　ObjectPool
## 内容
[![Image from Gyazo](https://i.gyazo.com/926876cbbc37a07e2ee7f348ef905f64.gif)](https://gyazo.com/926876cbbc37a07e2ee7f348ef905f64)
　このデモでは、**生成したオブジェクトを使用が終わった際に削除せず**、オブジェクトのアクティブ・非アクティブと座標を変更することで、**オブジェクトの再利用**を行うということをしています。

　オブジェクトプールは、**オブジェクトの生成と破棄によってメモリに負荷がかかることを回避する**という目的のデザインパターンです。

　デモの使用例として、2021年にUnityから[UnityEngine.Pool](https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1.html)を使用する例と使用しない例があります。ここではUnityEngine.Poolを使用する例のほうを見ながら説明していきます。

　
　1. まず、`RevisedProjectile` (弾)を作成し、ObjectPoolへの参照を持たせる
　2. 弾のクラスに、オブジェクトをプールに追加する処理を定義する
~~~c#:RevisedProjectile
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

namespace DesignPatterns.ObjectPool
{
    public class RevisedProjectile : MonoBehaviour
    {
        // 非アクティブになるまでの時間
        [SerializeField] private float timeoutDelay = 3f;

        private IObjectPool<RevisedProjectile> objectPool;

        // 弾にObjectPoolへの参照を与えるパブリックプロパティ
        public IObjectPool<RevisedProjectile> ObjectPool { set => objectPool = value; }
        
        // プールに戻す際の処理
        public void Deactivate()
        {
            StartCoroutine(DeactivateRoutine(timeoutDelay));
        }

        IEnumerator DeactivateRoutine(float delay)
        {
            yield return new WaitForSeconds(delay);

            // 動きをリセット
            Rigidbody rBody = GetComponent<Rigidbody>();
            rBody.velocity = new Vector3(0f, 0f, 0f);
            rBody.angularVelocity = new Vector3(0f, 0f, 0f);

            // このゲームオブジェクトをプールに戻す
            objectPool.Release(this);
        }
    }
}

~~~

　
　3. 射撃を行う銃にアタッチするコンポーネントで、`RevisedProjectile` (弾)　の`ObjectPool`を作成する
　
　[ObjectPoolのコンストラクタ](https://docs.unity3d.com/ScriptReference/Pool.ObjectPool_1-ctor.html)の引数は以下のものです
|コンストラクタ|内容|
|-----|---|
|Func\<T> createFunc|生成の処理|
|Action\<T> actionOnGet = null|プールから一時削除する処理|
|Action\<T> actionOnRelease = null|プールから使用するときの処理|
|Action\<T> actionOnDestroy = null|プールから破棄するときの処理|
|bool collectionCheck = true|すでにオブジェクトがプールにあるか|
|int defaultCapacity = 10|プールのデフォルトの容量|
|int maxSize = 10000|プールの最大サイズ|

　
    
~~~c#:RevisedGun
using UnityEngine;
using UnityEngine.Pool;

namespace DesignPatterns.ObjectPool
{
    public class RevisedGun : MonoBehaviour
    {
        //変数いろいろ//
        ~~~~~~~~

        private IObjectPool<RevisedProjectile> objectPool;

        // すでにこのオブジェクトがプールに含ませているか示す
        [SerializeField] private bool collectionCheck = true;

        // プール容量と最大サイズを指定
        [SerializeField] private int defaultCapacity = 20;
        [SerializeField] private int maxSize = 100;

        
        private void Awake()
        {
            //オブジェクトプールを作成
            objectPool = new ObjectPool<RevisedProjectile>(
                CreateProjectile,
                OnGetFromPool, 
                OnReleaseToPool, 
                OnDestroyPooledObject,
                collectionCheck, 
                defaultCapacity, maxSize);
        }
        
        // 生成の処理
        private RevisedProjectile CreateProjectile()
        {
            RevisedProjectile projectileInstance = Instantiate(projectilePrefab);
            projectileInstance.ObjectPool = objectPool;
            return projectileInstance;
        }

        // プールから一時削除する処理
        private void OnReleaseToPool(RevisedProjectile pooledObject)
        {
            pooledObject.gameObject.SetActive(false);
        }

        // プールから使用するときの処理
        private void OnGetFromPool(RevisedProjectile pooledObject)
        {
            pooledObject.gameObject.SetActive(true);
        }

        // プールから破棄するときの処理
        private void OnDestroyPooledObject(RevisedProjectile pooledObject)
        {
            Destroy(pooledObject.gameObject);
        }
        ~~~~~~~~~~
        //生成の処理//
    }
~~~
　
処理の順序をよりまとめると、以下の通りになります
1. オブジェクトを生成
1. 上限までプールに追加
1. オブジェクトを非アクティブに
1. 再び生成する際にプールの中から生成



## まとめ・感想
・大量のオブジェクトを扱うゲーム（シューティングとか、無双ゲームとか）を制作するうえで非常に役立つ
・Flyweightパターン・ScriptableObjectと併用すると効果的

#  8.　Singleton
## 内容
　[![Image from Gyazo](https://i.gyazo.com/16b96664a4c42c3a842cf951975683b9.gif)](https://gyazo.com/16b96664a4c42c3a842cf951975683b9)

　シングルトンとは次のようなものです
* **クラスが自身のインスタンスを1つだけ作成できることを保証する**
* **その1つのインスタンスに簡単にどこからでもアクセスできるようにする**

　デモの場合は、サウンドを再生するゲームオブジェクトをシングルトンで実装することで、その**ゲームオブジェクトの重複を防ぐ**とともに、**ほかのクラスからの参照を行いやすく**しています。

~~~c#:Singleton<T>.cs
using UnityEngine;

namespace DesignPatterns.Singleton
{
    public class Singleton<T> : MonoBehaviour where T : Component
    {
        private static T _instance;
        public static T Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = (T)FindObjectOfType(typeof(T));

                    if (_instance == null)
                    {
                        SetupInstance();
                    }
                    else
                    {
                        string typeName = typeof(T).Name;

                        Debug.Log("[Singleton] " + typeName + " instance already created: " +
                            _instance.gameObject.name);
                    }
                }

                return _instance;
            }
        }

        public virtual void Awake()
        {
            RemoveDuplicates();
        }
~~~
~~~c#:SetupInstance
        //このクラスのインスタンスの生成処理を行う
        private static void SetupInstance()
        {
            _instance = (T)FindObjectOfType(typeof(T));

            if (_instance == null)
            {
                GameObject gameObj = new GameObject();
                gameObj.name = typeof(T).Name;

                _instance = gameObj.AddComponent<T>();
                DontDestroyOnLoad(gameObj);
            }
        }
~~~
~~~c#:RemoveDuplicates
        //Awakeで処理を行う
        private void RemoveDuplicates()
        {
            //インスタンスなければ、DontDestroyOnLoadに登録
            if (_instance == null)
            {
                _instance = this as T;
                DontDestroyOnLoad(gameObject);
            }
            //そうでなければ破棄する
            else
            {
                Destroy(gameObject);
            }
        }
~~~
　
ここでやっている処理をまとめると、以下の通りになります
1. シングルトンクラスのインスタンスが既にあるか判定
1. インスタンスが**無かった場合**生成
1. インスタンスが**あった場合**ゲームオブジェクトを削除

## まとめ・感想
　このパターンは、今回紹介しているデザインパターン12種の中でもっとも有名ではないでしょうか。多くの人がこれを利用したクラスの作成をしていると思います。

 　
しかし上記のような実装のシングルトンには、明確な**デメリット**が存在しています
* クラスに対する**アクセスが簡単になりすぎ**、依存関係の整理が難しくなる
* クラスが**それぞれ独立したものでなくなる**ので、テストがしずらくなる

　というようなものです。今まで、クラスの依存関係を切り離す方法を説明してきましたが、このパターンでは逆に**クラスが密結合**になってしまいます。
 　
　
 ですが、このようなデメリットだけでなく、強力な**メリット**もあるのが事実です
* ほかのクラスからの参照が行いやすくなる
* インスタンスをほかのシーンにすべて引き継ぐことができる
* **静的なインスタンスを1つだけ持ち続けられる**ので、パフォーマンスがいい

　
　このように、非常に便利な機能として利用することもできるので、シングルトンを利用する際には、最大限注意を払う必要があります。
#  9.　Command
## 内容
[![Image from Gyazo](https://i.gyazo.com/8437c6421a88f08574be1c8f191d7131.gif)](https://gyazo.com/8437c6421a88f08574be1c8f191d7131)
　コマンドパターンとは、**入力をコマンドオブジェクトとしてカプセル化**することで行動を追跡し、**入力の保存やシミュレーション**を行えるようにするパターンです。

　デモのように、ストラテジーゲームなどによく見られる、リプレイ機能などを作成するときに役立ちます。

　
　デモでのUndo・Redoの実装方法は以下の通りです
* `ICommand`を継承した`MoveCommand`を作成
* `_undoStack`と`_redoStack`に、Commandをスタックしておく
* 入力を受け取るごとに、`MoveCommand`を発行し保存する

　このようにすることで、入力の保存を行っています。
~~~c#:ICommand
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.Command
{
    public interface ICommand
    {
        //実行処理
        public void Execute();
        //戻る処理
        public void Undo();
    }
}
~~~
~~~c#:MoveCommand
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.Command
{
    public class MoveCommand : ICommand
    {
        private PlayerMover _playerMover;
        private Vector3 _movement;

        public MoveCommand(PlayerMover player, Vector3 moveVector)
        {
            this._playerMover = player;
            this._movement = moveVector;
        }

        // コマンド実行時の処理
        public void Execute()
        {
            //パス（通った地点）のリストに追加
            _playerMover?.PlayerPath.AddToPath(_playerMover.transform.position + _movement);

            //プレイヤーを移動させる
            _playerMover.Move(_movement);
        }

        // コマンドを戻すときの処理
        public void Undo()
        {
            //プレイヤーを前の場所に移動
            _playerMover.Move(-_movement);

            //パスのリストから移動前の座標を削除
            _playerMover?.PlayerPath.RemoveFromPath(); 
        }
    }
}
~~~
~~~c#:InputManager
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


namespace DesignPatterns.Command
{
    public class InputManager : MonoBehaviour
    {
        [Header("Button Controls")]
        [SerializeField] Button forwardButton;
        [SerializeField] Button backButton;
        [SerializeField] Button leftButton;
        [SerializeField] Button rightButton;
        [SerializeField] Button undoButton;
        [SerializeField] Button redoButton;

        [SerializeField] private PlayerMover player;

        private void Start()
        {
            //ボタンにイベントをセット
            forwardButton.onClick.AddListener(OnForwardInput);
            backButton.onClick.AddListener(OnBackInput);
            rightButton.onClick.AddListener(OnRightInput);
            leftButton.onClick.AddListener(OnLeftInput);
            undoButton.onClick.AddListener(OnUndoInput);
            redoButton.onClick.AddListener(OnRedoInput);
        }

        private void RunPlayerCommand(PlayerMover playerMover, Vector3 movement)
        {
            if (playerMover == null)
            {
                return;
            }

            // 先に進めるかどうか判定
            if (playerMover.IsValidMove(movement))
            {
                // コマンドを発行し、undoに保存
                ICommand command = new MoveCommand(playerMover, movement);

                // 発行したコマンドのExecuteを実行
                CommandInvoker.ExecuteCommand(command);
            }
        }

        //========= ここから下は入力時の処理 =========

        private void OnLeftInput()
        {
            RunPlayerCommand(player, Vector3.left);
        }

        private void OnRightInput()
        {
            RunPlayerCommand(player, Vector3.right);
        }

        private void OnForwardInput()
        {
            RunPlayerCommand(player, Vector3.forward);
        }

        private void OnBackInput()
        {
            RunPlayerCommand(player, Vector3.back);
        }

        private void OnUndoInput()
        {
            CommandInvoker.UndoCommand();
        }

        private void OnRedoInput()
        {
            CommandInvoker.RedoCommand();
        }
    }
}
~~~
## まとめ・感想
* 入力処理とそれによって行う処理が分かれているので、機能の追加や変更によって既存のクラスが受ける影響を削減できる
* ストラテジーやシミュレーションゲームなどを作る際には必須級の実装方法かも

#  10.　State
## 内容
[![Image from Gyazo](https://i.gyazo.com/8c528948e62d15b445748649ff678639.gif)](https://gyazo.com/8c528948e62d15b445748649ff678639)
　ステートパターンは、**オブジェクトの処理を状態ごとに分割**することで、その時に必要な処理だけを行うようにするというパターンです。

　デモでは、`Idle` `Walk` `Jump`という３つのStateがあり、その状態に応じてジャンプや移動の処理を行っています。

　
　このパターンの例の一つとして、`UnrefactoredPlayerController`というクラスが作成されています。このクラスでのStateの判定は、以下のように行っています
~~~c#:UnrefactoredPlayerController.Update()
private void Update()
        {
            switch (state)
            {
                case PlayerControllerState.Idle:
                    Idle();
                    break;
                case PlayerControllerState.Walk:
                    Walk();
                    break;
                case PlayerControllerState.Jump:
                    Jump();
                    break;
            }
        }
~~~

　おそらく、最初にStateで動きを制御しようとしたら、みんなこの方法で実装を行うと思います。これでも実装は可能ですが、**`PlayerController`内の処理が複雑になる**だけでなく、**Stateを追加・修正するたびにコードを変更**しなければいけなくなってしまいます。

　
　この問題を解消するために、デモで使用されている例では別の方法をとっています。

　実装方法としては、`IState`を継承した、**Stateごとの処理**を定義したクラスを作成し、`PlayerController`では、**その時のStateの処理**を実行するとだけ書くという方法です。
　
　そして、そのStateの管理を`StateMachine`というクラスで行うことで、**Playerの動き**・**Stateの管理**・**Stateの実行する処理**の３つを、すべて別のクラスで行っています。
~~~c#:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.State
{
    public class PlayerController : MonoBehaviour
    {
        //===============この上でいろいろな変数を定義===============

        [SerializeField] private PlayerInput playerInput;
        private StateMachine playerStateMachine;

        public CharacterController CharController => charController;
        public bool IsGrounded => isGrounded;
        public StateMachine PlayerStateMachine => playerStateMachine;

        private void Awake()
        {
            playerInput = GetComponent<PlayerInput>();
            charController = GetComponent<CharacterController>();

            //StateMachineをインスタンス化
            playerStateMachine = new StateMachine(this);
        }

        private void Start()
        {
            //StateMachineの初期化処理
            playerStateMachine.Initialize(playerStateMachine.idleState);
        }

        private void Update()
        {
            //StateMachineで現在のStateの処理を実行
            playerStateMachine.Update();
        }

        //==========以下はプレイヤーの動きの処理===============
~~~
~~~c#:StateMachine
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.State
{
    [Serializable]
    public class StateMachine
    {
        public IState CurrentState { get; private set; }

        // Stateのクラス
        public WalkState walkState;
        public JumpState jumpState;
        public IdleState idleState;

        // Stateが切り替わった際に行うAction
        public event Action<IState> stateChanged;

        //Stateクラスのコンストラクタに必要なパラメーターを渡す
        public StateMachine(PlayerController player)
        {
            //各Stateクラスのインスタンスを生成し、PlayerControllerに渡す
            this.walkState = new WalkState(player);
            this.jumpState = new JumpState(player);
            this.idleState = new IdleState(player);
        }

        public void Initialize(IState state)
        {
            CurrentState = state;
            state.Enter();

            //状態の変化を通知する
            stateChanged?.Invoke(state);
        }

        //Stateを遷移させる
        public void TransitionTo(IState nextState)
        {
            CurrentState.Exit();
            CurrentState = nextState;
            nextState.Enter();

            //状態の変化を通知する
            stateChanged?.Invoke(nextState);
        }

        // 現在のStateのUpdateを実行
        public void Update()
        {
            if (CurrentState != null)
            {
                CurrentState.Update();
            }
        }
    }
}
~~~
~~~c#:IState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.State
{
    public interface IState : IColorable
    {
        public void Enter() {}

        public void Update() {}

        public void Exit() {}
    }
}
~~~
~~~c#:IdleState
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DesignPatterns.State
{
    public class IdleState : IState
    {

        private PlayerController player;

        //プレイヤーの色
        private Color meshColor = Color.gray;
        public Color MeshColor { get => meshColor; set => meshColor = value; }

        //コンストラクタでプレイヤーの参照を渡す
        public IdleState(PlayerController player)
        {
            this.player = player;
        }

        public void Enter()
        {
            //IdleStateになった際の処理を書く
        }

        //このStateで行う処理と、別のStateに遷移する処理を書く
        public void Update()
        {
            //JumpStateへの遷移
            if (!player.IsGrounded)
            {
                player.PlayerStateMachine.TransitionTo(player.PlayerStateMachine.jumpState);
            }

            //WalkStateへの遷移
            if (Mathf.Abs(player.CharController.velocity.x) > 0.1f || Mathf.Abs(player.CharController.velocity.z) > 0.1f)
            {
                player.PlayerStateMachine.TransitionTo(player.PlayerStateMachine.walkState);
            }
        }

        public void Exit()
        {
            //このStateから抜けた際の処理を書く
        }
    }
}
~~~
　StateMachineは、常に一つの状態を持つということが、大きなポイントになっています。UnityEditorでは、AnimatorがStateMachineをうまく組み合わせて作成されているので、このパターンを理解するのに役立つかもしれません。

　また、Stateパターンは、状況に応じて行動を自動で切り替える、簡単な敵などのAIを作成する際にも利用できます。

## まとめ・感想
* ゲームを作るうえで、必ずと言っていいほど利用する場面がある
* **一つのものにいろいろな行動をさせたいとき**に役立つ
* 状態ごとに処理を書けるので、SOLID原則に則ることができる
* **余計な判定を行うことが減る**ので、パフォーマンス的にもいい

#  11.　Observer
## 内容
[![Image from Gyazo](https://i.gyazo.com/fd94328bbed11bff5b35440fc2d66efd.gif)](https://gyazo.com/fd94328bbed11bff5b35440fc2d66efd)

 オブザーバーパターンとは、多数のクラスからある１つのクラスの値を監視し、それが変化した場合に処理を行うデザインパターンのことです。

　デモでは、監視される側の`Subject`クラス内で定義されている`Action`に対し、監視する側である`Observer`クラスが登録（購読）を行い、ボタンが押されるたびにそれを呼び出すというような実装をしています。

　これによって、**`Subject`は`Observer`がいくつあろうが、気にすることなく**実装することができ、「これをどう使うか知らないけど、とりあえず公開だけしておくね」というスタンスをとることができます。

~~~c#:ButtonSubject
using UnityEngine;
using System;

namespace DesignPatterns.Observer
{
    [RequireComponent(typeof(Collider))]
    public class ButtonSubject: MonoBehaviour
    {
        //登録する用のAction
        public event Action Clicked;

        private Collider collider;

        void Start()
        {
            collider = GetComponent<Collider>();
        }

        //ボタンが押された際に呼び出すメソッド
        public void ClickButton()
        {
            Clicked?.Invoke();
        }

        void Update()
        {
            CheckCollider();
        }

        //ボタンの接触判定
        private void CheckCollider()
        {
            if (Input.GetMouseButtonDown(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                RaycastHit hitInfo;

                if (Physics.Raycast(ray, out hitInfo, 100f))
                {
                    if (hitInfo.collider == this.collider)
                    {
                        //ボタンが押されたらイベントを実行
                        ClickButton();
                    }
                }
            }
        }
    }
}
~~~
~~~c#:AnimObserver
using UnityEngine;

namespace DesignPatterns.Observer
{
    public class AnimObserver : MonoBehaviour
    {
        [SerializeField] Animation animClip;
        [SerializeField] ButtonSubject subjectToObserve;
        void Start()
        {
            if (subjectToObserve != null)
            {
                //イベントに自身の処理を追加
                subjectToObserve.Clicked += OnThingHappened;
            }
        }

        private void OnThingHappened()
        {
            if (animClip != null)
            {
                animClip.Stop();
                animClip.Play();
            }
        }
    }
}
~~~

　ここまでdelegateでの実装について説明しましたが、現在このパターンを使用するときはだいたい**UniRx**を使うと思います。ボタンくらいならいいかもしれませんが、そうでないならデモの方法だといろいろ使い勝手が悪いです。

　
理由としては
* このままでは、**値の変化そのもの**を監視できない
* 変化した値に応じた処理を行いずらい

　などがあります
## まとめ・感想
* 機能ごとに追加を行うことができるので、SOLID原則に則ることができる
* **機能の追加削減が行われやすい箇所で使いやすい**
* クラスが疎結合になり、他のクラスの使用方法を気にせず実装できる

#  12.　MVP
## 内容
[![Image from Gyazo](https://i.gyazo.com/9429cef34e5581f812b56b141a3edfef.gif)](https://gyazo.com/9429cef34e5581f812b56b141a3edfef)
　MVPとは、主にGUI周りの実装をする際に用いられます。**実装の要素をModel・View・Presenterの3つに分割**することで、ごちゃつきがちなGUIの実装をわかりやすくしようという目的のデザインパターンです。

　要素の内容は以下の通りです
* **Model** 　    ：データの実体を持つ部分
* **View**　 　：データの反映を行う、画面に表示を行う部分　
* **Presenter**：ModelとViewを仲介する
![mvp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2950440/1b04ac55-5bb9-12e7-6769-6a8886119e98.png)

　Presenterからのみ参照を行うことで、**ModelとViewは他のクラスへの参照を持つことなく**実装を行うことができるという利点があります。

　
　デモでは、HPの管理をMVPで実装しています。`Health`クラスに値が変化した際に呼ばれる`Action`を定義し、それに`HealthPresenter`クラスで`View`(今回の場合だとSlider)の処理を追加しています。

~~~c#:Health
using UnityEngine;
using System;

namespace DesignPatterns.MVP
{
    public class Health : MonoBehaviour
    {
        //値が変わった際に呼び出すイベント
        public event Action HealthChanged;

        private const int minHealth = 0;
        private const int maxHealth = 100;
        private int currentHealth;

        public int CurrentHealth { get => currentHealth; set => currentHealth = value; }
        public int MinHealth => minHealth;
        public int MaxHealth => maxHealth;

        public void Increment(int amount)
        {
            currentHealth += amount;
            currentHealth = Mathf.Clamp(currentHealth, minHealth, maxHealth);
            UpdateHealth();
        }

        public void Decrement(int amount)
        {
            currentHealth -= amount;
            currentHealth = Mathf.Clamp(currentHealth, minHealth, maxHealth);
            UpdateHealth();
        }

        public void Restore()
        {
            currentHealth = maxHealth;
            UpdateHealth();
        }

        //値の変更が行われた際に呼び出す
        public void UpdateHealth()
        {
            HealthChanged.Invoke();
        }
    }
}
~~~
~~~c#:HealthPresenter
using UnityEngine;
using UnityEngine.UI;

namespace DesignPatterns.MVP
{
    public class HealthPresenter : MonoBehaviour
    {
        [Header("Model")]
        [SerializeField] Health health;

        [Header("View")]
        [SerializeField] Slider healthSlider;
        [SerializeField] Text healthText;

        private void Start()
        {
            if (health != null)
            {
                health.HealthChanged += OnHealthChanged;
            }

            Reset();
        }

        private void OnDestroy()
        {
            if (health != null)
            {
                health.HealthChanged -= OnHealthChanged;
            }
        }

        //ここで値の変更を行う
        public void Damage(int amount)
        {
            health?.Decrement(amount);
        }

        //値の変更を行う
        public void Heal(int amount)
        {
            health?.Increment(amount);
        }

        public void Reset()
        {
            health?.Restore();
        }

        public void UpdateView()
        {
            if (health == null)
                return;

            if (healthSlider !=null && health.MaxHealth != 0)
            {
                healthSlider.value = (float) health.CurrentHealth / (float)health.MaxHealth;
            }

            if (healthText != null)
            {
                healthText.text = health.CurrentHealth.ToString();
            }
        }

        public void OnHealthChanged()
        {
            UpdateView();
        }
    }
}
~~~
　
　`MVP`パターンは、`Observer`パターンと密接な関係にあります。というか後者を理解するには、前者の理解が必要不可欠だと思います。

　というのも、MVPはGUI実装をするうえでObserverパターンを用いた際に、機能をより細かく分割したデザインパターンだからです。なので、これも**UniRx**を用いたほうがよりよい実装ができます。
## まとめ・感想
* **Observerと同じような効果**を得られる
* 値の変更と反映を**分業して作業する**ことができて便利


# 最後に
　ここまで説明したデザインパターンのほとんどは、**SOLID原則に則った設計を行えるようなもの**になっているので、ざっと概要を理解するだけでもクラスの参照関係やコードの見通しをかなり良くできるはずです。
　
　ただSOLID原則は、拡張や修正を行いやすくすることを目的としたものであり、やたらめったら使っても**逆にコードの見通しが悪くなったり**、**必要のない柔軟性を持たせすぎて無駄に時間がかかってしまったり**するので、状況に応じて臨機応変に利用するべきだと思います。