喜欢Four这个项目,就赶快在GitHub上Star这个项目吧!node
喜欢个人文章,来微博关注我吧:王选易在学C艹git
点我下载程序员
项目Logo:github
下面是该游戏的项目地址,各位想参考源代码的同窗能够到个人GitHub上下载该项目的源码。设计模式
项目主页数组
GitHub地址数据结构
bug反馈及建议架构
我作这个项目的原始目的是实验MVC在游戏中的应用。mvc
Model-View-Controller(MVC)是一种组合设计模式,它体现了一种关注点分离(Separation of concerns,SoC)的思想。MVC主要把逻辑层和表现层进行了解耦,将一个问题划分红了不一样的关注点。加强了应用的稳定性,易修改性和易复用性。app
MVC常常被使用在Web框架中,包括J2EE,RoR和.Net中都对MVC模型进行了框架层面上的封装,以便程序员能够简单方便地做出结构良好的Web应用。
Cocos2d-x自己并无提供内置的MVC支持,可是,咱们仍是能够在游戏中基于MVC架构来设计游戏。在这篇博文中,我将向你们展现一下我是如何使用MVC架构来塔尖Four这个游戏的。
Four这个游戏的创意来自一个叫作走四棋的传统游戏,走四棋规则的详细介绍在这里:走四棋的百度百科。
下面我简单谈一下这个面板游戏(board game)的一些特性
举个例子,下面这幅图即为游戏过程当中的一幅图。在下面的游戏过程当中,位于(1,0)位置的黑子向左移动到(0,0)的位置后便可吃掉白子。
Cocos2d-x有这样一些主要的类,CCSprite,CCLayer,CCScene,CCNotificationCenter。咱们会使用这些类进行游戏中MVC架构的搭建,若是你对这些类的做用不熟,请参考个人这篇博文【Cocos2d-x-基础概念】Director Scene Layer and Sprite。
咱们通常的游戏流程是
这个过程看起来十分简单,而且能够十分快速地作出游戏。可是其缺陷就在于在CCLayer中咱们作了太多的事情。CCLayer同时承担了逻辑层和表示层的任务。不符合咱们上文中提到的关注点分离的原则。若是游戏中有较为复杂的状态转换时就捉襟见肘了。
下面是该游戏项目的目录结构,咱们接下来对这几个文件夹进行分别的讲解:
下面是该项目的一个简单的类图
在类图中,虚线表明的是经过消息机制进行沟通,而在Cocos2d-x中,这种沟通是经过CCNotificationCenter来实现的。
Model在游戏中表明的是消息驱动的有限状态机,Model会接受Controller层发送的消息,并根据消息来更改本身的内部数据,而后把内部数据改变这一消息发送给View,通知它更新。
Model在Cocos2d-x对应的是哪一个类呢?
很遗憾,可是Cocos2d-x并无提供状态机的feature,因此咱们须要本身实现一个Model类,在Model类中,须要本身实现诸如状态转换和消息处理等功能。例如在个人Model类中,我提供了以下接口。
class Model : public CCObject { public: // 添加一条状态转换,from-起始状态,msg-接收的消息,to-终结状态,在msg发生时会发生状态转换 Model* addTransition(const string& from, const string& msg, const string& to); // 检查当前状态机可否发生msg对应的状态转换 bool checkMessage(const string& msg); // 触发msg对应的状态转换 void onMessage(const string& msg); void onMessage(const string& msg, CCObject* o); // 等待某个CCAction结束后发送一条消息。 void waitAction(cocos2d::CCNode* node, cocos2d::CCFiniteTimeAction* action, const string& msg); // 获得当前状态名称 const char* getState(); };
注意如下几点:
CCNotificationCenter
发送一条消息通知游戏中的其余组件更新逻辑。Model不会持有View,因此View都是经过消息来得到Model更新的事件的。
咱们在编写游戏时,应该先编写Model,而后经过测试来保证Model的正确性。
以后,咱们去写View的时候,实际就是对Model这个状态机发送的各类消息的回调函数的编写了。
这样就讲表示层和逻辑层分离开,能够方便地单独测试每一个模块,可维护性大大提升。
在编写Model的时候,咱们常常会遇到这样的问题,就是Model中的数据是否应当与View中的数据保持一致。
好比:在View层的棋子的位置信息是否应该和Model层的位置信息保持一致?
其实,真实状况就是,这要看Model层的
计算使用那种数据形式更加方便,好比在Model中,咱们会把棋盘转化为一个二维数组,这样在AI运算,逻辑判断时,更加有利,因此棋子的逻辑位置和实际位置必然是不一样的。
再好比,不少时候游戏中的物理引擎的计量单位是厘米,米。而不是OpenGL中的坐标。这也是为了逻辑运算的方便。
可是有些数值,咱们会让它保持一致,好比人物的属性等等。
View在游戏中表明的是Model消息的接受者,在Cocos2d-x中,View通常是指CCLayer的子节点,即CCSprite,CCLabelAtlas,CCMenuItem,Particle System(粒子系统)等等。
它们会重载onEnter函数,在onEnter中注册本身对某个Message的监听。同时在onExit函数中将全部监听清除。(清除监听很重要,不然会出现很可怕的野指针问题)。
Controller在游戏中负责将用户触摸事件转化为逻辑事件(即咱们上文中所说的消息),同时要对用户触摸事件中的信息进行正规化,而且通知变动。
Controller在Cocos2d-x中通常用CCLayer
来实现,由于CCLayer天然地继承了CCTouchDelegate这一接口,自己就能够触摸事件,因此我在这个游戏中全部的XXXController都是继承自CCLayer。
Controller另外一个很重要的职责,就是建立View和Model,由于Controller至关于Model和View的一个中间层,因此天然Controller会同时持有View和Model的应用,来方便地传递数据。
Protocol中定义了一些Model,View和Controller中共享的数据,
Message.h
中,就定义了游戏中各类类型的消息, Tag.h
中,就定义了游戏中一些Node的Tag,其实Tag这个定义不是很准确,其实这里的Tag指的是惟一标识CCNode的一个ID。 ChessboardProrocol.h
中,定义了游戏中期盘的宽,搞和一些经常使用的数据结构。