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()方法了,达到驱动实体生命周期函数的目的。

猜你喜欢

转载自blog.csdn.net/u013404885/article/details/131492879