A short description on the MVC pattern
- The Controller sends a command to the model to update the model's state. It can also send commands to it's associated view to change the views's presentation of the model.
- The Model notifies it's associated views and controllers when there has been a change in the state. This notification allows the views to produce an updated output and the controllers to change the available set of commands.
- The View requests information from the model that it needs for generating an output representation to the user.
In this scene, we have just the three "game objects", a background, a ball and a collision animation. These objects are considered as model. Background is simple, it does not have any states or actions. This just combines with the ball and the collision object to present to the view.
cocos2d-x class CCLAYER acts as a controller here. When the user interacts with the screen, it sends a message to the controller, which in return sends a command to the model according to either instructions from the user, or planned game steps (like bounce the ball).
Model: The Ball and the Collision class contains information about themselves. It has no idea what is happening in the controller class. For example, the ball knows how to bounce and it will do so when asked. In the MVC pattern, model cannot access the controller directly, but it can inform the controller that it has finished the task that it was suppose to do. The way this can be achieved is through a delegate control.
In this example, when the ball finishes bouncing, the view needs to show a collision animation. The ball informs the game layer class through a delegate control that it has finished bouncing, and therefore the layer requests the collision class to animate at the same time.
The game layer requests the ball to bounce
CPP code:
Code: Select all
Ball* ball = Ball::create();
ball->setDelegate(this);
ball->initWithSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("ball_1.png"));
ball->setPosition(spawnLocation);
ball->setInitialPosition(spawnLocation);
ball->changeState(kStateBouncing);
_sceneSpriteBatchNode->addChild(ball, ZValue, kBallTagValue);
CPP code:
Code: Select all
CCJumpTo *jumpTo = CCJumpTo::create(1.5f, this->getInitialPosition(), this->getPositionY() + getScreenSize().height/2, 1.0);
CCSequence *sequence = CCSequence::create(jumpTo, CCCallFuncN::create(this, callfuncN_selector(Ball::bounceMe)) ,NULL);
this->runAction(sequence);
CPP code:
Code: Select all
this->getDelegate()->touchGround(this->getPosition());
The game layer knows that the ball has finished bouncing and then it requests the collision to animate at the position where the ball had finished bouncing
CPP code:
Code: Select all
void GamePlayLayer::touchGround(CCPoint startPosition){
Collision *collision = Collision::create();
collision->initWithSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName("collision_1.png"));
collision->setPosition(startPosition);
collision->changeState(kStateTapped);
_sceneSpriteBatchNode->addChild(collision, ZValue, kBallTagValue);
}
CPP code:
Code: Select all
CCSequence *sequence = CCSequence::create(CCAnimate::create(getAnim()), CCCallFuncN::create(this, callfuncN_selector(Collision::removeAndClean)) ,NULL);//finish animation and remove the sprite.
this->runAction(sequence);
CPP code:
Code: Select all
void GamePlayLayer::ccTouchesBegan(CCSet *touches, CCEvent *event){
CCTouch* touch = (CCTouch*)touches->anyObject();
Ball* ball = (Ball*)_sceneSpriteBatchNode->getChildByTag(kObjectTypeBall);
if (ball != NULL && ball->boundingBox().containsPoint(touch->getLocation())) {
ball->stopAllActions();
ball->changeState(kStateTapped);
this->createObjectOfType(kObjectTypeBall, 5, ccp(getScreenSize().width/2,getScreenSize().height/8.0), kZValue);
}
}
This solution contains a rough implementation of the MVC pattern for the cocos2d-x framework. In the start it looks like we have complicated things, but trust me, when the project gets bigger and you have 100's of sprites to manage, coding becomes very simple and manageable if you follow 'a' pattern (not just necessarily this one). This pattern is working great for me and I have fully implemented this in my upcoming Game 'Masato'.
Please comment below and let me know your thoughts around it. There is always room for improvements.
Here is a video to demonstrate the end result.