Chapter 7 - Some Icing on the Cake¶
The simple game is completed basically, but we could add some adornments to make the game seem more professional.
In this chapter, we will add a new scene. When a certain quantity of monsters are killed, show “You Win” on the screen, and when there is a monster runs out of the left of the screen, show “You Lose”.
Now create two files GameOverScene.cpp and GameOverScene.h in the Classes directory.
For GameOverScene.h:
1#ifndef _GAME_OVER_SCENE_H_
2#define _GAME_OVER_SCENE_H_
3
4#include "cocos2d.h"
5
6class GameOverLayer : public cocos2d::CCLayerColor
7{
8public:
9 GameOverLayer():_label(NULL) {};
10 virtual ~GameOverLayer();
11 bool init();
12 CREATE_FUNC(GameOverLayer);
13
14 void gameOverDone();
15
16 CC_SYNTHESIZE_READONLY(cocos2d::CCLabelTTF*, _label, Label);
17};
18
19class GameOverScene : public cocos2d::CCScene
20{
21public:
22 GameOverScene():_layer(NULL) {};
23 ~GameOverScene();
24 bool init();
25 CREATE_FUNC(GameOverScene);
26
27 CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer);
28};
29
30#endif // _GAME_OVER_SCENE_H_
Port Tips:
1.The class member functions could be realized in the .m file without declaration in the header file, but it is illegal in c++, so there is bool init(); in GameOverScene.h.
2. The function node() is convenient for developers because of integration of new, init, and autorelease, etc.. But there is no keyword like self(objc) in c++, so CCLayer::node() and CCScene::node() should be realized in their derived classes. The realization of node() is similar, we construct two macros to make the realization more essier: LAYER_NODE_FUNC and
SCENE_NODE_FUNC. To use the two macros, init() must be realized in the derived classes.
3. About constructors and init(). Cocos2d-x doesn’t port init() of objc to the constructor in c++ directly, because there is no return value in the c++ constructor, in this case, we have to handle exception with try-catch, but try-catch is not supported on android SDK. So, cocos2d-x initialize the classed in two stages, firstly calling the constructor and then doing initialization in init(). This method is also adopted in the interface design of iOS, for example, [[NSString alloc] init], and the utilization of c++ class in Samsung bada.
4. The setter and getter of _label and _layer are realized in @synthesize in objc, and we have constructed some macros in cocos2dx\include\Cocos2dDefine.h to simulate @property and @synthesize. In the codes above, CCX_SYNTHESIZE_READONLY defines the read-only member variable with only getter but not setter. In c++, the inline function can only be defined in the header file, so @synthesize is realized in the header file naturally.
For GameOverScene.cpp:
1// cpp with cocos2d-x
2#include "GameOverScene.h"
3#include "HelloWorldScene.h"
4
5using namespace cocos2d;
6
7bool GameOverScene::init()
8{
9 if( CCScene::init() )
10 {
11 this->_layer = GameOverLayer::create();
12 this->_layer->retain();
13 this->addChild(_layer);
14
15 return true;
16 }
17 else
18 {
19 return false;
20 }
21}
22
23GameOverScene::~GameOverScene()
24{
25 if (_layer)
26 {
27 _layer->release();
28 _layer = NULL;
29 }
30}
31
32bool GameOverLayer::init()
33{
34 if ( CCLayerColor::initWithColor( ccc4(255,255,255,255) ) )
35 {
36 CCSize winSize = CCDirector::sharedDirector()->getWinSize();
37 this->_label = CCLabelTTF::create("","Artial", 32);
38 _label->retain();
39 _label->setColor( ccc3(0, 0, 0) );
40 _label->setPosition(ccp(winSize.width/2, winSize.height/2));
41 this->addChild(_label);
42
43 this->runAction( CCSequence::create(
44 CCDelayTime::create(3),
45 CCCallFunc::create(this,
46 callfunc_selector(GameOverLayer::gameOverDone)),
47 NULL));
48
49 return true;
50 }
51 else
52 {
53 return false;
54 }
55}
56
57void GameOverLayer::gameOverDone()
58{
59 CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());
60}
61
62GameOverLayer::~GameOverLayer()
63{
64 if (_label)
65 {
66 _label->release();
67 _label = NULL;
68 }
69}
There are two objects in GameOverScene.cpp, one scene and one layer. A scene can involve several layers. In this game, there is only one layer, and in its center, a label with the words “You Win” or “You Lose” will be showed for three seconds.
Port Tips
1. Pay attention to GameOverLayer._label and GameOverScene._layer, they are declared in objc with @property (nonatomic, retain), it means that they are retained, so they should be released in dealloc. Similarly, there is retain() in init() of GameOverLayer and GameOverScene, and release() should be called in ~GameOverLayer() and ~GameOverScene() respectively.
2. NSAutoReleasePool is also ported in cocos2d-x. This Garbage Collector is good for c++ programming, and the utility is the same as in iOS, more information refers to http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html.
In cocos2d-x, we should call release() in two cases:
• The object is newed by ourself, for example, CCSprite *sprite = new CCSprite();
• The object is created by a static function, for example, CCSprite *sprite = CCSprite::spriteWithFile(...), in this case, we don’t need to release, but when sprite->retain() is called, then sprite->release() should be called too.
Then return to the issues, the GameOverScene should be called in some conditions: a certain number of monsters are killed or there is one monster escaped.
We add a variable in HelloWorldScene to count how many monsters are killed by the hero.
1 // cpp with cocos2d-x
2 protected:
3 int _projectilesDestroyed;
And Initialize it in HelloWorld::HelloWorld(),
|
Include GameOverScene.h in HelloWorldScene.cpp,
1// cpp with cocos2d-x
2#include "GameOverScene.h"
After removeChild(target) in the targetsToDelete for loop of HelloWorld::update(), add the codes below to check the win condition.
1// cpp with cocos2d-x
2_projectilesDestroyed++;
3if (_projectilesDestroyed >= 5)
4{
5 GameOverScene *gameOverScene = GameOverScene::create();
6 gameOverScene->getLayer()->getLabel()->setString("You Win!");
7 CCDirector::sharedDirector()->replaceScene(gameOverScene);
8}
Add the codes below to check the failure condition in the “if (sprite->getTag() == 1)” conditional of spriteMoveFinished(),
1// cpp with cocos2d-x
2GameOverScene *gameOverScene = GameOverScene::create();
3gameOverScene->getLayer()->getLabel()->setString("You Lose :[");
4CCDirector::sharedDirector()->replaceScene(gameOverScene);
Now, everything is ready, compile and run, all kinds of effects will be showed, monsters everywhere, bullets everywhere, high background music and a prompt when you win or lose.
The whole game is completed now, cheers!
iPhone
android
win32