2014年6月12日 星期四

C#元件分享 - 事件處理器EventSender


此元件作者為我的好友 - 華仔(聖淨之風),
他為了讓更多人能使用這個簡潔方便的事件處理器,就由我在此跟大家分享囉!

使用方式很簡單,只要將這兩個元件檔案<EventSender.cs>和<EventSenderT.cs>
放在Unity專案下即可,不必掛到場景物件上。
亦可使用在Unity以外的C#專案中。

<EventSender> - 用來發佈不含參數的事件
using UnityEngine;
using System;
using System.Collections;

public class EventSender
{
    public delegate void PubEventHandler();  //定義委託
    private event PubEventHandler onPublish; //定義事件訪問器
 
    /// <summary>事件發佈</summary>
    public void DoSomething ()
    {
        PubEventHandler handler = onPublish; //防止多緒錯誤
        if (handler != null)
            handler();
    }

    /// <summary>訂閱事件</summary>
    /// <param name='in_onEvent'>觸發事件函式</param>
    public void Register (PubEventHandler in_onEvent)
    {
        onPublish += in_onEvent;
    }

    /// <summary>解除訂閱</summary>
    /// <param name='in_onEvent'>觸發事件函式</param>
    public void UnRegister (PubEventHandler in_onEvent)
    {
        onPublish -= in_onEvent;
    }
}

<EventSenderT> - 用來發佈包含參數的事件
(參數可以是任意類型,也可以自定義一個class,在其中包許多參數)
using UnityEngine;
using System;
using System.Collections;

public class EventSenderT<T>
{
    public delegate void PubEventHandler(T e); //定義委託
    private event PubEventHandler onPublish; //定義事件訪問器
 
    /// <summary>事件發佈</summary>
    public void DoSomething (T e)
    {
        PubEventHandler handler = onPublish; //防止多緒錯誤
        if (handler != null)
            handler(e);
    }
 
    /// <summary>訂閱事件</summary>
    /// <param name='in_onEvent'>觸發事件函式</param>
    public void Register (PubEventHandler in_onEvent)
    {
        onPublish += in_onEvent;
    }
 
    /// <summary>解除訂閱</summary>
    /// <param name='in_onEvent'>觸發事件函式</param>
    public void UnRegister (PubEventHandler in_onEvent)
    {
        onPublish -= in_onEvent;
    }
}

當要發佈事件時,首先要宣告事件,
例如假設我們在這支程式<ButtonArthur>中宣告兩種事件,
然後把<ButtonArthur>掛在場景中的一個Cube物件上
(Unity -> GameObject -> Create Other -> Cube):
using UnityEngine;
using System.Collections;

public class ButtonArthur : MonoBehaviour
{
    /// <summary>索引</summary>
    public int iInx = 0;
    /// <summary>宣告滑鼠移入按鈕事件,輸出類型為int</summary>
    public EventSenderT<int> evtBtnEnter = new EventSenderT<int>();
    /// <summary>宣告滑鼠移出按鈕事件,沒有輸出值</summary>
    public EventSender evtBtnExit = new EventSender();

    /// <summary>當滑鼠移入時</summary>
    void OnMouseEnter ()
    {
        //發佈事件,輸出類型為int,int的值為iInx
        evtBtnEnter.DoSomething(iInx);
    }

    /// <summary>當滑鼠移出時</summary>
    void OnMouseExit ()
    {
        //發佈事件,沒有輸出值
        evtBtnExit.DoSomething();
    }
}

而其他程式只需要在事件發佈之前,預先訂閱事件,
假設我們在<PanLogin>訂閱了<ButtonArthur>的 evtBtnEnter 及 evtBtnExit 事件,
並且把<PanLogin>掛在場景中隨便一個物件上:
using UnityEngine;
using System.Collections;

public class PanLogin : MonoBehaviour
{
    /// <summary>打算訂閱的按鈕,要記得將指標指向場景中的<ButtonArthur></summary>
    public ButtonArthur btn = null;

    void Awake ()
    {
        //先訂閱事件,當收到事件時,事件內容會輸入此處指定的函式
        btn.evtBtnEnter.Register(onBtnEnter);
        //先訂閱事件,當收到事件時,事件內容會輸入此處指定的函式
        btn.evtBtnExit.Register(onBtnExit);
    }

    //對應事件當初宣告的類型,輸入值必須為int
    void onBtnEnter (int i_i)
    {
        //顯示收到的事件內容
        Debug.Log (i_i);
        //若要解除訂閱,只要使用UnRegister函式即可
        btn.evtBtnEnter.UnRegister(onBtnEnter);
    }

    //對應事件當初宣告的類型,不能有輸入值
    void onBtnExit ()
    {
        //顯示收到事件
        Debug.Log ("onBtnExit");
        //若要解除訂閱,只要使用UnRegister函式即可
        btn.evtBtnExit.UnRegister(onBtnExit);
    }
}

順道一提,當多個程式都訂閱了同一事件時,
是依照訂閱順序來處理的,一個處理完才換下一個。

訂閱時也要注意,若多次訂閱同一事件的話,就像同一份報紙訂了N次,
到時當然會收到N份報紙了。
因此若不需要接收事件時,就可以使用UnRegister來解除訂閱,
即使原本沒有訂閱該事件,對其解除訂閱時也不會出錯,可以安心使用。

沒有留言:

張貼留言