框架学习(3)"/>
ET 7.2框架学习(3)
上次我们说到了第五个单例,这次继续阅读源码。
第六个单例是EventSystem,事件系统,这个单例在ET系统中非常重要。
先来看看作者怎么讲的,打开et目录下的Book目录,找到3.4事件机制EventSystem.md这篇文章。
其大意就是一个系统需要关注事件A,那么先向系统申明说我关注了事件A,当事件A发生时告诉我,然后我来自己做关于事件A的处理。那么这个机制是如何实现的呢,作者利用了C#的反射机制。
在EventSystem.cs这个代码文件中,“先向系统申明说我关注了事件A”,这个的实现在以下函数中
public void Add(Dictionary<string, Type> addTypes)
我们先来看看是哪里调用了这个函数,通过打断点运行的方式,可以看到在CodeLoader这个单例中的Start()方法调用了这个函数(代码模式和非代码模式都会调用到,这里看一个就行了,意思是一样的)
// 获取所有程序集中的所有类型
Dictionary<string, Type> types = AssemblyHelper.GetAssemblyTypes(assemblies);// 将类型添加到事件系统中
EventSystem.Instance.Add(types);
可以看到他把程序中所有定义的类型都取出来了,然后传入了Add()函数。
Add()函数中一共做了以下几件事:
1、把程序中的所有类型都保存到了allTypes中。
2、建立了BaseAttribute特性子类的类型到非特性类型的关系表types。
这点看起来有点绕,举个例子吧,我们可以在ET中找到以下类:
namespace ET.Client
{[Event(SceneType.Client)]public class AfterCreateClientScene_AddComponent: AEvent<EventType.AfterCreateClientScene>{protected override async ETTask Run(Scene scene, EventType.AfterCreateClientScene args){scene.AddComponent<UIEventComponent>();scene.AddComponent<UIComponent>();scene.AddComponent<ResourcesLoaderComponent>();await ETTask.CompletedTask;}}
}
这里面的[Event(SceneType.Client)],表明这个类具有EventAttribute特性,而EventAttribute特性又继承于BaseAttribute
using System;namespace ET
{[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]public class EventAttribute: BaseAttribute{public SceneType SceneType { get; }public EventAttribute(SceneType sceneType){this.SceneType = sceneType;}}
}
所有上面的types关系表中会有一条EventAttribute到AfterCreateClientScene_AddComponent的映射关系。
3、创建所有同时具有ObjectSystemAttribute特性和继承自ISystemType接口的类,并且建立映射关系到typeSystems中。
我们先来看一下继承自ISystemType接口的有哪些:
可以看到只要我们的类继承自上图中的任意接口且具有ObjectSystemAttribute特性就要加入这个映射关系中。
再举个实例:
在SessionAcceptTimeoutComponentSystem.cs这个文件中,有SessionAcceptTimeoutComponentAwakeSystem和SessionAcceptTimeoutComponentDestroySystem这两个类,他们都用[ObjectSystem]标记为具有ObjectSystemAttribute特性,且一个继承于AwakeSystem,一个继承于DestroySystem,这个两个System分别继承于IAwakeSystem接口和IDestroySystem接口,满足上述条件,加入映射容器typeSystems中。
using System;namespace ET
{[Invoke(TimerInvokeType.SessionAcceptTimeout)]public class SessionAcceptTimeout: ATimer<SessionAcceptTimeoutComponent>{protected override void Run(SessionAcceptTimeoutComponent self){try{self.Parent.Dispose();}catch (Exception e){Log.Error($"move timer error: {self.Id}\n{e}");}}}[ObjectSystem]public class SessionAcceptTimeoutComponentAwakeSystem: AwakeSystem<SessionAcceptTimeoutComponent>{protected override void Awake(SessionAcceptTimeoutComponent self){self.Timer = TimerComponent.Instance.NewOnceTimer(TimeHelper.ServerNow() + 5000, TimerInvokeType.SessionAcceptTimeout, self);}}[ObjectSystem]public class SessionAcceptTimeoutComponentDestroySystem: DestroySystem<SessionAcceptTimeoutComponent>{protected override void Destroy(SessionAcceptTimeoutComponent self){TimerComponent.Instance.Remove(ref self.Timer);}}
}
我们可以看到上面的写法是对SessionAcceptTimeoutComponent类的一种成员函数扩充方式,同时AwakeSystem和DestroySystem是一个模板类,这里都传入了SessionAcceptTimeoutComponent类型,这个类型会在ISystemType的成员函数Type()返回
namespace ET
{public interface ISystemType{Type Type();Type SystemType();InstanceQueueIndex GetInstanceQueueIndex();}
}
这个的用处是在建立typeSystems容器的映射关系时使用的,其作为了其键值,然后索引了一个系统类型和对象的键值对,图示如下:
4、创建所有具有EventAttribute特性的类,并且建立事件类型到事件类对象的映射关系到allEvents中。
举个实例:
有一个事件类型为ChangePosition:
using Unity.Mathematics;namespace ET
{namespace EventType{public struct ChangePosition{public Unit Unit;public float3 OldPos;}public struct ChangeRotation{public Unit Unit;}}
}
有两个类使用到了这个事件类型
using Unity.Mathematics;namespace ET.Server
{[Event(SceneType.Map)]public class ChangePosition_NotifyAOI: AEvent<ET.EventType.ChangePosition>{protected override async ETTask Run(Scene scene, ET.EventType.ChangePosition args){Unit unit = args.Unit;float3 oldPos = args.OldPos;int oldCellX = (int) (oldPos.x * 1000) / AOIManagerComponent.CellSize;int oldCellY = (int) (oldPos.z * 1000) / AOIManagerComponent.CellSize;int newCellX = (int) (unit.Position.x * 1000) / AOIManagerComponent.CellSize;int newCellY = (int) (unit.Position.z * 1000) / AOIManagerComponent.CellSize;if (oldCellX == newCellX && oldCellY == newCellY){return;}AOIEntity aoiEntity = unit.GetComponent<AOIEntity>();if (aoiEntity == null){return;}unit.DomainScene().GetComponent<AOIManagerComponent>().Move(aoiEntity, newCellX, newCellY);await ETTask.CompletedTask;}}
}
using UnityEngine;namespace ET.Client
{[Event(SceneType.Current)]public class ChangePosition_SyncGameObjectPos: AEvent<EventType.ChangePosition>{protected override async ETTask Run(Scene scene, EventType.ChangePosition args){Unit unit = args.Unit;GameObjectComponent gameObjectComponent = unit.GetComponent<GameObjectComponent>();if (gameObjectComponent == null){return;}Transform transform = gameObjectComponent.GameObject.transform;transform.position = unit.Position;await ETTask.CompletedTask;}}
}
这两个事件类都具有EventAttribute特性,所以最终allEvents中会有一条ChangePosition到这两个类对象的映射关系。
5、创建所有具有InvokeAttribute特性的类,并且建立回调类型到回调类对象的映射关系到allEvents中。
举个例子:
namespace ET
{[FriendOf(typeof(MoveComponent))]public static class MoveComponentSystem{[Invoke(TimerInvokeType.MoveTimer)]public class MoveTimer: ATimer<MoveComponent>{protected override void Run(MoveComponent self){try{self.MoveForward(true);}catch (Exception e){Log.Error($"move timer error: {self.Id}\n{e}");}}}...}
}
上面有个MoveTimer类,其拥有InvokeAttribute特性,其继承于ATimer抽象类
namespace ET
{public abstract class ATimer<T>: AInvokeHandler<TimerCallback> where T: class{public override void Handle(TimerCallback a){this.Run(a.Args as T);}protected abstract void Run(T t);}
}
而ATimer又继承于AInvokeHandler抽象类,AInvokeHandler又继承于IInvoke接口
using System;namespace ET
{public interface IInvoke{Type Type { get; }}public abstract class AInvokeHandler<A>: IInvoke where A: struct{public Type Type{get{return typeof (A);}}public abstract void Handle(A a);}public abstract class AInvokeHandler<A, T>: IInvoke where A: struct{public Type Type{get{return typeof (A);}}public abstract T Handle(A a);}
}
我们看到有个
public Type Type{get{return typeof (A);}}
的方法,这就是返回类型的,而这个A就是TimerCallback类型,故建立了一个从TimerCallback到MoveTimer的映射关系。这样我们就可以向系统发出一个事件,然后所有关注这个事件的处理函数进行响应处理,看触发TimerCallback事件的函数
private void Run(TimerAction timerAction){switch (timerAction.TimerClass){case TimerClass.OnceTimer:{EventSystem.Instance.Invoke(timerAction.Type, new TimerCallback() { Args = timerAction.Object });timerAction.Recycle();break;}case TimerClass.OnceWaitTimer:{ETTask tcs = timerAction.Object as ETTask;tcs.SetResult();timerAction.Recycle();break;}case TimerClass.RepeatedTimer:{ long timeNow = GetNow();timerAction.StartTime = timeNow;this.AddTimer(timerAction);EventSystem.Instance.Invoke(timerAction.Type, new TimerCallback() { Args = timerAction.Object });break;}}}
回顾上面的内容,我们发现这个函数主要就是创建了一批对象,做了一些特性和类(对象)的映射工作。
我们接着看一些比较重要的成员函数。
// 注册系统public void RegisterSystem(Entity component){Type type = component.GetType();OneTypeSystems oneTypeSystems = this.typeSystems.GetOneTypeSystems(type);if (oneTypeSystems == null){return;}for (int i = 0; i < oneTypeSystems.QueueFlag.Length; ++i){if (!oneTypeSystems.QueueFlag[i]){continue;}this.queues[i].Enqueue(component.InstanceId);}}
这个会在Entity.cs进行注册时调用,当注册一个实体(组件也是实体)时,会将这个实体的InstanceId放到queues队列数组中,queues队列数组的长度为InstanceQueueIndex.Max
public enum InstanceQueueIndex{None = -1,Update,LateUpdate,Load,Max,}
这样如果我的实体的System类有UpdateSystem、LateUpdateSystem、LoadSystem等,同时有ObjectSystem特性,就会加入到上述的队列中,然后EventSystem本身就是继承于ISingletonUpdate和ISingletonLateUpdate的,这样可以在Update()函数和LateUpdate()中遍历上述的队列,调用实体的Update()和LateUpdate()方法了,达到驱动实体生命周期函数的目的。
更多推荐
ET 7.2框架学习(3)
发布评论