消息事件的监听与分发

消息事件的监听与分发
Unity游戏中通常使用的消息事件是直接使用委托实现的,这个对开发团队来说是件麻烦的事,所以需要封装
一个统一的接口供开发者使用,使用事件机制的优点是不需要在UI上直接挂接代码。
 
首先定义一个消息事件的基类,这个是消息底层的实现方式,主要目的是初始化消息;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum CEventType //事件类型,最好放在单独的脚本中
{
    GAME_OVER,
    GAME_WIN,
    PAUSE,
    ENERGY_EMEPTY,
    GAME_DATA,
}

public class CBaseEvent {

    protected Hashtable arguments;  //哈希表用来存储委托事件
    protected CEventType type;  //事件类型
    protected Object sender; //存储事件分发的对象
    public CEventType Type
    {
        get { return this.type; }
        set { this.type = value; }
    }
    public IDictionary Params
    {
        get
        {
            return this.arguments;
        }
        set
        {
            this.arguments = value as Hashtable;
        }
    }   
    public Object Sender
    {
        get
        {
            return this.sender;
        }
        set
        {
            this.sender = value;
        }
    }

    public override string ToString()
    {
        return this.type+"["+((this.sender==null)?"null":this.sender.ToString())+"]";
    }
    public CBaseEvent Clone()
    {
        return new CBaseEvent(this.type,this.arguments,sender);
    }
    public CBaseEvent(CEventType type,Object sender)
    {
        this.type = type;
        this.sender = sender;
        if(this.arguments==null)
        {
            arguments = new Hashtable();
        }
    }
    public CBaseEvent(CEventType type,Hashtable args,Object sender)
    {
        this.type = type;
        this.arguments = args;
        this.sender = sender;
        if(this.arguments==null)
        {
            this.arguments = new Hashtable();
        }
    }

}
事件的监听和分发接口封装在游戏逻辑中经常呗调用,同时也是对外提供的接口:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public delegate void CEventListenerDelegate(CBaseEvent evt);
public class CEventDispatcher{
    static CEventDispatcher instance;
    public static CEventDispatcher GetInstance()
    {
        if (instance == null) instance = new CEventDispatcher();
        return instance;
    }
    private Hashtable listeners = new Hashtable();
    //增加事件监听
    public void AddEventListener(CEventType eventType,CEventListenerDelegate listener)
    {
        CEventListenerDelegate cEventDelegate = this.listeners[eventType] as CEventListenerDelegate;
        cEventDelegate = (CEventListenerDelegate)Delegate.Combine(cEventDelegate, listener);
        //上面相当于    //cEventDelegate += listener;

        this.listeners[eventType] = cEventDelegate;
    }
    public void RemoveEventListener(CEventType eventType,CEventListenerDelegate listener)
    {
        CEventListenerDelegate cEventDelegate = this.listeners[eventType] as CEventListenerDelegate;
        if(cEventDelegate!=null)
        {
            cEventDelegate = (CEventListenerDelegate)Delegate.Remove(cEventDelegate,listener);
            //上面相当于    //cEventDelegate -= listener;
        }
        this.listeners[eventType] = cEventDelegate;
    }
    public void DispatchEvent(CBaseEvent evt)  //触发函数
    {
        CEventListenerDelegate cEventDelegate = this.listeners[evt.Type] as CEventListenerDelegate;
        if(cEventDelegate!=null)
        {
            try
            {
                cEventDelegate(evt);   //内置触发函数
            }
            catch(System.Exception e)
            {
                throw new System.Exception(string.Concat(new string[]
                    {
                        "Error dispathcing event",
                        evt.Type.ToString(),
                        ":",
                        e.Message,
                        " ",
                        e.StackTrace
                    }),e);
            }
        }
    }

    public void RemoveAll()
    {
        this.listeners.Clear();
    }
}

下面是监听和分发的使用
 CEventDispatcher.GetInstance().AddEventListener(CEventType.GAME_WIN, NumberListener);//添加监听事件
 
CEventDispatcher.GetInstance().DispatchEvent(new CBaseEvent(CEventType.GAME_WIN, this));  //触发NumberListener函数


完整调用实例
public class CEventDelegateExample : MonoBehaviour {

	
	void Start () {
        CEventDispatcher.GetInstance().AddEventListener(CEventType.GAME_WIN, NumberListener);//添加监听事件
	}
	
	
	void Update () {
        if(Input.GetMouseButtonDown(0))
        {
            CEventDispatcher.GetInstance().DispatchEvent(new CBaseEvent(CEventType.GAME_WIN, this));  //如果按下鼠標左键,触发NumberListener函数
        }

    }
    void OnDestroy()
    {
        CEventDispatcher.GetInstance().RemoveEventListener(CEventType.GAME_WIN, NumberListener);
    }
    public void NumberListener(CBaseEvent evt)
    {
        Debug.Log("Mouse Down");
    }
}


下面尝试用Lua来实现事件的监听与触发


GlobalListenerMap = { ---所有事件存储表
--[[
	[target] = {
		[type#key] = listenerList,
	}
--]]
}
local cacheEventTypeKey = {--所有的type#key存储表
	--[eventType] = { count, keyvalue}
}
local GlobalObserver = false
function GetGlobalObserver()
	return GlobalObserver
end

local GlobalTarget = false
function GetGlobalTarget()
	return GlobalTarget
end
----------------------------------------------------
local function genIndex(eventType,key) --生成type#key,作为target中的事件列表的索引
	local eventTypeMap=cacheEventTypeKey[eventType]
	if not eventTypeMap then
		local f = string.format("%s#%s", eventType, tostring(key))
		local keyValue={}
		keyValue[tostring(key)]=f
		eventTypeMap={}
		eventTypeMap._KeyCount=1 --计数
		eventTypeMap._KeyValue = keyValue
		cacheEventTypeKey[eventType] = eventTypeMap
		return f
	end

	local keyValue=eventTypeMap._KeyValue
	local f=keyValue[tostring(key)]
	if f then
		return f
	end

	local keyCount=eventTypeMap._KeyCount+1
	if keyCount>1000 then
		keyValue = {}
		keyCount=1
		eventTypeMap._KeyValue=keyValue
	end
	eventTypeMap._KeyCount=keyCount --计数
	f=string.format("%s#%s", eventType, tostring(key))
	keyValue[tostring(key)]=f
	eventTypeMap._KeyValue = keyValue
	return f
end

local nextListenerId = 0
local function genListenerId()
	nextListenerId = nextListenerId + 1
	return nextListenerId
end
-------------------事件监听处理器---------------------------------
local cListener={}
local cListenerMeta={__index=cListener}
function cListener.Create(observer,target,type,key,callback)
	local OId=genListenerId()
	local typeKeyIndex=genIndex(type,key)
	local listener =  {
		observer,
		target,
		type,
		key,
		callback,
		typeKeyIndex,
		OId,
	}
	setmetatable(listener,cListenerMeta)

	local targetListenerList=GetTargetListenerList(target,typeKeyIndex)
	table.insert(targetListenerList,listener)

	if IsTable(observer) then---绑定在监听者上,在监听者release时便于删除事件
		local objListenerMap = GetSubTableWithDefault(observer, "__eventListenerMap")
		objListenerMap[OId] = listener
	end

	return listener
end
function cListener:Remove()
	local typeIndex=self:GetTypeKey()
	if not typeIndex then
		return
	end
	self[6]=nil

	local observer=self:GetObserver()
	if IsTable(observer) then
		local OId=self:GetObserLisID()
		observer.__eventListenerMap[OId]=nil
	end

	local target = self:GetTarget()
	local typeListenerMap = GlobalListenerMap[target]
	if not typeListenerMap then
		return
	end

	local listenerList = typeListenerMap[typeIndex]
	local len = #listenerList
	if len == 1 and listenerList[1] == self then
		listenerList[1] = nil

		typeListenerMap[typeIndex] = nil
		if next(typeListenerMap,nil) then
			GlobalListenerMap[target] = nil
		end
	else
		local key = table.member_key(listenerList, self)  --- O(N)
		if key then
			----使用最后一个元素覆盖并删除最后一个元素
			listenerList[key] = listenerList[len]
			listenerList[len] = nil
		end
	end
end
function cListener:GetObserver() return self[1] end
function cListener:GetTarget() return self[2] end
function cListener:GetType() return self[3] end
function cListener:GetKey() return self[4] end
function cListener:GetCallBack() return self[5] end
function cListener:GetTypeKey() return self[6] end
function cListener:GetObserLisID() return self[7] end

function cListener:toString()
	local str=self:GetTarget()..":"..genIndex(self:GetType(),self:GetKey())
	return str
end
function cListener:Handle(event)
	-- print("Handle this event",self:toString())
	local callback = self:GetCallBack()
	xpcall(callback,
			function() 
				print(self:toString()..":Handle error") 
			end, 
		event)
end
--------------------------事件--------------------------------
local cEvent={}
function cEvent.Create(target, type, key, data)
	local event = {
		target,
		type,
		key,
		data,
	}
	setmetatable(event, {__index=cEvent})
	return event
end
function cEvent:GetTarget() return self[1] end
function cEvent:GetType() return self[2] end
function cEvent:GetKey() return self[3] end
function cEvent:GetData() return self[4] end

----------------------------------------------------------
function GetSubTableWithDefault(target,key)
	local value=target[key]
	if not value then
		value={}
		target[key]=value
	end
	return value
end
function GetTargetListenerList(target,typeKeyIndex)
	local targetMap=GetSubTableWithDefault(GlobalListenerMap,target)
	local listenerList=GetSubTableWithDefault(targetMap,typeKeyIndex)
	return listenerList
end

function AddEventListener(observer,target,type,key,callback)
	local listener=cListener.Create(observer,target,type,key,callback)
	return listener
end

function RemoveEventListener(listener)
	if listener then
		listener:Remove()
	end
end
---移除自己监听别人的
function RemoveAllEventListener(observer)
	if not IsTable(observer.__eventListenerMap) then return end
	for _, listener in pairs(observer.__eventListenerMap) do
		listener:Remove()
	end
	observer.__eventListenerMap = nil 
end
function RemoveEventListener(observer,target,type,key)
	local toDelListenerList={}
	local typeKeyIndex=genIndex(type, key)
	local typeListenerMap = GlobalListenerMap[target]
	if not typeListenerMap then
		return
	end

	local listenerList = typeListenerMap[typeKeyIndex]
	if not listenerList then
		return
	end

	for _, listener in pairs(listenerList) do
		if IsTable(observer) then
			if listener:GetObserver() == observer then
				table.insert( toDelListenerList, listener)
			end
		else
			table.insert( toDelListenerList, listener)
		end
	end

	for _, listener in ipairs(toDelListenerList) do
		listener:Remove()
	end
end

---事件触发
function DispatchEvent(target,type,key,data)
	local event=cEvent.Create(target,type,key,data)
	local typeKeyIndex=genIndex(type,key)
	local targetListenerList=GetTargetListenerList(target,typeKeyIndex)
	for _, listener in pairs(targetListenerList) do
		listener:Handle(event)
	end
end

function __init__(module, updated)
	GlobalObserver = clsObject:New()
	GlobalTarget = clsObject:New()
end

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值