UnityActionとUnityEvent

## 0.0 はじめに
Unityで準備されたUnityActionとUnityEventは、メソッドを値として使用することができるdelegate（デリゲート）やEvent（イベント）で大変便利な機能です。メソッドを複数登録できそれらを一度に実行したり、他のオブジェクトにある関数を実行したりできます。







## 0.1 delegate（デリゲート）
UnityActionとUnityEventを使う前にデリゲートについて簡単にまとめておきます。
delegateは日本語で「委譲（いじょう）」と言い、代わりのものを指すようです。
プログラミングでは簡単に言うと**関数を入れられる変数**のことです。この変数を実行すると入っている関数が実行されます。
尚、delegateを使うには宣言が必要です。delegateに渡せる関数は宣言した型と返り値、引数など形が完全に同じでなければなりません。
ちなみに先に関数を作って、コピペ（｛｝部はすべて削除）、そしてdelegate挿入＆型名の変更するだけで簡単に形の同じdelegateを宣言できます。
このあとのActionやFuncも同じですが、+=を使って登録する関数を増やすことができます。




**デリゲートに何もメソッドが登録されていない時にデリゲートを実行しようとすると例外エラーが発生します。?演算子とInvokeメソッドを使って登録したメソッドを呼び出しましょう。引数はInvokeの（）内に入れます。**


```C# DelegateTest.cs
using UnityEngine;

public class DelegateTest :MonoBehaviour
{
    delegate void DelegateType(int price); // delegate型の宣言

    private void Start() {

        DelegateType dele = Kakaku; // delegateに関数を登録
        dele += Shohizei; // delegateに関数を追加登録
        dele?.Invoke(100); // 登録したdelegateを実行

    }

    public void Kakaku(int price) {
        Debug.Log("価格は" + price + "円です");
    }
    public void Shohizei(int price) {
        Debug.Log("消費税は" + price * 0.1 + "円です");
    }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/d114d1de-c89f-bd3e-91b2-0d128bec80e9.png)











## 0.2 ActionとFunc
C#にはデリゲートの型の宣言を必要としないActionとFuncがあります。
返り値が無い場合がAction、有る場合が Funcです。
引数は<>内で設定します。
尚、ActionとFuncを使うときはusing Systemが名前空間に必要です。





**Actionの使い方**
```C# ActionTest.cs
using UnityEngine;
using System; // 追加

public class ActionTest : MonoBehaviour {
    private void Start() {

        Action dele = Method1; // delegateに関数を登録
        dele += Method2; // delegateに関数を追加登録
        dele?.Invoke(); // delegateを実行

        Action<int> deleR = Method3; // delegateに引数あり関数を登録
        deleR?.Invoke(3); // delegateを実行
    }

    public void Method1() {
        Debug.Log("Method1です");
    }
    public void Method2() {
        Debug.Log("Method2です");
    }
    public void Method3(int no) {
        Debug.Log("Methodの番号は" + no + "です");
    }
}
```








![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/41fb1566-38a1-5787-9976-b2cf2edc2841.png)


　










**Funcの使い方**
返り値が必要な場合はFuncを使います。
**<>内の最後の型が返り値です。 　< int, int, string > の場合は最後の stringが返り値となります。**
```C# FuncTest.cs
using UnityEngine;
using System; // 追加

public class FuncTest : MonoBehaviour {
    private void Start() {

        Func<int, string> func = Zeikomi; // delegateに関数を登録
        Debug.Log(func?.Invoke(100)); // delegateを実行 funcの返り値を表示

    }

    public string Zeikomi(int price) {
        return "税込み価格は " + price * 1.1 + "円です";
    }
}
```









## 0.3 匿名関数
匿名関数（匿名メソッド）を使うと関数を省略できます。


```C# NamelessTest.cs
using UnityEngine;
using System; // 追加

public class NamelessTest : MonoBehaviour {
    private void Start() {

        Action<int> act = delegate (int price) {
            Debug.Log("価格は " + price + " 円です");
        };
        act?.Invoke(200);

        Func<int, string> func = delegate(int price){
            return "税込み価格は " + (price * 1.1) + "円です";
        };
        Debug.Log(func?.Invoke(500));
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/020af68f-5bdc-07da-6ae4-8074d594e763.png)
















## 0.4 ラムダ式を使った匿名関数
さらに省略できるのがラムダ式を使った関数登録です。
ちなみに｛｝を使うことによって複数の処理を記述することが可能です。

```C# LambdaTest.cs
using UnityEngine;
using System; // 追加

public class LambdaTest : MonoBehaviour {
    private void Start() {

        Action<int> act = (int price) => Debug.Log("価格は " + price + " 円です");
        act?.Invoke(50);

        Func<int, string> func = (int price) => {
            Debug.Log("Func ラムダ式だよ");
            return "税込み価格は " + (price * 1.1) + "円です";
        };
        
        Debug.Log(func?.Invoke(300));
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/049acc72-2050-1d49-3b64-c024ad05cbb4.png)













## 0.5 eventをつけた宣言
次にeventをつけて宣言を行ってみます。
動き方はこれまでのdelegateとほぼ同じですが、eventをつけるとそのクラスでしか実行できなくなります。
他のクラスでもdelegateへ登録、追加、削除はできますが実行はできません。
また、eventをつけたdelegateへは+=で登録を行います。

```C# EventTest.cs
using UnityEngine;
using System;

public class EventTest : MonoBehaviour {
    
    private void Start() {

        EventTestClass testClas = new EventTestClass();
        testClas.dele += Kakaku;
        //testClas.dele?.Invoke(120); // eventが付いているのでエラーが出る
        testClas.ToDoTest(150);
    }

    public void Kakaku(int price) {
        Debug.Log(price + "円です");
    }
}

// Eventテスト用のクラス
public class EventTestClass{

    public event Action<int> dele;

    public void ToDoTest(int v) {
        dele?.Invoke(v);
    }
}
```









## 1.0 UnityAction
ここからようやくUnityActionについての説明です。
UnityActionは簡単に言えば返り値がvoidのdelegateのことです。
Actionと同じく型の宣言は必要ありません。
また、+=を使って登録する関数を増やすことができます。
**尚、UnityAction（UnityEvent）を使うには名前空間にusing UnityEngine.Eventsの追加が必要です。**

```C# UnityActionTest.cs
using UnityEngine;
using UnityEngine.Events; // これ忘れないように

ublic class Test : MonoBehaviour{
    private void Start() {
        MainClass mainClass = new MainClass(); // Testクラスのインスタンス生成
        mainClass.unityAction = Kakaku; // TestクラスのUnityActionに関数登録
        mainClass.unityAction += Zeikomi; // TestクラスのUnityActionに関数追加
        mainClass.CallBackTest(); // Testクラスの関数実行
    }

    public void Kakaku(int price) {
        Debug.Log("価格は " + price + " 円です");
    }

    public void Zeikomi(int price) {
        Debug.Log("税込み価格は " + price * 1.1 + "円です");
    }
}

public class MainClass{

    public UnityAction<int> unityAction;

    public void CallBackTest() {
        Debug.Log("リンゴを買います"); // 処理を行う
        unityAction(200); // コールバック
    }
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/ac657610-a843-1f85-465e-75a8775711ad.png)















## 2.0 UnityEvent
UnityEventはUnityActionやeventと動作ほぼ同じですが、関数の登録にはAddListener()を使います。
**UnityAction同様に名前空間にusing UnityEngine.Eventsの追加が必要です。**

下記の例は何らかの処理を終わった後に登録関数を呼び出す流れでコールバックしています。

　
UnityEvent宣言クラス
```C# UnityEventTest.cs
using UnityEngine;
using UnityEngine.Events; // これ忘れないように

public class UnityEventTest : MonoBehaviour
{
    public UnityEvent unityEvent; // UnityEvent準備

    public void CallBackMethed() {
        // 何らかの処理
        Debug.Log("UnityEventTestクラスのCallBackMethed関数です");

        unityEvent?.Invoke(); // UnityEventの実行（登録関数がある場合）
    } 

}

```




　
関数登録クラス
```C# Test.cs
using UnityEngine;

public class Test : MonoBehaviour {

    [SerializeField] UnityEventTest unityEventTest; // UnityEventTestインスタンス格納用

    private void Start() {
        unityEventTest.unityEvent.AddListener(Method); // 関数登録
        unityEventTest.CallBackMethed(); // コールバック実行
    }

    // UnityEventに登録する関数
    void Method() {
        Debug.Log("Testクラスの関数が実行されました");
    }
}
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/cbc9ff3e-021e-62b8-c9d2-5efe6e080a4c.png)











## 3.0 実践
では実践です。UnityEventを使ってマウスのクリックやタッチで画像の色が変わるプロジェクトをつくりましょう。
まずは何でもよいので白い画像を準備してHierarchy（ヒエラルキー）に入れましょう。(Circleと名付けました)
またCreateからCreate Emptyを選んで空のオブジェクトを作ります。(ChangeColorManagerと名付けました)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/a5a090a7-cd2f-14a1-9a6a-ddd662dbae48.png)















CircleオブジェクトおよびChangeColorManagerオブジェクトに下記のスクリプトを張り付けます。
```C# Circle.cs 
using UnityEngine;

public class Circle : MonoBehaviour
{
    [SerializeField] Color c; // 変更後の色

    // 色を変える関数
    public void ChangeColor() {
        GetComponent<SpriteRenderer>().color = c;
    }
}
```





```C# ChangeColorManager.cs
using UnityEngine;
using UnityEngine.Events; // UnityEventを使うため追加

public class ChangeColorManager : MonoBehaviour
{
    [SerializeField] UnityEvent onClick; // デリゲート（イベント）

    private void Update() {

        // マウスクリック、画面タッチでデリゲート実行
        if (Input.GetMouseButton(0)) {
            onClick?.Invoke(); // デリゲート実行
        }
    }
}
```








　
　

Circleオブジェクトに張り付けたCircleスクリプトでは変更後の色を設定できます。（ここでは赤色に設定してます）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/a46457cf-2f01-30d7-1ef2-b453ede4d1b1.png)

ChangeColorManagerオブジェクトに張り付けたChangeColorManagerスクリプトにはOnClick()の関数が出ています。＋ボタンを押して関数を登録できるようにします。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/d294ccff-689c-9bb2-98f2-29a512bfc77f.png)

















ヒエラルキーのCircleオブジェクトをドラッグ＆ドロップしてプルダウンメニューからCircle、ChangeColor()関数を選びます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/cf6f72fe-2980-622f-aafd-5dded8c2935f.png)



では実行してみましょう。
マウスクリックもしくは画面タッチで画像の色が変われば成功です。




オブジェクトが１つだけではUnityEventを使った恩恵が分からないのですが、登録オブジェクト（登録関数が）多くなると効果が分かりやすいと思います。
![GIF UnityEvent.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/86239660-8d7a-7348-2859-442204d208ae.gif)




















## 4.0 まとめ
デリゲートすべてに共通　：　引数は使えるが形は同じでなければならない。

**delegate　：　宣言が面倒だが、返り値のある関数も扱える
Action　：　宣言の必要がない。返り値はvoidのみ
Func　：　宣言の必要がない。返り値のある関数もOK
event　：　この修飾子をつけると他のクラスから実行できないので安全　ただし関数の登録は+=で行う
UnityAction　：　void型のdelegate、Actionと同じく宣言の必要なし、基本的にActionとかと同じ動き。=や+=で関数登録できる。
UnityEvent　：　インスペクタービューから関数登録が可能です。スクリプト上ではAddListener()で関数を追加する。**

実行の際はdelegate?.Invoke()とすればデリゲートに関数が登録されてなくてもエラーが出ない。

以上です。