Unity游戏架构:模块间通信原理及设计思路

本文参与“Unity游戏架构”征文活动

转载自“ 游戏扶持by腾讯游戏学院”


先谈一谈个人对游戏框架的一点理解,顾名思义,框架是一个项目的骨架,如同大树的主干,搭建框架,在此基础上再加入各个功能模块,构成有一个完整的项目。如同一棵树有一个健壮的主干,再从主干上生长出一个一个的分支,最终长成一颗枝繁叶茂的大树。此外,框架会设定好模块的基本格式,更加有利于功能的模块化;框架还负责各个模块之间的交互,每个模块作为一个独立的个体,内部是独立运行的,如果模块间需要进行一些交互,则需要通过框架来实现,避免模块间直接通信,最终模块关系错综复杂,难以维护。

一、简单实现方式

模块间的数据交互、信息传递是框架中比较重要的一部分,最近根据做过的几个项目和一些资料,编写了一套简单的模块间信息传递机制,在此之前也发过几篇关于模块封装的博文,组装到一起,应该也是可以用了。

关于消息机制(消息/广播/通知···有多个叫法,不过实现的功能都是类似的),大概原理是:

1. 消息:由唯一消息ID、消息体组成(有的写法也会将消息ID分离出消息体,不包含在消息体内,这样方便消息转发都多个不同模块,但不便于管理)。消息ID,用int值表示,根据需求划分一定数量的ID给每个模块,模块内部单独管理;消息体:数据信息的载体,一般是一个子类,这样方便不同模块自定义数据格式。

要注意一点,跨模块消息,A模块需要B模块的数据,就需要注册B模块的消息,这样B在发送消息之后,只要注册过这条消息的模块,都会接收到消息,这也要求模块内定义ID后,不能随意变动ID,建议采用枚举表示,使用时将枚举转为Int。

2. 建立消息中心,保存所有的消息及对应接收回调函数,各模块通过管理者将消息注册到消息中心,有对外的发消息接口,供各模块调用,当然同样要有注销接口。在收到消息之后执行对应的回调函数,将参数传递到注册过消息的多个具体模块,模块内部自行处理。

3. 回调:各个模块管理者,在脚本运行开始,注册所需要的消息,在脚本待销毁的时候注销,提供一个消息接收回调,消息中心会将消息下发到回调,然后内部处理消息。

4. 关于消息中心保存记录消息,我用的字典Dictionary保存对应的ID和回调,利用委托的一个优点就是委托的“+=”和“-=”,比如有多个模块注册了同一个消息,可以将callback+=newCallback,这样来把所有的回调记录下来,在注销时减掉。

但委托减法具有不可预测的结果,虽然改成Event事件可以避免程序报错,但结果与委托一样也会有这种问题,为了避免出现问题,在使用减法时,每次只减掉一个元素(即 a-= b,不要a-=(b+c)  ),就不会发生意外了,可以忽略代码里的警告了。

Demo代码如下,写的比较简单,实际项目需要再完善。



二、再封装设计

以上只是一种比较常见的消息机制,在此基础上还可以进行改进、封装,因为涉及到之前的项目,这里不粘代码了,简单说一下设计思路吧 。

1. 消息分类:这一点与上面Demo一样,按模块对消息进行分类。

2. 消息中心:每个模块的管理者作为一个消息中心,负责本模块消息的 注册、注销、处理。总消息中心,不处理具体消息,只负责不同模块间的消息流转。

要注册一条消息,模块先判断是否是本模块消息,是的话直接注册到本模块,若不是,则转发到上级的消息中心,消息中心再将消息识别下发到对应的模块,对应模块进行注册。

原本所有的消息都是在消息中心进行处理,现在在模块内部处理,消息中心只负责将消息下发到对应的模块即可。

比如说北京有一个快递中心,一天,在朝阳区的A要寄快递给海淀区的B,A找到朝阳区的快递员上门取件,快递员取件后将快递送到快递中心,再由快递中心识别快递物品,委派海淀区的快递员将快递配送给B。但第二天,朝阳区的A想要寄快递给同在朝阳区的C,同样朝阳区的快递员会上门取件,然后将快递送到快递中心,经识别后将快递委派给朝阳区的快递员,再配送给C。这样就显得比较繁琐了,快递中心的负荷也会非常大。

快递中心感觉这样好心累,要进行改革,于是增大了快递员的权利,可以直接处理自己负责地区的快递,无需再经过快递中心。这样A在寄快递给C的时候,朝阳区的快递员从A取件之后,发现这是朝阳地区内的快递,是寄给C的,就可以直接配送给C,省时又省力。如果A再给B寄快递,朝阳区的快递员取件之后,识别快递是其他地区的,就直接将快递送到快递中心,快递中心收到之后,只需识别是海淀区的,无需关注收件人是谁,再将快递流转到海淀区的快递员,由该快递员配送到C手中。这样来,整个快递流程就完美了。

3. 记录消息及其回调:

网上搜到的大都是用委托或事件来记录消息的,前面也提到过,利用“+=”“-=”计算可以记录一条消息和多个回调。

也想过用每个消息用一个List来记录所有的回调,但明显这样是不可取的。

这里介绍另一种记录方式:

记录的不是具体某个回调函数,而是采用链表的方式记录回调函数所在的类。

3.1 写一个父类或接口,定义一个Callback函数,所有的模块管理者,都重写或实现该函数,用作消息的回调。

3.2 定义一个消息节点类(包含两个属性,本节点的回调脚本,下一个节点Next),注册消息的第一个回调类后,其Next指向第二个回调类,依次类推,记录一个消息的所有回调类。

3.3 只需记录消息ID和第一个节点,获取第一个节点后依次获取节点的Next节点,知道Next节点为空,即遍历完所有节点。

3.4 在收到消息之后,遍历所有的节点,执行回调类的回调函数。



关于游戏架构,消息/通知机制只是其中的一部分,还有很多很多需要去学习去实践,希望以上的内容可以帮助到大家。




营运型手游开发、测试、正式的三阶段开发架构

在手机游戏的畅销排行榜上,可以看到大多数的游戏都是营运型的游戏。所谓的营运型游戏,指…的是游戏的开发并不是上架后就结束,而是需要持续的配合游戏营运的需求,进行游戏的更新、…内容调整以及后续内容的开发。这样的游戏虽然相对来说获利较佳,不过对于游戏开发团队来说

电商网站的商品详情页系统架构

数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入Nginx服务器。

web应用单点登录原理与简单实现

一、单系统登录机制1、http无状态协议web应用采用browser/server架构,http作为通信协议。

游戏编程开发《球球大作战》源码解析:服务器与客户端架构

鉴于agar.io类型游戏的火爆场面,一些公司纷纷效仿,一时间出现各种《大作战》类型的游戏。出于学习的目的,亦是做些技术和方案储备,接下来会有大概篇文章,分析下面这款使用nodejs编写的开源“球球大作战”。由于该游戏采用服务端运算、客户端显示的方式,服务

老公月入五万的程序员夫人,都过上贵妇的生活了么?

作者:程序人生https://blog.csdn.net/csdnsevenn/article/details/之前被一篇文章刷......夫人们要当贵妇,都只是一个梦,而已。架构师小秘圈聚集万架构师的小圈子长按二维码 ▲关注「架构师小秘圈」公众号谢谢老板,点个好看↓