This is the 3rd part from the series how to build a simple puzzle game using cocos2d and MVC pattern. If you haven't read previous parts I suggest you do it before reading this post:
Updating the Model
When the user touches the toolbox item and then touches the space on a game board we want that the game piece represented by a toolbox item will be placed on a game board at touch position. In the previous part we already implemented touchedAtRow method in GameBoardViewDelegate. We will extend this protocol and add additional method for handling toolbox item selection.
@protocol GameBoardViewDelegate
- (void)gameBoard:(GameBoard *)gameBoard touchedAtRow:(int)row column:(int)column;
- (void)gameBoard:(GameBoard *)gameBoard toolboxItemTouchedAtIndex:(int)index;
@end
We need to change the implementation of our touches handler, so that we now detect whether we clicked on a toolbox or on a game board.
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint point = [self convertTouchToNodeSpace:touch];
// touched on a game board
if (CGRectContainsPoint(gameBoardRectangle, point)) {
int row, column;
// calculate row and column based on a touch coordinate
// ...
// call controller
[self.delegate gameBoard:self.gameBoard touchedAtRow:row column:column];
}
// touched on a toolbox
else if (CGRectContainsPoint(toolboxRectangle, point)) {
int index;
// calculate toolbox item index based on a touch coordinate
[self.delegate gameBoard:self.gameBoard toolboxItemTouchedAtIndex:index];
}
}
Handling events on the Controller side is easy, first of all we extend the Model to keep the state of our model and allows us to modify the state based on user touches. Our interface will look something like this (implementation details omitted for clarity):
@interface GameBoard : NSObject {
// ...
}
// ...
- (void)putGamePiece:(GamePiece *)gamePiece row:(int)row column:(int)column;
- (GamePiece *)getGamePieceFromToolboxItemAtIndex:(int)index;
@end
Then we finish the implementation of GameBoardViewDelegate in GameBoardController and handle our events accordingly.
- (void)gameBoard:(GameBoard *)aGameBoard toolboxItemTouchedAtIndex:(int)index {
// keep the toolbox selection state in the Model
gameBoard.selectedToolboxItemIndex = index;
}
- (void)gameBoard:(GameBoard *)aGameBoard touchedAtRow:(int)row column:(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:row column:column];
}
}
What we achieved so far is that when the user touches on of the toolbox item and then touches the space on a game board the Model gets updated reflecting the desired game board state.
Notifying the View about the changes in the Model
In order to reflect the state of the Model in the View we need to send some notifications so when the Model gets changed the View will be able to react upon it.
We do this the same way we notify the Controller about the View changes. We start from the delegate declaration inside the Model.
@protocol GameBoardDelegate;
@interface GameBoard : NSObject
// ...
@property (nonatomic, assign)
id<GameBoardDelegate> delegate;
// ...
@end
@protocol GameBoardDelegate
- (void)gameBoard:(GameBoard *)gameBoard didPutGamePiece:(GamePiece *)gamePiece row:(int)row column:(int)column;
@end
@implementation GameBoard
- (void)putGamePiece:(GamePiece *)gamePiece row:(int)row column:(int)column {
// ...
// store game piece
// notify that the game piece was put on a gameboard
[delegate gameBoard:self didPutGamePiece:gamePiece row:row column:column];
}
@end
The last part is the implementation of GameBoardDelegate in the GameBoardView so that when the game piece is put on a game board the View will put a CCSprite at a given position.
@interface GameBoardView : CCLayer
// ...
@end
@implementation GameBoardView
- (id)initWithGameBoard:(GameBoard *)aGameBoard delegate:(id)aDelegate {
if ((self = [super init])) {
// retain gameboard
self.gameBoard = aGameBoard;
self.gameBoard.delegate = self;
// assign delegate
self.delegate = aDelegate;
}
}
- (void)gameBoard:(GameBoard *)gameBoard didPutGamePiece:(GamePiece *)gamePiece row:(int)row column:(int)column {
// create CCSprite and put it on a game board at corresponding position
CCSprite *gamePieceSprite = [CCSprite spriteWithFile:fileName];
// ...
[self addChild:gamePieceSprite];
}
@end
Summary
All the pieces are now connected together. The interaction between the Model, the Controller and the View conforms to MVC paradigm:
- The View handles the touches and notifies the Controller,
- The Controller reacts upon the user touches and updates the Model,
- The model updates its state, does the logic and tells the View that it has been changed,
- The View updates itself based on the current Model state.