喜歡Four這個項目,就趕快在GitHub上Star這個項目吧!
喜歡我的文章,來微博關注我吧:王選易在學C艹
點我下載
項目Logo:
下面是該游戲的項目地址,各位想參考源代碼的同學可以到我的GitHub上下載該項目的源碼。
項目主頁
GitHub地址
bug反饋及建議
我做這個項目的原始目的是實驗MVC在游戲中的應用。
Model-View-Controller(MVC)是一種組合設計模式,它體現了一種關注點分離(Separation of concerns,SoC)的思想。MVC主要把邏輯層和表現層進行了解耦,將一個問題劃分成了不同的關注點。增強了應用的穩定性,易修改性和易復用性。
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
中,定義了游戲中期盤的寬,搞和一些常用的數據結構。