本文基於前面兩篇文章,如果您還沒有看過,建議先閱讀下面兩篇文章:
更新Model
當用戶從工具箱中選一個小工具,然後把它放置到game board上面去時,我們需要編碼響應這些事件。在上一篇文章中,我們已經實現了GameBoardViewDelegate的touchedAtRow方法。我們還需要給這個協議再添加一個接口方法。如下所示:
class GameBoardViewDelegate
{
public:
virtual void touchAtGrid(GameBoard *gameBoard, int row, int column) = 0;
virtual void touchWithToolBoxItemAtIndex(GameBoard *gameBoard, int index) = 0;
};
我們需要修改touch事件處理器,這樣就可以判斷我們到底是觸摸了工具箱還是game board。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);
// 下面假設遊戲區與工具區8:2來劃分寬度
CCSize size = CCDirector::sharedDirector()->getWinSize();
CCRect *gameBoardRectangle = new CCRect(0, 0, size.width*0.8, size.height);
CCRect *toolBoxRectangle = new CCRect(size.width*0.8, 0, size.width*0.2, size.height);
if(CCRect::CCRectContainsPoint(*gameBoardRectangle, point)){
// calculate row and column touched by the user and call a delegate method
int row = 0;
int column = 0;
// ...
this->gameBoardViewDelegate->touchAtGrid(gameBoard, row, column);
} else if (CCRect::CCRectContainsPoint(*toolBoxRectangle, point)){
int index = 0;
// calculate toolbox item index based on a touch coordinate
this->gameBoardViewDelegate->touchWithToolBoxItemAtIndex(gameBoard, index);
}
}
在controller類裏面處理touch事件是非常簡單的,我們只需要持有一個model的引用,然後基於touch事件來調用model的方法就行了。我們的接口看起來和下面差不多,只是省略掉了一些實現細節:
class GameBoard : public CCObject
{
public:
// ...
GamePiece* getGamePieceFromToolBoxItemAtIndex(int index);
public:
// ...
int selectedToolBoxItemIndex;
};
然後,我們在GameBoardController裏面完全實現GameBoardViewDelegate的兩個方法。void GameBoardController::touchAtGrid(GameBoard *gameBoard, int row, int column)
{
// if the toolbox item is selected move item from toolbox to game board
if(gameBoard->selectedToolBoxItemIndex != -1){
GamePiece *gamePiece = gameBoard->getGamePieceFromToolBoxItemAtIndex(gameBoard->selectedToolBoxItemIndex);
gameBoard->putGamePiece(gamePiece, row, column);
}
}
void GameBoardController::touchWithToolBoxItemAtIndex(GameBoard *gameBoard, int index) {
// keep the toolbox selection state in the Model
gameBoard->selectedToolboxItemIndex = index;
}
到目前爲止,我們實現了,用戶可以點擊工具箱中的小工具,然後把它們放置到game board中的一個小方塊上面,同時model類在中間起了橋樑作用。
通知view關於model的改變
爲了在view裏面反映出model的狀態更改,我們可以在model有變化的時候給view發送通知消息,然後view就可以根據不同的消息來作出不同的響應了。和我們在實現view通過controller一樣,這裏我們也定義了一個GameBoardDelegate,用來通知view model的變化。
class GameBoardDelegate
{
public:
virtual void didPutGamePiece(GamePiece *gamePiece, int row, int column) = 0;
};
class GameBoard : public CCObject
{
// ...
public:
void setGameBoardDelegate(GameBoardDelegate *aGameBoardDelegate);
private:
GameBoardDelegate *gameBoardDelegate;
};
void GameBoard::putGamePiece(GamePiece *gamePiece, int row, int column)
{
// ...
// store game piece
// notify that the game piece was put on a gameboard
this->gameBoardDelegate->didPutGamePiece(gamePiece, row, column);
}
void GameBoard::setGameBoardDelegate(GameBoardDelegate *aGameBoardDelegate)
{
this->gameBoardDelegate = aGameBoardDelegate;
}
在GameBoardView裏面實現GameBoardDelegate的時候,當我們需要在game board上面放置一個小工具的時候,我們定義了一個CCSprite。class GameBoardView :
public CCLayer, public GameBoardDelegate
{
// ...
};
void GameBoardView::initWithGameBoard(GameBoard *aGameBoard, GameBoardViewDelegate *aDelegate)
{
// ...
this->gameBoard = aGameBoard;
this->gameBoard->retain();
this->gameBoard->setGameBoardDelegate(this);
this->gameBoardViewDelegate = aDelegate;
// ...
}
void GameBoardView::didPutGamePiece(GamePiece *gamePiece, int row, int column)
{
// create CCSprite and put it on a game board at corresponding position
CCSprite *gamePieceSprite = CCSprite::spriteWithFile("CloseNormal.png");
// ...
this->addChild(gamePieceSprite, 1);
}
總結
現在框架中所有的部分都聯繫起來了,model、view和controller三者組成了著名的MVC模式
· View接收touch事件,然後把事件傳遞給controller,
· Controller 響應用戶的touch事件,然後更新model
· model 更新它自身的狀態, 處理遊戲邏輯,然後告訴view它改變了哪些東西。
· View則基於Model當前的狀態來更新自己的顯示