Unity 游戏框架搭建 2019 (四十七) 集成到 MonoBehaviourSimplify
还记得我们的简易消息机制是为了解决什么问题诞生的嘛?
是为了解决脚本间访问的问题。
我们回过头再看下 A 脚本如果想访问 B 脚本,使用消息机制,如何实现。
代码如下:
public class A : MonoBehaviour
{void Update() {if(Input.GetMouseButtonDown(0)){MsgDispatcher.Send("DO","ok");}}
}public class B : MonoBehaviour
{void Awake(){MsgDispatcher.Register("DO",DoSomething);}void DoSomething(object data){// do something}void OnDestroy(){MsgDispatcher.UnRegiter("DO",DoSomething);}
}
用法还是很简单的。
不过假如我们的 B 脚本注册了非常多的消息,代码会变成如下:
public class B : MonoBehaviour
{void Awake(){MsgDispatcher.Register("DO",DoSomething);MsgDispatcher.Register("DO1",MsgReceiver);MsgDispatcher.Register("DO2",MsgReceiver1);MsgDispatcher.Register("DO3",MsgReceiver2);}void DoSomething(object data){// do something}...void OnDestroy(){MsgDispatcher.UnRegiter("DO",DoSomething);MsgDispatcher.UnRegiter("DO1",MsgReceiver);MsgDispatcher.UnRegiter("DO2",MsgReceiver1);MsgDispatcher.UnRegiter("DO3",MsgReceiver2);}
}
每次注册一个消息,对应地,在 OnDestroy 操作的时候就要注销一个事件。这个非常像我们写 C++ 的时候遵循的一个内存管理法则,每次申请内存就要在析构方法里进行释放。
而这样使用消息机制,初学者非常容易忘记消息的注销,从而导致引用异常等等。
那么如何解决呢?
用一个 Dictionary 记录这个脚本中已经注册过的消息,以及消息名对应的回调。
代码如下:
public class B : MonoBehaviour
{Dictionary<string,Action<object>> mMsgRegisterRecorder = new Dictionary<string,Action<object>>();void Awake(){MsgDispatcher.Register("DO",DoSomething);mMsgRegisterRecorder.Add("DO",DoSomething);MsgDispatcher.Register("DO1",MsgReceiver);mMsgRegisterRecorder.Add("DO1",MsgReceiver);MsgDispatcher.Register("DO2",MsgReceiver1);mMsgRegisterRecorder.Add("DO2",MsgReceiver1);MsgDispatcher.Register("DO3",MsgReceiver2);mMsgRegisterRecorder.Add("DO3",MsgReceiver2);}void DoSomething(object data){// do something}...void OnDestroy(){foreach (var keyValuePair in mMsgRegisterRecorder){MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); }mMsgRegisterRecorder.Clear();}
}
这样,不管注册了多少个消息,只要在 OnDestroy 的时候, 进行一个遍历,这样消息就全部注销掉了。
但是这样写的话注册,就变得麻烦了,每次注册要先两行代码。
MsgDispatcher.Register("DO3",MsgReceiver2);
mMsgRegisterRecorder.Add("DO3",MsgReceiver2);
把两行提取成一个方法就好了。
提取的方法,代码如下:
private void RegisterMsg(string msgName, Action<object> onMsgReceived)
{MsgDispatcher.Register(msgName, onMsgReceived);mMsgRegisterRecorder.Add(msgName, onMsgReceived);
}
而注册消息的代码就会变成如下:
private void Awake()
{RegisterMsg("Do",DoSomething);RegisterMsg("DO1",MsgReceiver);RegisterMsg("DO2", _=>{ });RegisterMsg("DO3", _=>{ });
}
是不是精简了很多,而且也可以注册 Lambda 表达式了。
不过我们看下现在的 B 脚本全部代码:
public class B : MonoBehaviour{Dictionary<string, Action<object>> mMsgRegisterRecorder = new Dictionary<string, Action<object>>();private void Awake(){RegisterMsg("Do",DoSomething);RegisterMsg("DO1",_=>{ });RegisterMsg("DO2", _=>{ });RegisterMsg("DO3", _=>{ });}private void RegisterMsg(string msgName, Action<object> onMsgReceived){MsgDispatcher.Register(msgName, onMsgReceived);mMsgRegisterRecorder.Add(msgName, onMsgReceived);}void DoSomething(object data){// do something}private void OnDestroy(){foreach (var keyValuePair in mMsgRegisterRecorder){MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); }mMsgRegisterRecorder.Clear();}}
目前,每个要使用相同消息策略的脚本,都实现如上的代码,会产生很多的重复代码。所以这里我们要开始考虑如何让这个消息注册/注销的策略进行复用。首先用静态方法是不可能了,因为这个策略是有状态的(成员变量)。所以以我们目前掌握的知识来看,只能用继承的方式了。
继承也有两种,一种是继承一个新类,另一种是继承到 MonoBehaviourSimplify 里。
笔者选择后者,这样我们的脚本只要继承 MonoBehaviourSimplify 就会获得 API 简化和消息功能了,一举多得,而且很方便。
集成后的代码,也就是第十三个示例的代码如下:
using System;
using System.Collections.Generic;namespace QFramework
{public abstract partial class MonoBehaviourSimplify{Dictionary<string, Action<object>> mMsgRegisterRecorder = new Dictionary<string, Action<object>>();protected void RegisterMsg(string msgName, Action<object> onMsgReceived){MsgDispatcher.Register(msgName, onMsgReceived);mMsgRegisterRecorder.Add(msgName, onMsgReceived);}private void OnDestroy(){OnBeforeDestroy();foreach (var keyValuePair in mMsgRegisterRecorder){MsgDispatcher.UnRegister(keyValuePair.Key,keyValuePair.Value); }mMsgRegisterRecorder.Clear();}protected abstract void OnBeforeDestroy();}public class B : MonoBehaviourSimplify{private void Awake(){RegisterMsg("Do", DoSomething);RegisterMsg("DO1", _ => { });RegisterMsg("DO2", _ => { });RegisterMsg("DO3", _ => { });}void DoSomething(object data){// do something}protected override void OnBeforeDestroy(){}}
}
在以上代码里,笔者把 MonoBehaviourSimplify 添加了 abstract 关键字,这样用户在使用 MonoBehaviourSimplify 的时候就不能自己创建出来实例了。
而又添加了如下抽象方法:
protected abstract void OnBeforeDestroy();
做这步的目的呢,是为了提醒子类不要覆写了 OnDestroy。提醒是怎么做到的呢。
我们通过分析可以得出,使用 MonoBehaviourSimplify 的情况有两种。
一种是,在写脚本之前就想好了这个脚本要继承 MonoBehaviourSimplify,但是继承之后,编译会报错,因为有一个抽象方法,必须实现,也就是 OnBeforeDestroy。那么实现了这个,用户就会知道设计 MonoBehaviourSimplify 的人,是推荐用 OnBeforeDestroy 来做卸载逻辑的,并不推荐用 OnDestroy。这是第一种。
第二种呢,脚本本来就有了,但是在中途想要换成继承 MonoBehaviourSimplify,继承了之后,同样报错了,报错了之后发现 MonoBehaviourSimplify 推荐用 OnBeforeDestroy 来做卸载逻辑,这时候如果以前的脚本已经有了 OnDestroy 逻辑,用户就会把 OnDestroy 的逻辑迁移到 OnBeforeDestroy 里。这样也算达到了一个提醒的作用。
这就是 OnBeforeDestroy 的设计初衷,而 abstract 关键字,就应该这样用。
但是到这里呢,这套策略还是有一点小问题的。这个小问题就留在下一篇讲了。
今天的内容就这些,我们下一篇再见。
转载请注明地址:凉鞋的笔记:liangxiegame.com
更多内容
QFramework 地址:https://github.com/liangxiegame/QFramework
QQ 交流群:623597263
Unity 进阶小班:
- 主要训练内容:
- 框架搭建训练(第一年)
- 跟着案例学 Shader(第一年)
- 副业的孵化(第二年、第三年)
- 权益、授课形式等具体详情请查看《小班产品手册》:https://liangxiegame.com/master/intro
- 主要训练内容:
关注公众号:liangxiegame 获取第一时间更新通知及更多的免费内容。
Unity 游戏框架搭建 2019 (四十七) 集成到 MonoBehaviourSimplify相关推荐
- Unity 游戏框架搭建 2019 (四十五) 独立的方法和独立的类
我们在开始本示例之前,先整理出我们当前库中的代码类型. 工具方法:CommonUtil.GameObjectSimplify等. 类: MonoBehaviourSimplify. 静态方法中的方法全 ...
- Unity 游戏框架搭建 2019 (四十二) MonoBehaviour 简化
在前两篇,我们完成了第九个示例.为了完善第九个示例,我们复习了类的继承,又学习了泛型和 params 关键字. 我们已经接触了类的继承了.接触继承之前,把类仅仅当做是方法的集合,接触了继承之后,我们的 ...
- Unity 游戏框架搭建 2018 (四) 我所理解的框架
前言 架构和框架这些概念听起来很遥远,让很多初学者不明觉厉.会产生"等自己技术牛逼了再去做架构或者搭建框架"这样的想法.在这里笔者可以很肯定地告诉大家,初学者是完全可以去做这些事情 ...
- Unity 游戏框架搭建 (七) 减少加班利器-QApp类
本来这周想介绍一些框架中自认为比较好用的小工具的,但是发现很多小工具都依赖一个类----App. App类的职责: 1.接收Unity的生命周期事件. 2.做为游戏的入口. 3.一些框架级别的组件初始 ...
- Unity 游戏框架搭建 (九) 减少加班利器-QConsole
为毛要实现这个工具? 在我小时候,每当游戏在真机运行时,我们看到的日志是这样的. 没高亮啊,还有乱七八糟的堆栈信息,好干扰日志查看,好影响心情. 还有就是必须始终连着usb线啊,我想要想躺着测试... ...
- Unity 游戏框架搭建 (二十一) 使用对象池时的一些细节
上篇文章使用SafeObjectPool实现了一个简单的Msg类.代码如下: class Msg : IPoolAble,IPoolType{#region IPoolAble 实现public vo ...
- Unity 游戏框架搭建 (五) 简易消息机制
什么是消息机制? 23333333,让我先笑一会. 为什么用消息机制? 三个字,解!!!!耦!!!!合!!!!. 我的框架中的消息机制用例: 1.接收者 using UnityEngine;names ...
- class unity 定义类_Unity 游戏框架搭建 2019 (二十五) 类的第一个作用 与 Obselete 属性...
在上一篇我们整理到了第七个示例,我们今天再接着往下整理.我们来看第八个示例: #if UNITY_EDITORusing UnityEditor; #endif using UnityEngine; ...
- Unity 游戏框架搭建 2018 (一) 架构、框架与 QFramework 简介
约定 还记得上版本的第二十四篇的约定嘛?现在出来履行啦~ 为什么要重制? 之前写的专栏都是按照心情写的,在最初的时候笔者什么都不懂,而且文章的发布是按照很随性的一个顺序.结果就是说,大家都看完了,都还 ...
最新文章
- Flutter 配置
- 基于opencv的简单视频处理类示例
- Hive自定义UDF的JAR包加入运行环境的方法
- dojo Quick Start/dojo入门手册--开始使用dojo.js
- 大数据WEB阶段(三)CSS
- 推箱子java下载_Java实现简单推箱子游戏
- php mysql数据库 指南_用 PHP 创建 MySQL 数据库
- PHP中上传文件文件名乱码
- android StringBuffer实现换行
- 前端学习(570):margin负值下的等高布局
- yeoman、bower、grunt 开发收集
- fseek函数与ftell函数使用例程
- MEGA-X 3D打印机教程:01_我的第一台3D打印机参数
- BUUCTF:[XMAN2018排位赛]file
- 读书笔记: 与爱因斯坦月球漫步-记忆术
- Dart语言中的Isolate
- 《苏菲的世界》读书笔记之一:自然派哲学家
- 读到哪句你心痛了??
- 开水果店前期投入,水果店会有哪些费用
- 提笔,再回忆~落笔,成悔,一切皆已随风:伤感日志
热门文章
- Marathon和Kubernetes的区别
- 访问学者J1签证通常准备那些材料?
- codeforces+contest+985D. Sand Fortress+思维
- C#系统实战(2):把登录模块化,用login.login(self)调用
- linux日志记录分析,linux日志朔源分析记录
- c语言如何给位域变量赋值,C语言下位操作(使用位域方法)
- IRIS 2021 技术文档 First Look 15 -- 使用 XEP 实例化存储 Java 对象
- 家庭网络WIFI相关知识
- 洛谷:ISBN号码(c语言)
- 遗传算法详细介绍以及基于遗传算法和非线性规划函数的寻优算法—MATLAB实现