Bartek Wilczynski bio photo

Bartek Wilczynski

IT consultant and entrepreneur with over 10 years of experience in a variety of medium-size line of business applications, mostly in .NET technology.

Email Twitter LinkedIn Youtube

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:

  1. GameBoardView - the View,
  2. GameBoardController - the Controller.
  3. GameBoard - the Model.

MVC diagram - introduced 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;
}

GameBoardController

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.