【デザインパターン】ObserverパターンについてUnityで学んでみよー

# ■はじめに
Observer知らんので、記事にしながら学んでみようと思います。(※正確性保証しません)

今回もUnityでの利用を例とします。




# ■説明
## ●Observerパターンとは
- **対象のオブジェクトの変更を、その他の色々なオブジェクトに通知するっていう設計**
    - 通知対象のオブジェクト：**Subject**
    - 通知を受け取るオブジェクト：**Observer**

## ●種類
### 1. Observerをリストで管理する
- **SubjectがObserverをリストで管理して、リスト内のObserverに通知する方法**
- **SubjectがObserverを参照する**

![Observer_List.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/bc6831cd-26b7-5136-37b0-9dcc8edb38f9.jpeg)


### 2. Eventを利用する
- **SubjectにEventを持たせて、Observerがそのイベントに変更されたときの登録する**
- **ObserverがSubjectを参照する**
- <ins>C#ではこちらのほうが簡単に実装できる</ins>

![Observer_Event.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/58533966-8228-f8f4-4d91-b0f1ffda47ed.jpeg)



<br>

- Unityで利用する場合、よっぽどのことがなければ、2のイベントでObserverを利用したほうが楽でいいかもです

- これは、ほかのデザインパターンであるMVPパターンにも利用されています


# ■サンプルプロジェクト
[公式のサンプルプロジェクト](https://blog.unity.com/ja/games/level-up-your-code-with-game-programming-patterns)を見てみましょう

こちらのサンプルプロジェクトでは、イベント駆動型でのObserverパターンを利用しています

![Observer.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/8176d419-4959-64d6-67b3-e102e9898efa.gif)

- ボタンを押すと、いろんなアクションが起こります
    - ボタンのアニメーション
    - 右のスピーカーオブジェクトからパーティクル出てくる
    - ボタンクリック音の後に、謎の効果音が再生される

<br>

また、サンプルプロジェクトでのSubject、Observerはこんな感じです
- Subject：**ボタン**
- Observer：**ボタンアニメーション再生スクリプト、パーティクル再生スクリプト、音声再生スクリプト**


# ■コード
そんなに複雑じゃないです

## ●ButtonSubject
- ObserverパターンのSubject担当です

```ButtonSubject.cs
public class ButtonSubject: MonoBehaviour
{
    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();
                }
            }
        }
    }
}
```

:::note 
- わざわざクリック用のレイを飛ばして当たり判定をとって押されたか判定しています
- クリックされたら、Clickedイベントを実行します
:::

今回は分かりやすさのためにわざと自作でレイで押されたか判定してますが、**UnityではButtonコンポーネントに`OnClicked()`イベントがすでに用意されています。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2419795/39fd5523-01ca-5ef4-d5e6-8318c554a393.png)

これもObserverパターンのひとつだったんですね～

## ●ParticleSystemObserver
- ObserverパターンのObserver担当です
- こいつのほかにも、AudioObserverとかAnimObserverなどがありますが、ほとんど同じなので省略します

```ParticleSystemObserver.cs
public class ParticleSystemObserver : MonoBehaviour
{
    [SerializeField] ButtonSubject subjectToObserve;
    [SerializeField] ParticleSystem particleSystem;

    private void Awake()
    {
        if (subjectToObserve != null)
        {
            subjectToObserve.Clicked += OnThingHappened;
        }
    }

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

    private void OnDestroy()
    {
        // unsubscribe/deregister from the event if we destroy the object
        if (subjectToObserve != null)
        {
            subjectToObserve.Clicked -= OnThingHappened;
        }
    }

}
```

:::note 
- インスペクターからSubjectを参照しています
- `Awake()`内で、Subjectのイベントに`OnThingHappend()`メソッドを追加しています
- `OnThingHappend()`メソッドでは、パーティクルを再生するようにしています
- `OnDestroy()`で、イベントから`OnThingHappend()`メソッドを除いています
:::

このサンプルプロジェクトでのObserverは、基底クラスがMonoBehaviorですが、Observer用の基底クラスを継承させたほうが便利かもです


# ■さいごに
UniRxってのでObserverが便利に使いやすくなってるっぽい。
使ってみようかな～


## ●参考サイト
https://www.midnightunity.net/design-pattern-observer/