序言,保卫萝卜项目作为自己学习整体游戏项目的开始,还是很有收获的。

项目初步实现了分管关卡地图编辑、场景结构、关卡选择、游戏地图等主要功能,同时内部构架采用了MVC加单例的构架,对我这种初学者还是很有启发,至少对游戏项目的构架有了个初步的概念。

学编程写博客是总结提高的重要手段,之前刚用MVC加单例(以后简称为MCVM,即Model,Controller、View 和 单例 Manager)的构架重构了单机斗地主项目,回头再来看自己的学习过程中还是有很多需要总结提高的地方,所以虽然仅仅是刚摸到了门边,还是要总结提高写几篇博客来。

一、项目整体构架的理解

当然目前对项目构架的理解仅来源于保卫萝卜项目、单机斗地主项目和初读《游戏编程模式》的学习,所以对别人的参考意义不大,仅是对自己的学习阶段的总结

(一)项目构架

先贴一张自己的《游戏编程模式》的读书笔记。

1、项目构架的作用

就对我自己而言,项目构架就是让我在制作具体游戏这个“盖房子”的过程中,区分出来哪些是钢筋、水泥沙子、砖石、地板等。

比如我想实现斗地主的发牌功能时,玩家能看到和操作的部分放到View的子类里,内部具体各个子牌库的卡牌交换,制作卡牌和分发卡牌的功能都放到Model的子类里,至于具体子类的如何分类设置,还是根据个人理解和习惯来。

2、构架的主旨:应对变化的灵活性和简便性

比如想在斗地主的基础上开发超能力斗地主,比如玩家可以发动超能力复制几张牌,或者抽走别人的牌,这样的新功能在完善的构架上就可以很简单的开发出来。

3、构架具体实操

对目前我这个水平来说,就是能重用的尽量都拿出来独立重用,能解耦的尽量解耦。

(二)MVC+M

1、MVC

MVC是个历史悠久的构架了,我也查阅了很多博客,感觉似乎没有很明确的界限。

个人理解是核心的要素是数据层和用户层的分离。

还是以斗地主为例来说,

(1)数据层:单张数据卡牌(不包含显示)作为内部数据操作的单元,具体的制作整幅扑克,洗牌、发牌、留地主牌、抢地主牌、分发并显示地主牌这些内部操作都放在Mode类的子类中。

(2)用户层:就指让用户能看到的部分,比如自己手牌,对面玩家的牌的背面,桌面打出的牌,还有用户操作的部分,比如抢地主的操作、出牌选牌的操作。

(3)为什么要分离?

最初的原因可能是为了大型项目的多人开发,比如后端和前端的区分。

对于我来说:

首先是解耦,这样单独改动内部逻辑或者界面要素就比较方便。

其次是重用,比如斗地主的牌库业务模型拿出来做杀戮尖塔、炉石传说、够级都可以一用。

最后是思路,给自己一个构架的思路,也就是解耦和重用的方法论。

2、M——XXXmanager各种单例类

单例类:一个类只有一个实例,并提供一个全局访问入口

在《游戏编程模式》被专门反对过,原因也是很明显:

全局属性破坏代码的易读性!全局变量促进耦合!对并发多线程不友好!

不过某次unity大佬的演讲也着提到了项目内尽量包含三类重要的单例类

--PoolManger 对象池模式,用于取出和暂存预制体,减少不可控的GC(垃圾回收)

--LevelManager 提供同步或异步跨场景的方法

--Savemanager 提供全局的保存方法和日志记录

所以,个人理解:

单例类因为太方便了所有要克制使用,正所谓犹豫不决扔进单例类,长时间如此就跟可读性、解构和重用性说拜拜了。

单例类尽可能的提供一些必需的、独立的全局方法,并尽可能的限制其全局访问,把访问权限局限在一定范围内。

二、MVC+M使用的一点小经验

因为目前只重构了单机都地主这一个项目,而且基本功能模块没有大改,基本上是把之前集中在GameControlller中的各个功能都区分到Model和View中,所以理解还很肤浅,而且也没有完全实现自己的进一步改进想法。

目前仅仅是自己的一个学习阶段的总结。

(一)程序基础类的设定

基础类是指游戏中的基础单元,

比如棋牌游戏中的棋子、单牌,卡牌对战游戏中的角色、单牌,

2D平台跳跃游戏中的地图格、怪物基类、物品基类和技能基类等,

RPG游戏中地图基类,怪物基类,任务基类,物品基类和技能基类等。

所以,游戏项目构架的基础是先把基础类确定并尽可能完善。

1、基本属性

可以枚举类型加以限制,比如纸牌的数字、权重、花色、归属等。

2、标志位

一般是Bool类变量,比如纸牌是否已显示

3、Tag

主要配合Unity的FindWithTag使用,比如指向性技能,自动寻路子弹等

(二)Model

1、概念与理解

由程序的基础类衍生出基础类的集合类的管理类

那么,比如卡牌与子牌库,地图格与关卡地图,Model子类就是用于管理这些集合类,比如斗地主中管理所有子牌库(玩家、桌面、地主栏牌、总牌库),比如塔防游戏中管理整个回合的运行。

对我自己而言,直观来说,就是游戏内运行的主要部分。如果人能够直接与游戏内的代码互动,游戏做到这步就完成了,当然人类不能,所有还要依赖用户层来让用户看到、听到和操作到。

2、Model类的具体细节

这里的Model类的基本细节都暂时按保卫萝卜内定义的框架来定义,自己暂时没有那个水平来修改。

如结构图所示,Model只有基本的SendEvent方法,用来通知View或Controller。

所以,Model可以说是MVC类内自由度最低的类,

引申来说就是以下几点。

(1)简而言之,Model类就是提供集合类的相关属性和操作方法

比如在斗地主中,Model-卡片管理类就是提供创建全套扑克、洗牌、发牌、传递牌、回收牌等方法。

Model-回合管理类就是提供回合进度状态、当前大牌、当前大牌玩家、当前地主玩家、当前地主牌等属性和开始回合、结算回合等方法。

供VC来具体调用

(2)Model类尽量避免直接调用其他Model类。

因为这样其自身没有提供这样的方法,而通过静态MVC类来调用又破坏了其解耦性与独立性。

如果确实出想了这种情况,有两种处理方法。

一种就是把两个model类合并,当然这里是出现了大量相互调用的情况,这意味着确实这俩Model类分的不合适。

另一种就是把数据打包进SendEvents的参数内,传递给V或者C让他们处理。

(三)View

1、概念与理解

用户层可以区分为UI和可见对象:

(1)管理UI的View类:UI比较容易理解,就是状态栏、积分栏、各类菜单以及血条、角色、小地图等UI要素。而View类一般是针对一个完整的菜单或者UI。

(2)管理可见对象的View类:可见对象简而言之就是用户/玩家能看到的部分,比如地图块、手牌和塔防中的塔、怪物、子弹等。一般来说都是生成器,比如子弹生成器、怪物生成器、塔生成器,卡牌生成器。

2、结构与实现

(1)View类可以通过GetModel方法访问Model类

——View应该只读的访问Model的数据。

个人理解其实这样设定存在滥用和失控的危险。

比如说如果整个游戏流程中间存在View调用Model而推进的部分,那么M与V之间的分离就遭到了破坏。

应该加上对View访问的限制,View类对Model的访问应该是只读的,不引起Model类内部及基础类变化的。

(2)View类既可以发送事件也可以接收多个事件。

——View主要包含两大类内容:

一是提供将数据转为化显示的方法,供Controller调用

二是接收用户输入(键盘操作、点击、拖拽和滑动等)并将其初步处理,并将其作为Event发出的方法

3、注意事项

(1)应该严格的区分输出流程和输入流程。不要滥用对Model的访问,导致View与model层高度耦合,而是Controller类失去了应由的中继作用。

简单来说

输出流程:提供内部数据到用户显示的方法,供Controller调用

输入流程:接收用户输入的方法,初步归纳处理,然后发送Events给Controller处理

(四)Controller类

1、概念与理解

controller类作为用户层和数据层的中间层,可以理解为业务逻辑层。

2、结构与实现

controller类无法发出事件,只能关联并接收单个事件,所以它是事件的流程终点

controller类除了无法发出事件,几乎拥有所有的MVC类权限(注册三种类,访问M和V)

(1)事件流程终点

个人认为理解Controller类的主要核心是它是大部分Event的终点。

而Event由的发出者来区分的话分为Model发出的类和VIew发出的类

● Model发出的Event:一般是Model内方法调用完毕所发出的事件,比如斗地主中的卡牌创建完毕,发牌完毕;另外一种应该是节点状态类,比如回合开始、开始出怪等。但由于Model本身是比较被动的类,Model发出的Event大部分是Model内的方法被调用、状态参数被修改后发出的事件。

Controller接收,一般会实现两种功能,一是Model间的通信,二是对View进行调用以更新用户层

● View发出的Event:一般是用户输入或者可见对象互动所发出的事件,起点是由用户或游戏进程产生的。

Controller接收,一般会实现两种功能,一是View间的通信,二是对Model进行调用以更新用户层

3、使用时的注意

● Controller和M/V的定位要严格区分开。

M提供内部数据相关的方法,

V提供修改用户层显示的方法,

C就是根据事件和事件参数来调用M/V提供的方法!

●复杂且需要频繁调用M/V的方法,不要写在C中,而应该把其中对数据层和业务层的内容分开,分别由M或V来书写,尽量以参数传递完成交互,也就是说要充分解耦。

(五)Events

1、概念和结构

Events作为MVC中组件间通信的重要载体,主要构成为 事件名字符串+事件参数类。

事件名字符串:

(1)用于和Controller一对一关联并存储到MVC类中的静态字典里;

(2)添加到View类的监听事件链表内,而且可添加多个事件。

事件参数类:

根据事件传递的需要注册相应的参数类,本质上是数据和实例打包传递。

2、类别

根据发出和接收者可以区分为 M-->C , V-->C , V-->V,M-->V者四种Event。

但对于后两种,我个人目前认为是需要严格限制和尽量避免使用的,因为会严重的破坏程序可读性,让人疲于寻找事件处理的终点。

为了更直观的理解,以RPG游戏来举例子

(1)V-->C :由用户层输入发起的事件,特征是随机性

举个例子如下:

玩家点击地面,发送点击移动事件E_MOVE,

并将点击的地面坐标、控制对象等信息打包进事件参数MoveArgs发给对应的Controller:C_MOVE

然后C_MOVE调用Model_Player的Move方法,

控制玩家类向点击的地面坐标移动。

(玩家类内部调用动画组件播放移动动画;

调用寻路模块来决定道路,循环判定道路条件等)

(2)M-->C:由于内部属性变化或流程进度而发起的事件,特征是必然性和规律性。

属性内部变化发起:

比如玩家类的经验值不停累加,达到升级条件了。控制玩家类的Model_Player内部修改玩家属性并发送E_LevelUp事件,C_LevelUp接收事件,调用响应的View组件,播放升级特效、音效。

流程进度发起:

玩家进入Boss房间,先播放文本和音效,再播放Boss出场动画,之后Boss随着血量或者时间的变化而进入不同阶段,同时房间内出现不同的陷阱、特效和小怪等,这些流程触发基本都是又Model发出的事件。

(3)如何避免使用 V-->V,M-->V和单例类发出的事件

这几种事件放在一起讲是因为这几种方式都很方便容易被滥用。

之前自己在重构单机斗地主项目时就犯了这样的错误,model发送事件View直接接受并处理,觉得Event和Controller一对一对应太麻烦,懒得新建Controller都扔给View接收。最终后果就是事件系统的失控,这还只是一个简单的单机斗地主项目,如果项目再复杂点估计还没完成主要功能就崩溃了。

如何避免呢?

● 坚持Controller为所有事件的终点。

无论事件从哪发起,什么类型,带什么参数,都交给对应的Controller来处理。

● 增强Event和Controller的重用性。

Event和Controller是一一严格对应的,那么稍微复杂点的项目岂不是要建立茫茫多的Event和Controller吗?比如Boss流程,难道每个Boss都要弄一套Event吗?

所以要对Event的数量和类别要提前规划和涉及,通过事件参数的设计来提高Event和controller的复用性。比如刚才那个例子,把事件对应的Boss信息和阶段信息都打包进事件参数,这样Boss流程就可以重复使用了。

● 谨慎并显式的使用不以Controller为终点的Event

(1)个人目前认为M-->V或直接调用MVC类而发送事件这两种是完全可以避免的。如果认识更深入了有变化的话再更新。

(2)V-->V 在特定情况下使用时要在事件命名上体现,比如 E_VV_SkillEffect。

目前思考来说应该局限在不涉及数据变化和调用时View组件间的通信,比如刷新出怪物的时候带一些特效,这样可能就需要怪物孵化器View跟特效渲染View之间进行调用。虽然也可以把特效渲染直接包含在孵化器View内部,但是为了解耦和功能相对独立,这样的VV通信也是可以接受的。

其实刚才这个例子想了下用V-C反而更好,所以如果确实不是VV能有巨大优势,还是尽量避免使用。

(六)部分关键类

1、封装参数类——Args:

● 将事件等部件需要传递,将事件相关的数据打包进参数内进行传递。每次发送时间前都要重新进行打包,接收后根据需要提取相关数据。

● 注意空对象检测,

● 具体结构可以把数据简单的参数类放到一个文件中,结构比较复杂的参数类再单独建文件

2、基础类——Data

● 将游戏的静态数据和基础对象封装为类

举例:Card.cs单个卡片、Level.cs关卡类、Point.cs路径点类、Round.cs回合类、Tile.cs地图格类格、事件名、基础路径名等关键数据

3、工具和规则类——Tools.cs

● 核心的工具类,提供XML读写、图片载入等功能。

● 游戏的静态规则,比如牌子判断、权重判断可以写成静态方法来使用。

4、可重用对象脚本——Objects:

● 一般挂在在自动生成对象的预制体上,全面管理预制体的生命周期。

● 游戏中主要可重用游戏对象的附属脚本,比如玩家、敌人、地块等

● 注意对象归纳和层级设置

5、静态数据类——StaticData:

● 主要游戏对象的信息封装和储存,比如角色初始属性、地图结构、游戏的核心参数、对象的三围属性等

● 角色存档的功能实现

● 可以进一步优化为XML文件或者数据库。

【技术博客】2020.04.28-简单塔防游戏和棋牌游戏构架学习 ——学习系列博客(一)构架初探相关推荐

  1. 多线程 可参考 博主【 lx青萍之末】 的 【C++并发编程 】系列博客

    关于多线程 相关知识 可参考博主[ lx青萍之末] 的 [C++并发编程 ]系列博客 https://blog.csdn.net/daaikuaichuan/category_6887432.html

  2. 2020/04/28 07-kazoo库使用和watcher

    之前启动的每个服务都有不同的状态,查看信息看到的是角色,zookeeper是典型的主从结构,任意一个都可以做主,但是主只能有一个,这个主就是leader(数据最新最权威,一切数据以它为准) 在3.2之 ...

  3. Flash制作简单塔防游戏(二)

    有了沿路跑的小怪,现在加个炮进去,如果小怪跑到炮的射程内,炮会旋转自己的炮管瞄准小怪,然后不停的打,如果小怪跑出了范围,则停止发射. 现在做武器,先做炮管,"插入" >> ...

  4. 唤镜引擎简单塔防事件截图

  5. Flash制作简单塔防游戏(一)

    玩过塔防游戏,基本上就是一堆大小怪物沿着固定路径行走,玩家在地图上某些点放置武器歼灭他们. 首先让我们准备一张简陋的地图 黄色的表示怪物行走路径,箭头表示行走的方向,路上的红色圆点坐标用来作为路径的数 ...

  6. (十一:2020.08.28)CVPR 2017 追踪之论文纲要(译)

    CVPR 2017 追踪之论文纲要(修正于2020.08.28) 讲在前面 论文目录 讲在前面 论坛很多博客都对论文做了总结和分类,但就医学领域而言,对这些论文的筛选信息显然需要更加精细的把控,所以自 ...

  7. (十四:2020.08.28)CVPR 2014 追踪之论文纲要(译)

    CVPR 2020 追踪之论文纲要(修正于2020.08.28) 讲在前面 论文目录 讲在前面 论坛很多博客都对论文做了总结和分类,但就医学领域而言,对这些论文的筛选信息显然需要更加精细的把控,所以自 ...

  8. 郭晓东的“系列博客,专辑”集锦

    基础知识: 字符编码的奥秘[专辑],浏览其中一篇:字符编码的奥秘utf-8, Unicode Unicode完整码表 <深度探索C++对象模型>[系列笔记]--对象模型.存储形式:默认构造 ...

  9. 【Socket网络编程】0.socket TCP/UDP 可参考 【lx青萍之末】 的 【Linux网络编程 】系列博客

    关于socket TCP/UDP 相关知识 可参考博主[ lx青萍之末] 的 [Linux网络编程 ]系列博客 https://blog.csdn.net/daaikuaichuan/category ...

最新文章

  1. 在线作图|如何绘制一张变量相关图(PCA)
  2. centos7 yum 安装 python3
  3. Register-SPWorkflowService 远程服务器返回错误: (404) 未找到
  4. mysql insert s锁_MySQL 死锁套路:唯一索引 S 锁与 X 锁的爱恨情仇
  5. 重拾Javascript(五)--优化字符串操作
  6. UFLDL深度学习笔记 (四)用于分类的深度网络
  7. 利用Trie(字典树)实现敏感词过滤算法
  8. 计算机文字录入在线打字,教您如何在一分钟内把书中的文字录入电脑
  9. 三相逆变器双pi控制器参数如何调节_单相光伏并网逆变器的环路控制
  10. 求点连通度,边连通度
  11. 【Mysql】execute和executeUpdate
  12. 使用五数概括法来确定数据集中的孤立点
  13. 【uniapp小程序】request发起请求
  14. ssm物业管理系统的设计与实现毕业设计源码261632
  15. github的博客搭建以及标签的自动化
  16. 电脑桌面录制直播嵌入网页
  17. 【NOIP模拟赛】小猫爬山
  18. java判断字符个数_使用Java判断字符串中的中文字符数量
  19. NUCLEO-L476RG开发板学习笔记汇总
  20. spring 定义自己的标签 学习

热门文章

  1. HelloWorld!仪式感
  2. h5移动端滑动内容置顶
  3. 如何去掉iview里面的input,button等一系列标签自带的蓝色边框
  4. Java内省API PropertyDescriptor#createPropertyEditor(javaBean)返回null造成空指针
  5. 监听文本框事件几种方法总结
  6. 腾讯的机器人梦:上亿美元下注七家创企
  7. Chrome 打开WebGL项目显卡不工作
  8. 计算机网络:自顶向下方法读书笔记(三)
  9. 购高配置计算机主机,解决方案:高端笔记本配置列表推荐和完整的高配置计算机物理检查...
  10. matplotlib在一张图同时画折线图和柱状图