前往
大廳
主題

【Unity紀錄】UnityEvent、Event的入門概念及應用

高雄鐵道具象 | 2021-03-29 20:44:11 | 巴幣 14 | 人氣 5307

不管是UnityEvent、Event,基本上都是基於Delegate所產生變體,而一旦使用了Event後,便可以極大幅的減少腳本之間的依賴,也不用在苦惱要在start還是update中getcomponet了,因為幾乎都可以用註冊的方式直接完成這些操作。
而其中UnityEvent更是將其完全盡可能極簡化,特別是如果不需要有額外參數傳入的UnityEvevt更是可以直接在Editor中進行操作,其使用難度以及實用性可以說是必學一點都不為過!
接著先上一個小專案的codeing,參考自以下這支影片

接著附上加上了我可以理解的coding
其中的MainScript
using System.Collections;
using System.Collections.Generic;
using TMPro;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
public class Collector : MonoBehaviour
{
    [SerializeField]
    TMP_Text _text;
    [SerializeField]
    List<Collectible> _gatherables;//要蒐集的東西
    [SerializeField]
    UnityEvent OnCompleteEvent;

    List<Collectible> _collectiblesRemaining;
    
    void OnEnable()
    {
        _collectiblesRemaining = new List<Collectible>(_gatherables);

        foreach (var collectible in _collectiblesRemaining) //逐一檢視collectiblesRemaining(Collectible新宣告)上的每個OnPick事件
        {
            collectible.OnPickup += HandlePickup;//在每個OnPickup中註冊新的HandlePickup

            UpdateText();
        }
    }

    void HandlePickup(Collectible collectible)
    {
        _collectiblesRemaining.Remove(collectible);
        UpdateText();

        if (_collectiblesRemaining.Count == 0)
        {
            OnCompleteEvent.Invoke();//開始執行事件,且因為 OnCompleteEvent為UnityEvent因此不需要以?.Invoke作為啟動,因為Unity不可能為null
        }
    }
    void UpdateText() //text更新
    {
        _text.SetText($"{_collectiblesRemaining.Count}more...");
        if (_collectiblesRemaining.Count == 0 || _collectiblesRemaining.Count == _gatherables.Count)
        {
            _text.enabled = false;
        }
        else
        {
            _text.enabled = true;
        }
    }

    [ContextMenu("AutoFill Collectibles")]
    void AutoFillCollectibles()
    {
        _gatherables = GetComponentsInChildren<Collectible>().Where(t => t.name.ToLower().Contains("red")).ToList();//可在該物件底下的子物件搜尋有<Collectible>的物件的name是否有Red並自動充進List,在編輯畫面中Inspector對著該script右鍵呼喚

    }
   
}

接著是撿拾的東西上的腳本
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Collectible : MonoBehaviour
{
    public event Action<Collectible> OnPickup;

    private void OnTriggerEnter2D(Collider2D other)
    {
        var player = other.GetComponent<Player>();
        if (player != null)
        {

            //if(OnPickup != null)
            //{
            //OnPickup(this)
            //}
            //↑原應該為上面這些字串
            OnPickup?.Invoke(this);//由於該Event有可能為null,因此需?.Invoke

            gameObject.SetActive(false);
        }
    }
}

在這個範例中,主要是示範了一般的Event以及UnityEvent在使用的一些差別,還有一個自動搜尋目標物件的酷炫功能示範,接著來試試看經過極簡化的UnityEvent
===============================================================================

一、不帶參數 No Parameter

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class UnityEventTest : MonoBehaviour
{
    [SerializeField]
    UnityEvent CubeCount1;
    [SerializeField]
    UnityEvent CubeCount2;
 

    public int ClickTime = 0;

    void Update()
    {
        if (ClickTime == 5)
        {
            CubeCount1.Invoke();
        }
        if (ClickTime == 8)
        {
            CubeCount2.Invoke();
        }
        
    }

    public void CountUP() //註冊進下圖中的button
    {
        ClickTime++;
    }
}
Editor中的畫面就是長得跟button很是相似,只要先需告好可在Editor中可見的UnityEvent就可以直接Editor中進行調選,甚至可以直接調用tag、setactive等這些常用的api連code都不用打,有夠方便。

二、有帶參數 Has Parameter

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class MyEvent : UnityEvent<int, bool> { }
//因為UnityEvent<T0,T1,T2...>(最多T3)是抽象類,所以需要聲明一個類來繼承它
public class UnityActionWithParameter : MonoBehaviour
{
    
    public MyEvent myEvent = new MyEvent();//實例化
    public UnityAction<int, bool> action;//聲明一個具有int,bool的unityaction並命名為action

    private float nowtime;
    int count = 0;
    
    void Start()
    {    
        action = new UnityAction<int, bool>(MyFunction);//將MyFunction註冊進action
        action += MyFunction2;//繼續增加註冊的function
        myEvent.AddListener(action);//讓myEvent監聽action
        //myEvent.RemoveListener(xxxx);  則是反註冊其中的action
    }


    void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            print("按下A鍵後輸出");
            myEvent.Invoke(10,true);
        }
        if (Input.GetKeyDown(KeyCode.B))
        {
            print("按下B鍵後輸出");
            myEvent.Invoke(20, false);
        }
    }

    public void MyFunction(int i,bool x)
    {
        if (x)
        {
            print(i);
        }
       
    }
    public void MyFunction2(int i,bool x)
    {
        if (x == false)
        {
            print(i * 2);
        }
    }
}
看結果

差不多就這樣,UnityEvent可以說是把Event給再次經過魔改後變得超級無敵簡單好上手,雖然帶參數的UnityEvent跟一般Event較為相近,想使用上較為不直觀,但是在程式設計上則是提供了更多的發揮空間。

UnityEvent就差不多到這裡,接下來應該會記錄學習塔防遊戲以及格狀回合制遊戲的過程,卡牌遊戲那60多個教學影片光是看了就想吐,但是為了專案還是得要乖乖硬啃完。
加油吧!

創作回應

樂小呈
習慣用System.Action,在編輯器中指定事件不好debug [e18]
2021-03-29 20:48:08
樂小呈
加油~
2021-03-29 20:48:30

更多創作