【深刻Cocos2d-x】使用MVC架構搭建遊戲Four


喜歡Four這個項目,就趕快在GitHub上Star這個項目吧!node

喜歡個人文章,來微博關注我吧:王選易在學C艹git

點我下載程序員


##項目起源github

項目Logo:設計模式

下面是該遊戲的項目地址,各位想參考源代碼的同窗能夠到個人GitHub上下載該項目的源碼。數組

項目主頁數據結構

GitHub地址架構

bug反饋及建議mvc

mvc

我作這個項目的原始目的是實驗MVC在遊戲中的應用。app

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)的一些特性

  • 一個4行4列的棋盤(Game Board)
  • 棋盤上會有一些「棋子」(Game Piece),每個方格上只能放一個棋子(Game Piece)。
  • 遊戲初始化時,棋盤的上面四格和下面四格分別有4個黑子和四格白子
  • 玩家能夠經過話筒使棋子在棋盤上發生移動,從而觸發吃子和勝利等事件。
  • 當遊戲中出現一個橫行或者一個豎列的棋子排布變爲兩黑一白或者兩白一黑時,便可吃掉一子。
  • 當一方(黑或白)的棋子被吃到只剩一個以後,這一方被記爲失敗

舉個例子,下面這幅圖即爲遊戲過程當中的一幅圖。在下面的遊戲過程當中,位於(1,0)位置的黑子向左移動到(0,0)的位置後便可吃掉白子。

##Cocos2d-x提供的工具

Cocos2d-x有這樣一些主要的類,CCSprite,CCLayer,CCScene,CCNotificationCenter。咱們會使用這些類進行遊戲中MVC架構的搭建,若是你對這些類的做用不熟,請參考個人這篇博文【Cocos2d-x-基礎概念】Director Scene Layer and Sprite

咱們通常的遊戲流程是

  • 經過AppDelegate初始化第一個CCScene。
  • 在第一個CCScene建立多個CCLayer。並控制好CClayer的疊加層次(zOrder)。
  • 在CCLayer中添加各類CCSprite或者CCLabelAtlas或者粒子效果。
  • 在CCLayer層註冊觸摸事件的監聽,而且在CCLayer的實現中寫出相應的callback函數,對CCLayer的Child Node進行相應的邏輯處理。

這個過程看起來十分簡單,而且能夠十分快速地作出遊戲。可是其缺陷就在於在CCLayer中咱們作了太多的事情。CCLayer同時承擔了邏輯層和表示層的任務。不符合咱們上文中提到的關注點分離的原則。若是遊戲中有較爲複雜的狀態轉換時就捉襟見肘了。

##項目的文件目錄

下面是該遊戲項目的目錄結構,咱們接下來對這幾個文件夾進行分別的講解:

下面是該項目的一個簡單的類圖

在類圖中,虛線表明的是經過消息機制進行溝通,而在Cocos2d-x中,這種溝通是經過CCNotificationCenter來實現的。

##Model(模型)

Model在遊戲中表明的是消息驅動的有限狀態機,Model會接受Controller層發送的消息,並根據消息來更改本身的內部數據,而後把內部數據改變這一消息發送給View,通知它更新。

Model在Cocos2d-x對應的是哪一個類呢?

很遺憾,可是Cocos2d-x並無提供狀態機的feature,因此咱們須要本身實現一個Model類,在Model類中,須要本身實現諸如狀態轉換和消息處理等功能。例如在個人Model類中,我提供了以下接口。

<!-- lang: cpp -->
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();
};

注意如下幾點:

  • 讓Model類繼承CCObject,是爲了與Cocos2d-x自身的內存管理系統一致。
  • onMessage中,咱們作的事情就是首先找到當前狀態在msg狀況下的狀態轉換。在狀態轉換後,經過CCNotificationCenter發送一條消息通知遊戲中的其餘組件更新邏輯。
  • waitAction中,咱們是在處理一種異步的狀態轉換,好比一個棋子在移動時就會經歷這樣的狀態轉換start->moving->end,那麼在移動結束後,View就要發送一條相似END_MOVE的消息來通知Model更新本身的狀態。

Model不會持有View,因此View都是經過消息來得到Model更新的事件的。

咱們在編寫遊戲時,應該先編寫Model,而後經過測試來保證Model的正確性。

以後,咱們去寫View的時候,實際就是對Model這個狀態機發送的各類消息的回調函數的編寫了。

這樣就講表示層和邏輯層分離開,能夠方便地單獨測試每一個模塊,可維護性大大提升。

###邏輯數據和實際數據

在編寫Model的時候,咱們常常會遇到這樣的問題,就是Model中的數據是否應當與View中的數據保持一致。

好比:在View層的棋子的位置信息是否應該和Model層的位置信息保持一致?

其實,真實狀況就是,這要看Model層的 計算使用那種數據形式更加方便,好比在Model中,咱們會把棋盤轉化爲一個二維數組,這樣在AI運算,邏輯判斷時,更加有利,因此棋子的邏輯位置和實際位置必然是不一樣的。

再好比,不少時候遊戲中的物理引擎的計量單位是釐米,米。而不是OpenGL中的座標。這也是爲了邏輯運算的方便。

可是有些數值,咱們會讓它保持一致,好比人物的屬性等等。

##View(視圖)

View在遊戲中表明的是Model消息的接受者,在Cocos2d-x中,View通常是指CCLayer的子節點,即CCSprite,CCLabelAtlas,CCMenuItem,Particle System(粒子系統)等等。

它們會重載onEnter函數,在onEnter中註冊本身對某個Message的監聽。同時在onExit函數中將全部監聽清除。(清除監聽很重要,不然會出現很可怕的野指針問題)。

##Controller(控制器)

Controller在遊戲中負責將用戶觸摸事件轉化爲邏輯事件(即咱們上文中所說的消息),同時要對用戶觸摸事件中的信息進行正規化,而且通知變動。

Controller在Cocos2d-x中通常用CCLayer 來實現,由於CCLayer天然地繼承了CCTouchDelegate這一接口,自己就能夠觸摸事件,因此我在這個遊戲中全部的XXXController都是繼承自CCLayer。

Controller另外一個很重要的職責,就是建立View和Model,由於Controller至關於Model和View的一箇中間層,因此天然Controller會同時持有View和Model的應用,來方便地傳遞數據。

##Protocol(協議)

Protocol中定義了一些Model,View和Controller中共享的數據,

  • Message.h中,就定義了遊戲中各類類型的消息,
  • Tag.h中,就定義了遊戲中一些Node的Tag,其實Tag這個定義不是很準確,其實這裏的Tag指的是惟一標識CCNode的一個ID。
  • ChessboardProrocol.h中,定義了遊戲中期盤的寬,搞和一些經常使用的數據結構。

##參考文獻

相關文章
相關標籤/搜索