Unity 游戏框架搭建 2019 (四十八) MonoBehaviourSimplify 中的消息策略完善

本文主要是介绍Unity 游戏框架搭建 2019 (四十八) MonoBehaviourSimplify 中的消息策略完善,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上一篇,笔者说,MonoBehaviourSimplify 中的消息策略还有一些小问题。我们在这篇试着解决一下。

先贴出来代码:

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(){}}
}

我们是使用字典进行注册消息的记录的,使用字典就要保证字典中的 key 是唯一的。而我们很可能在一个脚本中对一个关键字注册多次,这样用字典这个数据结构就显得不合理了。

相比字典,List 更合适,因为我们有有可能有重复的内容,而字典更适合做一些查询工作,但是 List 并不支持键值对,怎么办呢?

我们只好创建一个结构来存储我们的消息名和对应的委托,这个结构是一个类叫做 MsgRecord

消息策略部分的代码如下:

    public abstract partial class MonoBehaviourSimplify{List<MsgRecord> mMsgRecorder = new List<MsgRecord>();private class MsgRecord{public string Name;public Action<object> OnMsgReceived;}protected void RegisterMsg(string msgName, Action<object> onMsgReceived){MsgDispatcher.Register(msgName, onMsgReceived);mMsgRecorder.Add(new MsgRecord{Name = msgName,OnMsgReceived = onMsgReceived});}private void OnDestroy(){OnBeforeDestroy();foreach (var msgRecord in mMsgRecorder){MsgDispatcher.UnRegister(msgRecord.Name,msgRecord.OnMsgReceived);                }mMsgRecorder.Clear();}protected abstract void OnBeforeDestroy();}

代码比较简单。

而我们的示例代码,如下,增加了一行重复注册的代码。

    public class B : MonoBehaviourSimplify{private void Awake(){RegisterMsg("Do", DoSomething);RegisterMsg("Do", DoSomething);RegisterMsg("DO1", _ => { });RegisterMsg("DO2", _ => { });RegisterMsg("DO3", _ => { });}void DoSomething(object data){// do something}protected override void OnBeforeDestroy(){}}

而我们的 MonoBehaviourSimplify 内部实现发生了天翻地覆的变化,也没有对我们的示例代码产生一点影响,这叫封装。

那么到这里,我们的消息策略还有问题吗?

还有的,问题在创建 MsgRecord 的部分。
如下:

mMsgRecorder.Add(new MsgRecord
{Name = msgName,OnMsgReceived = onMsgReceived
});

我们每次注册消息,都要 new 一个 MsgRecord 对象出来,而我们在注销的时候,对这个对象是什么都没有做的,注销的代码如下:

foreach (var msgRecord in mMsgRecorder)
{MsgDispatcher.UnRegister(msgRecord.Name,msgRecord.OnMsgReceived);                
}

这样会造成一个性能问题,这个性能问题主要是有 new 时候寻址造成的,具体原因自行搜索,当然在本专栏的后边还是会介绍的。我们要做的,就是减少 new 的发生次数,要想减少,就得让我们的 MsgRecord 能够回收利用。

如何回收利用呢,答案是维护一个容器,比如 List 或者 Queue、Stack 等,也就是传说中的对象池。由于我们的 MsgRecord 的作用仅仅是作为一个存储结构而已,而存储的顺序也不是很重要,所以我们就用做简单的 Stack 结构,也就是栈,来作为 MsgRecord 对象池的容器。

其实现如下:

private class MsgRecord
{static Stack<MsgRecord> mMsgRecordPool = new Stack<MsgRecord>();public static MsgRecord Allocate(){if (mMsgRecordPool.Count > 0){return mMsgRecordPool.Pop();}return new MsgRecord();}public void Recycle(){Name = null;OnMsgReceived = null;mMsgRecordPool.Push(this);}public string Name;public Action<object> OnMsgReceived;
}

由于这个对象池只给 MsgRecord 用,所以就在 MsgRecord 内部实现了。
Allocate 是申请,也就是获取对象。Recycle 就是回收,当不用的时候调用一下就好了。

原理很简单。而 mMsgRecordPool 之所以设置成了 private 访问权限,是因为,不希望被外部访问到。对于一个类的设计来讲,MsgRecord 是一个非常合格的类了。

应用到我们的消息策略的代码如下:

protected void RegisterMsg(string msgName, Action<object> onMsgReceived)
{MsgDispatcher.Register(msgName, onMsgReceived);// var msgRecord = MsgRecord.Allocate();msgRecord.Name = msgName;msgRecord.OnMsgReceived = onMsgReceived;mMsgRecorder.Add(msgRecord);
}private void OnDestroy()
{OnBeforeDestroy();foreach (var msgRecord in mMsgRecorder){MsgDispatcher.UnRegister(msgRecord.Name,msgRecord.OnMsgReceived);  //msgRecord.Recycle();}mMsgRecorder.Clear();
}

我们发现,在申请对象部分可以简化成如下:

// var msgRecord = MsgRecord.Allocate();
//            
// msgRecord.Name = msgName;
// msgRecord.OnMsgReceived = onMsgReceived;
//            
// mMsgRecorder.Add(msgRecord);mMsgRecorder.Add(MsgRecord.Allocate(msgName, onMsgReceived));

只需要向 MsgRecord.Allocate 增加参数,代码如下:

public static MsgRecord Allocate(string msgName,Action<object> onMsgReceived)
{MsgRecord retMsgRecord = null;retMsgRecord = mMsgRecordPool.Count > 0 ? mMsgRecordPool.Pop() : new MsgRecord();retMsgRecord.Name = msgName;retMsgRecord.OnMsgReceived = onMsgReceived;return retMsgRecord;
}

代码不难,那么到这里,我们的完整的第十三个示例就写完了。

完整示例代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace QFramework
{public abstract partial class MonoBehaviourSimplify{List<MsgRecord> mMsgRecorder = new List<MsgRecord>();private class MsgRecord{private static readonly Stack<MsgRecord> mMsgRecordPool = new Stack<MsgRecord>();public static MsgRecord Allocate(string msgName,Action<object> onMsgReceived){MsgRecord retMsgRecord = null;retMsgRecord = mMsgRecordPool.Count > 0 ? mMsgRecordPool.Pop() : new MsgRecord();retMsgRecord.Name = msgName;retMsgRecord.OnMsgReceived = onMsgReceived;return retMsgRecord;}public void Recycle(){Name = null;OnMsgReceived = null;mMsgRecordPool.Push(this);}public string Name;public Action<object> OnMsgReceived;}protected void RegisterMsg(string msgName, Action<object> onMsgReceived){MsgDispatcher.Register(msgName, onMsgReceived);mMsgRecorder.Add(MsgRecord.Allocate(msgName, onMsgReceived));}private void OnDestroy(){OnBeforeDestroy();foreach (var msgRecord in mMsgRecorder){MsgDispatcher.UnRegister(msgRecord.Name,msgRecord.OnMsgReceived);  msgRecord.Recycle();}mMsgRecorder.Clear();}protected abstract void OnBeforeDestroy();}public class MsgDistapcherInMonoBehaviourSimplify : MonoBehaviourSimplify{
#if UNITY_EDITOR[UnityEditor.MenuItem("QFramework/13.消息机制集成到 MonoBehaviourSimplify", false, 14)]private static void MenuClicked(){UnityEditor.EditorApplication.isPlaying = true;new GameObject("MsgReceiverObj").AddComponent<MsgDistapcherInMonoBehaviourSimplify>();}
#endifprivate void Awake(){RegisterMsg("Do", DoSomething);RegisterMsg("Do", DoSomething);RegisterMsg("DO1", _ => { });RegisterMsg("DO2", _ => { });RegisterMsg("DO3", _ => { });}private IEnumerator Start(){MsgDispatcher.Send("Do","hello");yield return new WaitForSeconds(1.0f);MsgDispatcher.Send("Do","hello1");}void DoSomething(object data){// do somethingDebug.LogFormat("Received Do msg:{0}",data);}protected override void OnBeforeDestroy(){}}
}

运行结果如下图:
006tNc79gy1fzft5birfxj30w40ectar.jpg

菜单栏如下图:
006tNc79gy1fzft5ek7j9j30lk0fkamq.jpg

目录如下图:
006tNc79gy1fzft5i5y2ej30gm0dimz9.jpg

到这里我们可以进行一次导出了。

今天的内容就这些,我们下一篇在见,拜拜~

转载请注明地址:凉鞋的笔记:liangxiegame.com

更多内容

  • QFramework 地址:https://github.com/liangxiegame/QFramework

  • QQ 交流群:623597263

  • Unity 进阶小班

    • 主要训练内容:
      • 框架搭建训练(第一年)
      • 跟着案例学 Shader(第一年)
      • 副业的孵化(第二年、第三年)
    • 权益、授课形式等具体详情请查看《小班产品手册》:https://liangxiegame.com/master/intro
  • 关注公众号:liangxiegame 获取第一时间更新通知及更多的免费内容。

这篇关于Unity 游戏框架搭建 2019 (四十八) MonoBehaviourSimplify 中的消息策略完善的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/964417

相关文章

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

搭建Kafka+zookeeper集群调度

前言 硬件环境 172.18.0.5        kafkazk1        Kafka+zookeeper                Kafka Broker集群 172.18.0.6        kafkazk2        Kafka+zookeeper                Kafka Broker集群 172.18.0.7        kafkazk3

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个?

跨平台系列 cross-plateform 跨平台应用程序-01-概览 cross-plateform 跨平台应用程序-02-有哪些主流技术栈? cross-plateform 跨平台应用程序-03-如果只选择一个框架,应该选择哪一个? cross-plateform 跨平台应用程序-04-React Native 介绍 cross-plateform 跨平台应用程序-05-Flutte

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

pico2 开发环境搭建-基于ubuntu

pico2 开发环境搭建-基于ubuntu 安装编译工具链下载sdk 和example编译example 安装编译工具链 sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib 注意cmake的版本,需要在3.17 以上 下载sdk 和ex

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

数据治理框架-ISO数据治理标准

引言 "数据治理"并不是一个新的概念,国内外有很多组织专注于数据治理理论和实践的研究。目前国际上,主要的数据治理框架有ISO数据治理标准、GDI数据治理框架、DAMA数据治理管理框架等。 ISO数据治理标准 改标准阐述了数据治理的标准、基本原则和数据治理模型,是一套完整的数据治理方法论。 ISO/IEC 38505标准的数据治理方法论的核心内容如下: 数据治理的目标:促进组织高效、合理地

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [