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

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

Apache Ignite 与 Spring Boot 集成详细指南

《ApacheIgnite与SpringBoot集成详细指南》ApacheIgnite官方指南详解如何通过SpringBootStarter扩展实现自动配置,支持厚/轻客户端模式,简化Ign... 目录 一、背景:为什么需要这个集成? 二、两种集成方式(对应两种客户端模型) 三、方式一:自动配置 Thick

Python极速搭建局域网文件共享服务器完整指南

《Python极速搭建局域网文件共享服务器完整指南》在办公室或家庭局域网中快速共享文件时,许多人会选择第三方工具或云存储服务,但这些方案往往存在隐私泄露风险或需要复杂配置,下面我们就来看看如何使用Py... 目录一、android基础版:HTTP文件共享的魔法命令1. 一行代码启动HTTP服务器2. 关键参

OpenCV在Java中的完整集成指南分享

《OpenCV在Java中的完整集成指南分享》本文详解了在Java中集成OpenCV的方法,涵盖jar包导入、dll配置、JNI路径设置及跨平台兼容性处理,提供了图像处理、特征检测、实时视频分析等应用... 目录1. OpenCV简介与应用领域1.1 OpenCV的诞生与发展1.2 OpenCV的应用领域2

Python Web框架Flask、Streamlit、FastAPI示例详解

《PythonWeb框架Flask、Streamlit、FastAPI示例详解》本文对比分析了Flask、Streamlit和FastAPI三大PythonWeb框架:Flask轻量灵活适合传统应用... 目录概述Flask详解Flask简介安装和基础配置核心概念路由和视图模板系统数据库集成实际示例Stre

游戏闪退弹窗提示找不到storm.dll文件怎么办? Stormdll文件损坏修复技巧

《游戏闪退弹窗提示找不到storm.dll文件怎么办?Stormdll文件损坏修复技巧》DLL文件丢失或损坏会导致软件无法正常运行,例如我们在电脑上运行软件或游戏时会得到以下提示:storm.dll... 很多玩家在打开游戏时,突然弹出“找不到storm.dll文件”的提示框,随后游戏直接闪退,这通常是由于

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

SpringBoot集成EasyPoi实现Excel模板导出成PDF文件

《SpringBoot集成EasyPoi实现Excel模板导出成PDF文件》在日常工作中,我们经常需要将数据导出成Excel表格或PDF文件,本文将介绍如何在SpringBoot项目中集成EasyPo... 目录前言摘要简介源代码解析应用场景案例优缺点分析类代码方法介绍测试用例小结前言在日常工作中,我们经

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控