In the first part of the cocos2d meets MVC series I described how to start implementing MVC paradigm for cocos2d based board game. The first post only scratched the surface, we have prepared a skeleton but we still have a lot to do.
Introducing model
In the previous part we introduced the View and the Controller. To comply with MVC paradigm we need to add a model which will represent the current state of a game board. Our implementation now should contain the following classes:
- GameBoardView - the View,
- GameBoardController - the Controller.
- GameBoard – the Model.
Model implementation
GameBoard implementation
Our requirement (described in the first part) is that:
…A game board is defined by a number of rows and columns, which may vary for different application levels.
We implement it in the following way:
@interface GameBoard : NSObject { NSInteger numberOfRows; NSInteger numberOfColumns; } - (id)initWithRows:(NSInteger)aNumberOfRows columns:(NSInteger)aNumberOfColumns; @property (nonatomic) NSInteger numberOfRows; @property (nonatomic) NSInteger numberOfColumns; @end
Please notice that the model extends NSObject – the model should contain just the state of a game board (and the methods to update this state) – we shouldn’t put any view related concerns here.
GameBoardView implementation
We need to modify the View and pass it the Model, we do this in initWithGameBoard method.
@interface GameBoardView : CCNode { GameBoard *gameBoard; } @property (nonatomic, retain) GameBoard *gameBoard; - (id)initWithGameBoard:(GameBoard *)aGameBoard; @end
Our implementation of a GameBoardView can look like this (rendering game board spaces omitted to simplify the code):
- (id)initWithGameBoard:(GameBoard *)aGameBoard { if ((self = [super init])) { // retain gameboard self.gameBoard = aGameBoard; // render gameboard background CCSprite *gameboardSprite = [CCSprite spriteWithFile:@"gameboard.png"]; gameboardSprite.anchorPoint = CGPointMake(0, 0); [self addChild:gameboardSprite]; // render spaces for (int i = 0; i < gameBoard.numberOfRows; i++) { for (int j = 0; j < gameBoard.numberOfColumns; j++) { // position and render game board spaces } } } return self; }
GameBoardViewController
Finally the implementation of an init method in the Controller should be updated – the View needs to have the GameBoard model injected in its init method so we construct the game board in Controller (just for now) and pass it to the View (later on we will create a game board based on a level definition).
- (id)init { if ((self = [super init])) { // initialize model gameBoard = [[GameBoard alloc] initWithRows:7 columns:9]; // initialize view view = [[GameBoardView alloc] initWithGameBoard:gameBoard]; [self addChild:view]; } return self; }
Handling touches
GameBoardView updates
In order to handle touches we need to slightly modify the View. We make it extend CCLayer instead of CCNode to have a possibility to handle touch events inside of it:
@interface GameBoardView : CCLayer { ... }
The View itself should not be responsible for handling the actions that are results of user interaction with the game board so we will pass it to the Controller using protocol.
@protocol GameBoardViewDelegate - (void)gameBoard:(GameBoard *)gameBoard touchedAtRow:(int)row column:(int)column; @end
We also modify the init method of a GameBoardView so we can pass a delegate object that will be responsible for handling the touches:
- (id)initWithGameBoard:(GameBoard *)aGameBoard delegate:(id)aDelegate;
And the implementation is:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { UITouch *touch = [touches anyObject]; CGPoint point = [self convertTouchToNodeSpace:touch]; // calculate row and column touched by the user and call a delegate method // ... [self.delegate gameBoard:self.gameBoard touchedAtRow:row column:column]; }
GameBoardController updates
GameBoardController will be responsible for handling user touches, so we need to implement a GameBoardViewDelegate in a GameBoardController:
@interface GameBoardController : CCNode
- (void)gameBoard:(GameBoard *)gameBoard touchedAtRow:(int)row column:(int)column { // do the game logic here and update view accordingly }
The last step is to modify the View initialization in the following way (in init method):
// initialize view view = [[GameBoardView alloc] initWithGameBoard:gameBoard delegate:self];
Summary
In this part we implemented the Model and we did the necessary steps to make it visible to the View and to Controller. We also setup an indirect link between the View and the Controller so if the user touches the space on a game board the Controller will be able to handle the action according to game rules. The next part will cover:
- Updating the Model inside the Controller,
- Notifying the View about the changes in the Model.
If you have any questions or remarks feel free to leave a comment after this post.
没有评论:
发表评论