ObjectEvents学习笔记

ObjectEvents

请大家关注一下我的微博 @NormanLin_BadPixel坏像素


前面几段是单例的写法,不讲。

public Assembly HotfixAssembly;

这里不懂Assembly的可以先去了解一下C#的反射技术。这里简要介绍一下

通俗的来讲,就是反射是通过一个对象A去了解另一个对象B的内部结构和信息,即使在你不知道那个对象B存在的情况下。

Assembly:表示一个程序集,它是一个可重用、无版本冲突并且可自我描述的公共语言运行时应用程序构建基块。

这是有关热更新的程序集,我们现在不学热更新,先放着。但是对Assembly得了解。

private readonly Dictionary

IObjectEvent

public interface IObjectEvent
    {
        Type Type();
        void Set(object value);
    }

哎呦这接口啥用呦,一个返回Type的方法,一个参数是object的Set方法。看起来像是有点特殊的Get/Set方法。留着之后再研究吧。回到ObjectEvents

ObjectEvents

private EQueue<Disposer> updates = new EQueue<Disposer>();

Equeue

进去看了下,发现跟普通的Queue没什么区别,不知道为什么用这个,知道的大佬可以告知一声。

Disposer

前面的几个都是特性的定义,都是关于Bson序列化的。
1. 如果值等于默认值则忽略
2. 默认值是 1L
3. 是一个Bson元素
4. 是索引

Disposer的意思是处理者。应该是处理各种事件的抽象类。
在构造方法里把自己加入ObjectEvents里面。我们看一下ObjectEvents里面的 Add(Disposer) 方法。

ObjectEvents

public void Add(Disposer disposer)
        {
            IObjectEvent objectEvent;
            if (!this.disposerEvents.TryGetValue(disposer.GetType(), out objectEvent))
            {
                return;
            }

            if (objectEvent is ILoad)
            {
                this.loaders.Enqueue(disposer);
            }

            if (objectEvent is IUpdate)
            {
                this.updates.Enqueue(disposer);
            }

            if (objectEvent is IStart)
            {
                this.starts.Enqueue(disposer);
            }
        }

看到这,大家思考一下大概就能猜到了,这里只是自己管理了一套Unity里面的Start/Update还有一个Load。当实例化一个Disposer的时候,首先会判断一下是否有已经注册的该事件类型,如果有,则根据事件类型把自己压入对应的队列中。这里需要注意的是,ILoad/IUpdate/IStart都是接口,这里objectEvent is ILoad 是判断是否实现了ILoad接口。如何注册事件类型,这我们在之后会说到的。我们现在再继续来看Disposer。

Disposer

这里实现了一个接口 IDisposable
我去看了一下IDisposavle,发现有一个特性[ComVisible(true)],顺便百度了一下,原来:

COM = Component Object Model,微软的上一代编程模型
[ComVisible(true/false)]控制程序集中个别托管类型、成员或所有类型对 COM 的可访问性。

但是,它默认就是true。。

好了,看Disposer里面怎么实现这个接口的吧。默认是把Id设为0,然后调用ObjectPool里面的Recycle方法,把自己传进去。我们来看一下ObjectPool

ObjectPool

很好猜到这是一个对象池单例,Recycle应该是资源回收,也就是把这个处理器对象回收。

进去看一下代码,哦吼还真是。如果对对象池这个概念不理解的,这里不想说,百度一下吧,很简单好好理解的。

好,知道Disposer是什么了,我们回到ObjectEvents

ObjectEvents

我们看到,每个update/lateUpdate/loader都有两个队列。等等,两个干嘛?而且名字直接就是updates2。。。不是很优雅啊。我去看了一下ET源码,发现根本就没有ObjectEvents这个类。。。原来这是LandlordsCore的作者自己加进去的。可能是为了做统一管理游戏流程所以加了这个吧,很不错,我们也学习一下。

Close 是关闭的方法,略过。

public void Add(string name, Assembly assembly)

OK,看到了之前Init里面调用的方法了。我们一句一句来看它干了什么。

this.assemblies[name] = assembly;
foreach (Assembly ass in this.assemblies.Values)
            {
                foreach (Type type in ass.GetTypes())
                {
                    object[] attrs = type.GetCustomAttributes(typeof(ObjectEventAttribute), false);

                    if (attrs.Length == 0)
                    {
                        continue;
                    }

                    object obj = Activator.CreateInstance(type);
                    IObjectEvent objectEvent = obj as IObjectEvent;
                    if (objectEvent == null)
                    {
                        Log.Error($"组件事件没有继承IObjectEvent: {type.Name}");
                        continue;
                    }
                    this.disposerEvents[objectEvent.Type()] = objectEvent;
                }
            }

在储存程序集的字典里放入这个name的程序集,我们可以理解为不同模块的程序集。
然后,遍历字典里面存放的所有程序集。
这里我有疑惑,我们每次Add的是一个模块的程序集,其他模块的程序集没有变,为什么要对其他模块的程序集再做处理?我之后会试试不遍历所有模块会不会有影响。这里先按照作者的思路走吧。

首先遍历所有模块的程序集,再遍历单个模块程序集里的所有类。再获取这个类的所有指定自定义特性ObjectEventAttribute。这里用到了大量C#反射的知识,和我一样对反射了解不是很深的同学可以跟我一起百度一下了。浅析C#中的Attribute

ObjectEventAttribute只是一个标识ObjectEvent的特性。

attrs这个变量只是用来判断这个类是否有ObjectEvent特性,无则跳过,有则继续。

然后通过 Activator.CreateInstance(type) 来实例化一个Type类型的实例,再尝试从这个实例获取IObjectEvent接口,如果有,则注册事件。

继续往下读,Get和GetAll就不说了。Add之前讲过了。

public void Awake(Disposer disposer)

激活传入的处理器。首先获取处理器的类型来确定事件的类型,再从已经注册的事件列表里找是否已经注册该类型的事件,如果有,则设置这个事件的处理器,并且执行Awake方法。后面的几个Awake只是多了几个参数的激活方法。

private void Start()

跟Awake很相似,是执行starts队列里的事件处理器。

public void Load()
public void Update()
public void LateUpdate()

这三个很相似,分别是调用Load/Update/LateUpdate队列里面的事件处理器。需要主意的是,这些事件都有两个队列,在一次执行的时候会把队列1里面符合条件的处理器存入队列2。当全部执行完的时候,队列1是空的,这个时候会把队列1跟队列2互换。不是很清楚这样写是为了什么。可能是用来多次执行的,比如Update,如果在执行方法里面没有把Id设为0,那它就会出现在下一次执行的队列1里面继续执行。(我猜的)


至此,ObjectEvents讲完了。大家应该还有很多不理解的地方,比如我在什么时候注册了什么事件,又在哪个地方调用了这些方法。不急,我们先大概了解了这个ObjectEvents是干什么的,后面遇到它的应用理解起来就快了。这些问题后面肯定是会解决的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值