在这一章里,我们将会添加新的场景。当你干掉一定数量的怪物时,在屏幕上显示“You Win”,而当有怪物逃出屏幕左侧时,显示“You Lose”。 下面我们在类目录里新建两个文件,GameOverScene.cpp 和GameOverScene.h。 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 LAYER_NODE_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 SCENE_NODE_FUNC(GameOverScene); 26 27 CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer); 28}; 29 30#endif // _GAME_OVER_SCENE_H_ 1#import "cocos2d.h" 2@interface GameOverLayer : CCLayerColor 3{ 4 CCLabel *_label; 5} 6 7@property (nonatomic, retain) CCLabel *label; 8@end 9 10@interface GameOverScene : CCScene 11{ 12 GameOverLayer *_layer; 13} 14@property (nonatomic, retain) GameOverLayer *layer; 15@end 转换要点: 1. 在objc的头文件中,可以不声明类成员函数,而直接在.m文件里实现。cpp不允许这样做。所以我们会多个bool init(); 2. 由于cpp里没有self这种强大的关键字,所以CCLayer::node()和CCScene::node()方法的都需要派生类自己实现一份,不能像objc那样直接从父类继承下来靠self关键字变成指向自己的对象。node()方法很方便,集合了new,init,autorelease等方法,可以减少调用者的代码量。但由于每份node方法的代码都类似,我们就做了两个宏来方便大家 LAYER_NODE_FUNC和SCENE_NODE_FUNC. 如果想使用这两个宏,就必须在派生类里实现bool init()方法。 3. 关于构造函数和init方法。cocos2d-x在从objc改写为cpp时,并不是直接把init的内容翻到C++构造函数里面,主要出于这样的考虑:C++构造函数有个天生缺陷——没有返回值。这就导致C++构造函数依赖try-catch来捕捉逻辑异常。而一般try-catch用的人不多,开启try-catch支持会使编译后的二进制程序增加不少体积,而且android NDK上也是彻底不支持try-catch。所以我们采取现在比较流行的“二阶段构造”的方法,即使用时先调构造函数,再调用init处理初始化逻辑。这种思路不论是在苹果iOS的接口设计(比如[[NSString alloc] init],即二阶段构造)、还是在samsung bada操作系统使用C++类时都是如此。 4. objc中的@synthesize实现了_label和_layer两个属性的具体setter和getter。我们在cocos2dx\include\Cocos2dDefine.h中实现了一系列的宏定义,来模仿实现@property和@synthesize的功能。在上面代码中,我们用CCX_SYNTHESIZE_READONLY宏来实现了只读的类成员变量,只有getter没有setter。由于VC++的规则是inline函数只能在头文件里实现,所以@synthesize就从objc的.m文件里移动到cpp的.h文件里,和成员变量声明一并实现了 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::node(); 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::labelWithString("","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::actions( 44 CCDelayTime::actionWithDuration(3), 45 CCCallFunc::actionWithTarget(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} 1// objc with cocos2d-iphone 2#import "GameOverScene.h" 3#import "HelloWorldScene.h" 4 5@implementation GameOverScene 6@synthesize layer = _layer; 7 8- (id)init 9{ 10 if ((self = [super init])) 11 { 12 self.layer = [GameOverLayer node]; 13 [self addChild:_layer]; 14 } 15 return self; 16} 17 18- (void)dealloc 19{ 20 [_layer release]; 21 _layer = nil; 22 [super dealloc]; 23} 24 25@end 26@implementation GameOverLayer 27@synthesize label = _label; 28 29-(id) init 30{ 31 if( (self=[super initWithColor:ccc4(255,255,255,255)] )) 32 { 33 CGSize winSize = [[CCDirector sharedDirector] winSize]; 34 self.label = [CCLabel 35 labelWithString"" fontName"Arial" fontSize:32]; 36 37 _label.color = ccc3(0,0,0); 38 _label.position = ccp(winSize.width/2, winSize.height/2); 39 [self addChild:_label]; 40 41 [self runAction:[CCSequence actions: 42 [CCDelayTime actionWithDuration:3], 43 [CCCallFunc actionWithTarget:self 44 selectorselector(gameOverDone)], 45 nil]]; 46 } 47 return self; 48} 49 50- (void)gameOverDone 51{ 52 [[CCDirector sharedDirector] 53 replaceScene:[HelloWorld scene]]; 54} 55 56- (void)dealloc 57{ 58 [_label release]; 59 _label = nil; 60 [super dealloc]; 61} 62 63@end 注意,上面GameOverScene.cpp里有两个对象,一个场景(scene)和一个图层(layer),场景可以包含多个图层,而这个图层只在屏幕正中间放了一个文字标签(label),显示3秒种后返回到HelloWorldScene中。 转换要点 1. 再次注意GameOverLayer._label和GameOverScene._layer两个属性。这两个属性在objc的头文件里被声明为@property (nonatomic, retain),也就是被retain了一次,所以在dealloc里才要调用release方法。同样地,我们在~GameOverLayer()和~GameOverScene()析构函数里分别release()了这两个属性,但这个release需要和一个retain对应,所以在两个init方法里都分别添加了_label->retain()和_layer->retain(); 简而言之就是,在使用cocos2d-x中继承自NSObject类的对象指针时,以下两种情况是需要用户多调一个release  类对象是用户自己new出来的。比如CCSprite *sprite = new CCSprite();  类对象是通过某个静态函数建立并返回的,比如CCSprite *sprite = CCSprite::spriteWithFile(...),这种情况不需要用户release;但如果你接着调用了sprite->retain(), 那么就需要一个sprite->release()对应 之后回到问题上来,GameOverScene应该在某些条件下被调用:一定数量的怪物被干掉或者有怪物跳掉了。 我们在HelloWorldScene里加入一个变量,用来计算英雄杀掉了多少个怪物。 11 // cpp with cocos2d-x 22 protected: 33 int _projectilesDestroyed; 11 // objc with cocos2d-iphone 22 33 int _projectilesDestroyed; 并在HelloWorld::HelloWorld()中初始化它, 1 // cpp with cocos2d-x 2_projectilesDestroyed = 0; 在HelloWorldScene.cpp中包含GameOverScene.h 1// cpp with cocos2d-x 2#include "GameOverScene.h" 1// objc with cocos2d-iphone 2#import "GameOverScene.h" 在HelloWorld::update方法中的removeChild(target)后面的targetsToDelete循环中增加计数并检查获胜条件,获胜了就显示"You Win!"界面 1// cpp with cocos2d-x 2_projectilesDestroyed++; 3if (_projectilesDestroyed > 30) 4{ 5 GameOverScene *gameOverScene = GameOverScene::node(); 6 gameOverScene->getLayer()->getLabel()->setString("You Win!"); 7 CCDirector::sharedDirector()->replaceScene(gameOverScene); 8} 1 2// objc with cocos2d-iphone 3_projectilesDestroyed++; 4if (_projectilesDestroyed > 30) 5{ 6 GameOverScene *gameOverScene = [GameOverScene node]; 7 [gameOverScene.layer.label setString"You Win!"]; 8 [[CCDirector sharedDirector] replaceScene:gameOverScene]; 9} 与之匹配的是失败条件:任何一个怪物穿越了屏幕的最左边,你就挂了。于是修改spriteMoveFinished方法,在if (sprite->getTag() == 1)条件里面增加“You Lose”的代码: 1// cpp with cocos2d-x 2GameOverScene *gameOverScene = GameOverScene::node(); 3gameOverScene->getLayer()->getLabel()->setString("You Lose :["); 4CCDirector::sharedDirector()->replaceScene(gameOverScene); 1// objc with cocos2d-iphone 2GameOverScene *gameOverScene = [GameOverScene node]; 3[gameOverScene.layer.label setString"You Lose :["]; 4[[CCDirector sharedDirector] replaceScene:gameOverScene]; 现在,万事俱备,请编译并运行,所有类型的效果都会显示出来,怪物、子弹满屏飞,很H的背景音乐,并在你输或赢时显示一个提示界面。 整个游戏现在已经全部完成了,恭喜! iPhone android win32 wophone |