【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher
2021-07-10 21:04
标签:接口 学习 过程 nbsp 服务器 pat 框架 namespace 分析 最近马三换了一家大公司工作,公司制度规范了一些,因此平时的业余时间多了不少。但是人却懒了下来,最近这一个月都没怎么研究新技术,博客写得也是拖拖拉拉,周六周天就躺尸在家看帖子、看小说,要么就是吃鸡,唉!真是罪过罪过。希望能从这篇博客开始有些改善吧,尽量少玩耍,还是多学习吧~ 好了扯得有点远了,来说说我们今天博客的主题——“用C#和Lua实现Unity中的事件分发机制”,事件分发机制或者叫事件监听派发系统,在每个游戏框架中都是不可或缺的一个模块。我们可以用它来解耦,监听网络消息,或者做一些异步的操作,好处多多(其实是别人的框架都有这个,所以我们的框架也必须有这玩意~)。今天马三就和大家一起,分别使用C#和Lua实现两种可以用在Unity游戏开发中的事件分发处理机制,希望能对大家有些帮助吧~ 首先我们来实现C#版本的事件分发机制,目前这套流程已经集成到了马三自己的 ColaFrameWork框架 中了。这套框架还在架构阶段,里面很多东西都不完善,马三也是会随时把自己的一些想法放到里面,大家感兴趣的话也可以帮忙维护一下哈! 一般来说事件订阅、派发这种机制都是使用观察者模式来实现的,本篇博客也不例外,正是利用了这种思想。为了解耦和面向接口编程,我们制定了一个接口IEventHandler,凡是观察者都需要实现这个接口,而GameEventMgr事件中心维护了一个IEventHandler列表,保存着一系列的观察者,并在需要的时候进行一系列的动作。这样操作正是遵循了依赖倒置的设计原则:“高层模块不应该依赖于低层模块,两者都应该依赖于抽象概念;”、“抽象接口不应该依赖于实现,而实现应该依赖于抽象接口”。下面的代码定义了IEventHandler接口和一些委托还有事件传递时需要携带的参数。 然后我们再来看一下最核心的事件中心处理器GameEventMgr是如何实现的,还是先上一下全部的代码 GameEventMgr.cs: 我们在其内部维护了一个handlerDic字典,它的key是int类型的,对应的其实就是我们在上面定义的EventType 这个枚举,它的value是一个元素为IEventHandler类型的列表,也就是说我们按照不同的事件类型,将监听者分为了几类进行处理。监听者是可以监听多个消息类型的,也就是说一个监听者实例可以存在于多个列表中,这样并不会产生冲突。我们就从RegisterHandler(IEventHandler handler, params EventType[] eventType)这个对外提供的注册监听的接口入手,逐步的分析一下它的工作流程: 最后我们再来看一下具体的监听者应该如何实现IEventHandler接口,以 ColaFrameWork框架 中的UI基类——UIBase举例,在UIBase内部维护了一个Dictionary 为了使用更加简洁方便,我们还可以再封装一些函数出来,以便随时注册一个消息和取消注册一个消息,主要是RegisterEvent和UnRegisterEvent接口,代码如下: 关于C#版的事件分发机制大概就介绍到这里了,马三在这里只是大概地讲了下思路,更细致的原理和使用方法大家可以去马三的 ColaFrameWork框架 中找一下相关代码。 Lua版本的事件分发机制相对C#版的来说就简单了很多,Lua中没有接口的概念,因此实现方式和C#版的也大有不同,不过总的来说还是对外暴露出以下几个接口: 照例还是先上一下核心代码EventMgr.lua,然后再逐步解释: 在实际使用的时候主要是调用 RegisterEvent、UnRegisterEvent 和 DispatchEvent这三个接口。RegisterEvent用来注册一个事件,UnRegisterEvent 用来反注册一个事件,DispatchEvent用来派发事件。先从RegisterEvent接口说起,它需要传入3个参数,分别是ModuleId,EventId和回调函数func。ModuleId就是我们不同模块的id,他是一个模块的唯一标识,在实际应用中我们可以定义一个全局的枚举来标识这些模块ID。EventId是不同的消息的标识,它也是数字类型的枚举值,并且因为有了模块ID的存在,不同模块可以使用相同的EventId,这并不会导致消息的冲突。在RegisterEvent内部操作中,我们首先对ModuleId进行了左移16位的操作,然后再加上EventID组成我们的消息key,左移16位可以避免ModuleID直接与EventId组合后会产生Key冲突的问题,一般来说左移16位已经可以满足定义很多模块和事件id的需求了。然后调用 self:AddEventListener(key, func, nil) 方法,将计算出来的key和回调函数进行注册。在EventMgr的内部其实还是维护了一个监听者列表,注册消息的时候,就是把回调和参数添加到监听者列表中。反注册消息就是把对应key的回调从监听者列表中移除。派发事件的时候就是遍历key所对应的监听者列表,然后依次执行里面的回调函数。好了,接着说AddEventListener这个函数的操作,它首先会去获取key对应的监听者列表,结构为{func,para},如果没有就新建一个table,并把它保存为key所对应的监听者列表。得到这个监听者列表以后,我们首先会对其进行遍历,如果里面已经包含func回调函数的话,就直接return掉,过滤掉已经注册过的消息,防止重复注册。如果通过了上一步检查的话,就执行 table.insert(listeners, { f = func, p = param })操作,加入监听者的回调和参数。对于UnRegisterEvent方法,我们依然会计算出key,然后调用 RemoveEventListener 操作,把监听者从监听者列表中移除。在使用DispatchEvent接口进行事件派发的时候,我们依然会先计算出Key,然后取出key对应的监听者列表。接着依次遍历这些监听者,然后执行其中保存着的回调函数,并且把需要传递的事件参数传递进去。具体的使用方法,可以参考下面的Main.lua: 支持含参数事件分发和无参数事件分发,上面代码的执行结果如下,可以发现成功地监听了注册的消息,并且也获取到了传递过来的参数: 图1:代码执行结果 通过本篇博客,马三和大家一起学习了如何在Unity中使用C#和Lua分别实现事件分发机制,希望本篇博客能为大家的工作过程中带来一些帮助与启发。 本篇博客中的样例工程已经同步至Github:https://github.com/XINCGer/Unity3DTraining/tree/master/lua/LuaEventMgr,欢迎大家Fork! 马三的开源Unity客户端框架 ColaFramework框架:https://github.com/XINCGer/ColaFrameWork 如果觉得本篇博客对您有帮助,可以扫码小小地鼓励下马三,马三会写出更多的好文章,支持微信和支付宝哟! 作者:马三小伙儿 【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher 标签:接口 学习 过程 nbsp 服务器 pat 框架 namespace 分析 原文地址:https://www.cnblogs.com/msxh/p/9539231.html一、简介
二、C#版的事件分发机制
1 using System.Collections;
2 using System.Collections.Generic;
3 using EventType = ColaFrame.EventType;
4
5 ///
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using UnityEngine;
5 using EventType = ColaFrame.EventType;
6
7 ///
1 ///
1 ///
三、Lua版的事件分发机制
1 require("Class")
2 local bit = require "bit"
3
4 EventMgr = {
5 --实例对象
6 _instance = nil,
7 --观察者列表
8 _listeners = nil
9 }
10 EventMgr.__index = EventMgr
11 setmetatable(EventMgr, Class)
12
13 -- 构造器
14 function EventMgr:new()
15 local t = {}
16 t = Class:new()
17 setmetatable(t, EventMgr)
18 return t
19 end
20
21 -- 获取单例接口
22 function EventMgr:Instance()
23 if EventMgr._instance == nil then
24 EventMgr._instance = EventMgr:new()
25 EventMgr._listeners = {}
26 end
27 return EventMgr._instance
28 end
29
30 function EventMgr:RegisterEvent(moduleId, eventId, func)
31 local key = bit.lshift(moduleId, 16) + eventId
32 self:AddEventListener(key, func, nil)
33 end
34
35 function EventMgr:UnRegisterEvent(moduleId, eventId, func)
36 local key = bit.lshift(moduleId, 16) + eventId
37 self:RemoveEventListener(key, func)
38 end
39
40 function EventMgr:DispatchEvent(moduleId, eventId, param)
41 local key = bit.lshift(moduleId, 16) + eventId
42 local listeners = self._listeners[key]
43 if nil == listeners then
44 return
45 end
46 for _, v in ipairs(listeners) do
47 if v.p then
48 v.f(v.p, param)
49 else
50 v.f(param)
51 end
52 end
53 end
54
55 function EventMgr:AddEventListener(eventId, func, param)
56 local listeners = self._listeners[eventId]
57 -- 获取key对应的监听者列表,结构为{func,para},如果没有就新建
58 if listeners == nil then
59 listeners = {}
60 self._listeners[eventId] = listeners -- 保存监听者
61 end
62 --过滤掉已经注册过的消息,防止重复注册
63 for _, v in pairs(listeners) do
64 if (v and v.f == func) then
65 return
66 end
67 end
68 --if func == nil then
69 -- print("func is nil!")
70 --end
71 --加入监听者的回调和参数
72 table.insert(listeners, { f = func, p = param })
73 end
74
75 function EventMgr:RemoveEventListener(eventId, func)
76 local listeners = self._listeners[eventId]
77 if nil == listeners then
78 return
79 end
80 for k, v in pairs(listeners) do
81 if (v and v.f == func) then
82 table.remove(listeners, k)
83 return
84 end
85 end
86 end
1 require("EventMgr")
2
3 local function TestCallback_1()
4 print("Callback_1")
5 end
6
7 local function TestCallback_2(param)
8 print("Callback_2")
9 print(param.id)
10 print(param.pwd)
11 end
12
13 local EventMgr = EventMgr:Instance()
14 EventMgr:RegisterEvent(1, 1, TestCallback_1)
15 EventMgr:RegisterEvent(2, 1, TestCallback_2)
16 EventMgr:DispatchEvent(1, 1)
17 EventMgr:DispatchEvent(2, 1, { id = "abc", pwd = "123" })
四、总结
出处:https://www.cnblogs.com/msxh/p/9539231.html
请尊重别人的劳动成果,让分享成为一种美德,欢迎转载。另外,文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!
上一篇:Python语言特性
文章标题:【Unity游戏开发】用C#和Lua实现Unity中的事件分发机制EventDispatcher
文章链接:http://soscw.com/index.php/essay/103392.html