Unity 游戏框架搭建 2019 (四十七) 集成到 MonoBehaviourSimplify

本文主要是介绍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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

SpringBoot集成Netty,Handler中@Autowired注解为空

最近建了个技术交流群,然后好多小伙伴都问关于Netty的问题,尤其今天的问题最特殊,功能大概是要在Netty接收消息时把数据写入数据库,那个小伙伴用的是 Spring Boot + MyBatis + Netty,所以就碰到了Handler中@Autowired注解为空的问题 参考了一些大神的博文,Spring Boot非controller使用@Autowired注解注入为null的问题,得到

vue项目集成CanvasEditor实现Word在线编辑器

CanvasEditor实现Word在线编辑器 官网文档:https://hufe.club/canvas-editor-docs/guide/schema.html 源码地址:https://github.com/Hufe921/canvas-editor 前提声明: 由于CanvasEditor目前不支持vue、react 等框架开箱即用版,所以需要我们去Git下载源码,拿到其中两个主

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

【网络安全的神秘世界】搭建dvwa靶场

🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 | 每天学会一个渗透测试工具 下载DVWA https://github.com/digininja/DVWA/blob/master/README.zh.md 安装DVWA 安装phpstudy https://editor.csdn.net/md/?articleId=1399043

剑指offer(C++)--孩子们的游戏(圆圈中最后剩下的数)

题目 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!

BD错误集锦7——在集成Spring MVC + MyBtis时使用c3p0作为数据库时报错Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatem

异常信息如下: Type Exception ReportMessage Handler dispatch failed; nested exception is java.lang.AbstractMethodError: Method com/mchange/v2/c3p0/impl/NewProxyPreparedStatement.isClosed()Z is abstractDescr

陀螺仪LSM6DSV16X与AI集成(8)----MotionFX库解析空间坐标

陀螺仪LSM6DSV16X与AI集成.8--MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启CRC串口设置开启X-CUBE-MEMS1设置加速度和角速度量程速率选择设置FIFO速率设置FIFO时间戳批处理速率配置过滤链初始化定义MotionFX文件卡尔曼滤波算法主程序执行流程lsm6dsv16x_motion_fx_determin欧拉角简介演示 概述 本文将探讨

cocospod 搭建环境和使用

iOS 最新版 CocoaPods 的安装流程 1.移除现有Ruby默认源 $gem sources --remove https://rubygems.org/ 2.使用新的源 $gem sources -a https://ruby.taobao.org/ 3.验证新源是否替换成功 $gem sources -l 4.安装CocoaPods (1)  $sudo gem