1. 程式人生 > >Cocos2d-x裡面如何實現MVC(四)

Cocos2d-x裡面如何實現MVC(四)

    在上一篇文章中,我們使用cocos2d-x基於mvc做了一個簡單了遊戲架子,這個架子還非常簡單,還有許多東西有待實現。

介紹模型

    在上一篇博文中,我們介紹了view和controller。為了實現mvc模式,我們還需要新增一個model類來維護遊戲的狀態。我們的實現應該要包含下列這些類:

1 GameBoardView - 也就是View,

2 GameBoardController - 也就是Controller.

3 GameBoard – 也就是Model.


Model 實現

GameBoard 實現

    我們在第一部分所描述的需求是這樣子的:

    。。。一個game board 是通過n行n列組成的,它會隨著遊戲難度有所變化。

    因此,我們按照下面的編碼方式來實現之:

Class GameBoard : public CCObject {
public:
    int numberOfRows;
    int numberOfColums;
 
    void initGridSize(int aNumberOfRows, int aNumberOfColumns){
        this->numberOfColumns = aNumberOfColumns;
        this->numberOfRows = aNumberOfRows;
    }
}

    請注意,model是從CCbject繼承過來的---因為model只需要關注game board model的狀態就行了(當然,還有相應的更新狀態的方法)---我們不應該把其它東西也放進來,比如繼承到CCNode就不行,我們並不需要CCNode的東西,所以,為了純粹性,我們這裡繼承到game board 。

GameBoardView 的實現

    我們現在需要修改View,同時它包含一個model的引用,我們可以通過initWithGameBoard方法來初始化這個成員變數:

class GameBoardView :: public CCLayer {
public:
    GameBoard *gameBoard;
    void initWithGameBoardModel(GameBoard *aGameBoard);
}
   具體GameBoardView的實現細節如下:(為了演示方便,我們忽略了實際渲染每一個小方塊的程式碼)
void GameBoardView::initWithGameBoard(GameBoard *aGameBoard)
{
    if(gameBoard != NULL){
        gameBoard->release();
        gameBoard = NULL;
    }
 
    this->gameBoard = aGameBoard;
    this->gameBoard->retain();
 
    // render gameBoard background
    CCSprite *gameBoardSprite = CCSprite::spriteWithFile("gameBoard.png");
    gameBoardSprite->setAnchorPoint(ccp(0, 0));
    this->addChild(gameBoardSprite);
 
    for(int i=0; i<gameBoard->numberOfRows; i++)
        for (int j=0; j<gameBoard->numberOfColumns; j++)
        {
            // position and render game board spacse
        }
}

GameBoardController

    最後,我們要更新GameBoardController的init方法,因為view需要把GameBoard物件通過init方法注入進去,所以,我們在controller的init方法裡面,就應該定義好model物件,然後傳遞給view。

virtual bool init(){
    bool bRet = false;
    do{
        // 先呼叫超類的init方法
        CC_BREAK_IF(! CCLayer::init());
 
        GameBoard *gameBoard = new GameBoard();
        gameBoard->initGridSize(7, 9);
        view = GameBoardView::node();
        view->initWithGameBoard(gameBoard);
        this->addChild(view, 0);
 
        bRet = true;
    }while(0)
 
    beturn bRet;
}

處理touch事件

GameBoardView updates

    我們的View雖然繼承了CCLayer,但view本身是不應該處理使用者的互動(touch事件)的,所以,我們需要定義一個代理(GameBoardViewDelegate)。(譯者:為什麼這裡要定義代理呢?所謂代理代理,當然就是你不想做的事,找別人去做,這就是代理。所以,當你寫程式碼的時候,你想保持類的簡單性、重用性,你就可以把事件儘量都交給其它類去做,自己只管做好自己的事。也就是SRP,單一職責原則。如果一個類關注的點過多,做的事情太多。這些事情不管是你直接做的,還是呼叫別的物件去完成的。這都不行,自己做這些事,那就會使類的功能複雜化,維護不方便。而過多地呼叫其它物件來完成一些事情,表面上看起來好像不錯,實際上是過度耦合了。我們編寫類的原則應該是追求高內聚,低耦合的。可能你會說,用代理不也是交給別人做嗎?沒錯,問的好。但是,代理是介面,我們是針對介面程式設計,所以它的重用性會非常好。因此,下次當你想寫可擴充套件和可重用的程式碼的時候,不妨先想想代理這個東西吧。C++和java者是用介面實現的,而objc裡面delegate是用protocol實現的,具體怎麼轉換的,比較一下應該就可以了。)

class GameBoardViewDelegate
{
public:
    virtual void dealWithTouchesBegan(GameBoard *gameBoard, int row, int column) = 0;
};
    我們還需要再修改一下GameBoardView的init方法,通過傳送一個delegate進來處理touch事件。
void initWithGameBoard(GameBoard *aGameBoard, GameBoardViewDelegate *aDelegate);
下面是touch事件的具體實現,記得先在初始化時setIsTouchEnable(true)哦^_^:
void GameBoardView::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{
    CCTouch *touch = (CCTouch*) pTouches->anyObject();
    if(touch == NULL)
        return;
 
    // 置換座標,等效如下
    //CCPoint location = touch->locationInView(touch->view());
    //location = CCDirector::sharedDirector()->convertToGL(location);
    CCPoint point = this->convertTouchToNodeSpace(touch);
 
    // calculate row and column touched by the user and call a delegate method
    int row = 0;
    int column = 0;
 
    // ...
    this->gameBoardViewDelegate->dealWithTouchesBegan(gameBoard, row, column);
}

GameBoardController 更新

    GameBoardController將會負責處理使用者touch事件,所以,我們需要讓GameBoardController實現GameBoardViewDelegate介面:

class GameBoardController :
    public CCLayer, public GameBoardViewDelegate
{
public:
    //...
    virtual void dealWithTouchesBegan(GameBoard *gameBoard, int row, int column){
        // do the game logic here and update view accordingly
    }
    // ...
};
   還有最後一步,那就是修改view的init方法,把controller傳遞進去。
// initialize view
view->initWithGameBoard(gameBoard, this);

總結

    在這篇文章中,我們實現了Model,同時還通過一些程式碼把view和controller聯絡起來了。同時,我們還在view和controller,以及使用者touch事件之間建立了聯絡,這樣controller類就可以來響應遊戲裡面的使用者輸入了。(譯者:為什麼費這麼多勁,無非就是職責分離,這個非常重要!)。在接下來的文章裡面,我們會談到下面兩個問題:

· 在Controller裡面更新Model,

· 通知View關於Model的改變.