ET介绍——事件机制EventSystem
Posted Flamesky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ET介绍——事件机制EventSystem相关的知识,希望对你有一定的参考价值。
事件机制EventSystem
ECS最重要的特性一是数据跟逻辑分离,二是数据驱动逻辑。什么是数据驱动逻辑呢?不太好理解,我们举个例子 一个moba游戏,英雄都有血条,血条会在人物头上显示,也会在左上方头像UI上显示。这时候服务端发来一个扣血消息。我们怎么处理这个消息?第一种方法,在消息处理函数中修改英雄的血数值,修改头像上血条显示,同时修改头像UI的血条。这种方式很明显造成了模块间的耦合。第二种方法,扣血消息处理函数中只是改变血值,血值的改变抛出一个hpchange的事件,人物头像模块跟UI模块都订阅血值改变事件,在订阅的方法中分别处理自己的逻辑,这样各个模块负责自己的逻辑,没有耦合。 ET提供了多种事件,事件都是可以多次订阅的:
- AwakeSystem,组件工厂创建组件后抛出,只抛出一次,可以带参数
Player player = ComponentFactory.Create<Player>(); // 订阅Player的Awake事件 public class PlayerAwakeSystem: AwakeSystem<Player> public override void Awake(Player self)
- StartSystem,组件UpdateSystem调用前抛出
// 订阅Player的Start事件 public class PlayerStartSystem: StartSystem<Player> public override void Start(Player self)
- UpdateSystem,组件每帧抛出
// 订阅Player的Update事件 public class PlayerUpdateSystem: UpdateSystem<Player> public override void Update(Player self)
- DestroySystem,组件删除时抛出
// 订阅Player的Destroy事件 public class PlayerDestroySystem: DestroySystem<Player> public override void Destroy(Player self) Player player = ComponentFactory.Create<Player>(); // 这里会触发Destroy事件 player.Dispose();
- ChangeSystem,组件内容改变时抛出,需要开发者手动触发
// 订阅Player的Destroy事件 public class PlayerChangeSystem: ChangeSystem<Player> public override void Change(Player self) Player player = ComponentFactory.Create<Player>(); // 需要手动触发ChangeSystem Game.EventSystem.Change(player);
- DeserializeSystem,组件反序列化之后抛出
// 订阅Player的Deserialize事件 public class PlayerDeserializeSystem: DeserializeSystem<Player> public override void Deserialize(Player self) // 这里player2会触发Deserialize事件 Player player2 = MongoHelper.FromBson<Player>(player.ToBson());
- LoadSystem,EventSystem加载dll时抛出,用于服务端热更新,重新加载dll做一些处理,比如重新注册handler
// 订阅Player的Load事件 public class PlayerLoadSystem: LoadSystem<Player> public override void Load(Player self)
- 普通的Event,由开发者自己抛出,可以最多带三个参数。另外客户端热更层也可以订阅mono层的Event事件
int oldhp = 10; int newhp = 5; // 抛出hp改变事件 Game.EventSystem.Run("HpChange", oldhp, newhp); // UI订阅hp改变事件 [Event("HpChange")] public class HpChange_ShowUI: AEvent<int, int> public override void Run(int a, int b) throw new NotImplementedException(); // 模型头顶血条模块也订阅hp改变事件 [Event("HpChange")] public class HpChange_ModelHeadChange: AEvent<int, int> public override void Run(int a, int b) throw new NotImplementedException();
- 除此之外还有很多事件,例如消息事件。消息事件使用MessageHandler来声明,可以带参数指定哪种服务器需要订阅。
[MessageHandler(AppType.Gate)] public class C2G_LoginGateHandler : AMRpcHandler<C2G_LoginGate, G2C_LoginGate> protected override void Run(Session session, C2G_LoginGate message, Action<G2C_LoginGate> reply) G2C_LoginGate response = new G2C_LoginGate(); reply(response);
更具体的消息事件等到讲消息的时候再细细讲解了
10. 数值事件,数值模块再讲解
......, 更多的事件由自己去开发。
ET框架的逻辑就是由以上各种事件来驱动的。
ET开源地址地址:egametang/ET: Unity3D Client And C# Server Framework (github.com) qq群:474643097
UGUI 源码解读-EventSystem
EventSystem事件系统的源码目录结构如下:大致可分为EventData、InputModules、Raycasters、EventSystem、ExecuteEvents。
EventSystem的职责:
- 管理和处理输入事件(InputModule)
- 调用Raycaster发起射线检测,获取输入事件投射到的物体
- 将事件发送给投射物体处理。
每个场景一般有且只有一个EventSystem,EventSystem上一般会挂一个InputModule模块。
EventData
- BaseEventData:基础的事件信息,持有EventSystem
-
PointerEventData: 继承于BaseEventData,存储 触摸/点击/鼠标操作 事件信息,部分信息如下:
-
AxisEventData:继承于BaseEventData,移动相关的事件信息。
InputModules
-
BaseInput, 封装了触摸和鼠标的相关事件获取。
-
BaseInputModule ,所有InputModule的基类,抽象函数
public abstract void Process();
,持有EventSystem,EventData,BaseInput。OnEnable中调用EventSystem#UpdateModules注册输入模块。
EventSystem的Update函数中,先通过TickModules调用InputModule的UpdateModule函数,然后调用Process函数分发事件。
-
PointerInputModule,继承于BaseInputModule,封装了一些工具函数,比如
GetTouchPointerEventData
用于将Touch转为EventData,GetMousePointerEventData
用于将鼠标点击事件转为MouseState(MouseState存放着三个鼠标按钮的事件数据)。 -
StandaloneInputModule,继承于PointerInputModule,Process函数中,先调用ProcessTouchEvents处理触摸事件,如果没有触摸事件则调用ProcessMouseEvent处理鼠标事件。
Raycasters
在事件系统中充当捕获物体的角色,管理射线,为InputModule提供GameObject
-
BaseRaycaster, 所有Raycaster的基类,OnEnable中向RaycasterManager注册,
Raycast
函数通过PointerEventData
获取射线结果List<RaycastResult>
。public abstract void Raycast(PointerEventData eventData, List<RaycastResult> resultAppendList);
-
GraphicRaycaster, 见名知意,用于获取挂有Graphic脚本的GameObject,Graphic是UGUI比较重要的概念,Image、RawImage、Text等凡是需要Mesh渲染的UI都必须继承于它。
被射线检测到的结果会包装成RaycastResult返回:
什么时候会发起射线检测?
EventSystem中有一个RaycastAll函数,依次调用Raycast
函数发起射线检测。
在BaseInputModule的GetTouchPointerEventData
和GetMousePointerEventData
函数中通过eventSystem.RaycastAll开始射线检测。
protected PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released)
PointerEventData pointerData;
var created = GetPointerData(input.fingerId, out pointerData, true);
//....省略部分代码
if (input.phase == TouchPhase.Canceled)
pointerData.pointerCurrentRaycast = new RaycastResult();
else
//发起射线检测
eventSystem.RaycastAll(pointerData, m_RaycastResultCache);
var raycast = FindFirstRaycast(m_RaycastResultCache);
pointerData.pointerCurrentRaycast = raycast;
m_RaycastResultCache.Clear();
return pointerData;
protected virtual MouseState GetMousePointerEventData(int id)
// Populate the left button...
PointerEventData leftData;
var created = GetPointerData(kMouseLeftId, out leftData, true);
//....省略部分代码
//发起射线检测
eventSystem.RaycastAll(leftData, m_RaycastResultCache);
var raycast = FindFirstRaycast(m_RaycastResultCache);
leftData.pointerCurrentRaycast = raycast;
m_RaycastResultCache.Clear();
//....省略部分代码
return m_MouseState;
EventSystemHandler
被射线检测到的GameObject不一定直接参与事件处理,一般会检测当前物体或者其父物体上的脚本是否实现了IEventSystemHandler接口。
public interface IPointerEnterHandler : IEventSystemHandler
/// <summary>
/// Use this callback to detect pointer enter events
/// </summary>
void OnPointerEnter(PointerEventData eventData);
接口主要分为三大类:
- IPointerXXXHandler : 处理鼠标点击和触屏事件
- IDragXXXXHandler:处理拖拽事件
- IXXXHandler:处理其他如选择、取消等事件
ExecuteEvents
事件执行器,封装了事件执行相关的逻辑。
-
GetEventHandler ,用于获取实现了对应接口的GameObject
-
Execute, 获取物体身上的所有IEventSystemHandler脚本,依次执行事件函数
- ExecuteHierarchy,逐节点寻找可执行的物体(含可用的IEventSystemHandler),触发(Execute)即停止逐根搜索。
详细执行过程可看ProcessTouchEvents和ProcessMouseEvent两个函数的逻辑。
以上是关于ET介绍——事件机制EventSystem的主要内容,如果未能解决你的问题,请参考以下文章