ObserverをUnityで使ってみようと頑張った話

interfaceすらふわっふわな人が書いてるので間違いが（たぶん）沢山あります。

目標:UnityでObserverパターンをつかってなにか動くものを作る

作るもの:入力された情報が変更されるたびに、動くScrollView

まず、作ってみたObserver

```Unity:TextObserver.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace My{
    public interface ITextObserver<T>{
        void Notify(string s);

    }

    class ShowGetText<T> : ITextObserver<T>{
        public void Notify(string s){
            Debug.Log(s);
        }
    }

    public class GetText<T> : ITextObserver<T>{
        public void Notify(string s){
            
        }
    }


    public interface ITextSubject<T>{
        void AddObserver(ITextObserver<T> observer);
        void RemoveObserver(ITextObserver<T> observer);
        void NotifyObserver(string s);
    }

    public class TextOvserver<T> : ITextSubject<T>{
        private List<ITextObserver<T>> _observeList = new List<ITextObserver<T>>();

        public void AddObserver(ITextObserver<T> observer ){
            _observeList.Add(observer);
            Debug.Log(observer);
        }

        public void RemoveObserver(ITextObserver<T> observer){
            _observeList.Remove(observer);
        }

        public void NotifyObserver(string s){
            _observeList.ForEach(o => o.Notify(s));
            Debug.Log("Hello");
        }
    }
}
```

つぎに、変化をするSubject側


```Unity:TextSender.cs
public class TextSender : MonoBehaviour
{

    public My.ITextSubject<string> Subject{get;} = new My.TextOvserver<string>();

    string[] IN_Text = new string[3];

    [SerializeField]
    int index;
    void Start(){
        index = 0;
        IN_Text[0] = "ラットに20ダメージを与えた";
        IN_Text[1] = "ピカピカの薬を手に入れた";
        IN_Text[2] = "ラットから30ダメージを受けた";
    }

    // Update is called once per frame
    void Update()
    {
        if(index>2){
            index=0;
        }

        if(Input.GetMouseButtonDown(1)){
            
            Subject.NotifyObserver(IN_Text[index]);
            ++index;
        }

    }
}
```
マウスの右クリックをするたびに文章が変わって0~2でループする。
これを空のGamaObjectに貼り付ける。

最後にCanvasのScrollViewに下記のプログラムを貼り付ける

```Unity:TextShow.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;


public class TextShow : MonoBehaviour,My.ITextObserver<string>
{
    [SerializeField]
    public TextSender _test;

    [SerializeField]
    private ScrollRect scrollRect;

    [SerializeField]
    private Text textLog;

    private string GetText;

    public RectTransform originalElement;

    public RectTransform content;

    int elementNum;
     
     void Awake(){
         originalElement.gameObject.SetActive(false);
     }
    void Start()
    {
        scrollRect.verticalNormalizedPosition = 0;
        _test.Subject.AddObserver(this);
        textLog.text = GetText;
        elementNum = 15;

    }


    void Update()
    {
        if(GetText != textLog.text && GetText != null){
            OnSubmit();
            DeleteText();
        }
        
    }

    public void OnSubmit(){

        textLog.text = GetText;
        Debug.Log(textLog.text);

        var element = GameObject.Instantiate<RectTransform> (originalElement);
        element.SetParent(content, true);
        element.SetAsLastSibling();
        element.gameObject.SetActive(true);

        --elementNum;
    }

    public void DeleteText(){
        var clones = GameObject.FindGameObjectsWithTag("Element");
        if(elementNum <= 0){
            
            Destroy(clones[elementNum]);
            ++elementNum;
        }
    }

    public void Notify(string s){
        GetText  = s;
    }
    public string OnRecieved(string value){
        Debug.Log(value);
        return value;
     }
}
```
15まで表示で一番古いデータを一つづつ破壊。
NotifyでTextSenderで持っているテキスト内容が変化した文面を教えてくれる。
教えてくれたstring sをGetTextに入れてtextLog.textと内容が違ったらScrollViewに表示みたいな流れになっているはず。
出力された文面はElementのタグを付けたImageが保持するので強制的に分離させるみたいな構造なんだと思う……

最後にUnity側で
![TextShow.png](https://qiita-image-store.s3.amazonaws.com/0/369570/9e0749cb-3388-3082-00a5-e46789d92da9.png)

上記の様に設定。

成果：まだ全然わからん！でもとりあえず、動くものは作れた。

反省：アイテム取得とかダメージとかのメッセージを表示したいと思って色々漁った末にこんな物が出来上がりました。Notifyのところとかなにか使い方間違えている気がするし、Removeの使い所はどうすれば良いのかとかふわっとしている部分は色々ある。TextSenderに情報を集めればアイテム取得やダメージ表示できそうって思っているんだけどTextSenderに集めようとすると2重Observerみたいになってしまうからなにか考え方が間違ってる気がしている。


--------------------------------------------------
CやC++はかじったことあるけどC#全然わからない。
interfaceなにそれ！の状態から4日ぐらいかけて勉強。
Observerパターンを初めて組んでみた。

ほとんどテラシュールブログさんの
http://tsubakit1.hateblo.jp/entry/2015/10/19/022720
と
人生詰んでるし死ぬ前にゲーム作ってみるさん
http://ntgame.wpblog.jp/2018/07/14/post-1796/
の上2つの記事を素人がいじくり回した結果です。

勉強はとりすーぷさんのQiitaや動画、スライドを参考にさせていただきました。
https://learning.unity3d.jp/1324/
上の動画の説明が分かった気に一番なりました！

※なにか問題がある部分があれば削除訂正しますのでメールかTwitterまでお願いします。