上次我们说到了第五个单例,这次继续阅读源码。
第六个单例是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()方法了,达到驱动实体生命周期函数的目的。