从cocos2d-iphone 到cocos2d-x的转变心得

来源:互联网 发布:python单例模式优缺点 编辑:程序博客网 时间:2024/06/06 19:48

#ifndef _GAME_OVER_SCENE_H_#define _GAME_OVER_SCENE_H_#include "cocos2d.h"class GameOverLayer : public cocos2d::CCLayerColor{public:GameOverLayer():_label(NULL) {}; virtual ~GameOverLayer(); bool init();LAYER_NODE_FUNC(GameOverLayer); void gameOverDone(); CC_SYNTHESIZE_READONLY(cocos2d::CCLabelTTF*, _label, Label);};class GameOverScene : public cocos2d::CCScene{public:GameOverScene():_layer(NULL) {}; ~GameOverScene();24 bool init();SCENE_NODE_FUNC(GameOverScene);CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer);};#endif // _GAME_OVER_SCENE_H_#import "cocos2d.h"@interface GameOverLayer : CCLayerColor{CCLabel *_label;}@property (nonatomic, retain) CCLabel *label;@end@interface GameOverScene : CCScene{ GameOverLayer *_layer;}@property (nonatomic, retain) GameOverLayer *layer;@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文件里,和成员变量声明一并实现了

// cpp with cocos2d-x#include "GameOverScene.h"#include "HelloWorldScene.h"using namespace cocos2d;bool GameOverScene::init(){if( CCScene::init() ){ this->_layer = GameOverLayer::node(); this->_layer->retain(); this->addChild(_layer);return true; } else { return false;}}GameOverScene::~GameOverScene(){ if (_layer) { _layer->release(); _layer = NULL; }}bool GameOverLayer::init(){ if ( CCLayerColor::initWithColor( ccc4(255,255,255,255) ) ) { CCSize winSize = CCDirector::sharedDirector()->getWinSize(); this->_label = CCLabelTTF::labelWithString("","Artial", 32); _label->retain(); _label->setColor( ccc3(0, 0, 0) ); _label->setPosition(ccp(winSize.width/2, winSize.height/2)); this->addChild(_label); this->runAction( CCSequence::actions( CCDelayTime::actionWithDuration(3), CCCallFunc::actionWithTarget(this, callfunc_selector(GameOverLayer::gameOverDone)), NULL)); return true; }else { return false; }}void GameOverLayer::gameOverDone(){ CCDirector::sharedDirector()->replaceScene(HelloWorld::scene());}GameOverLayer::~GameOverLayer(){ if (_label) { _label->release(); _label = NULL; }}// objc with cocos2d-iphone#import "GameOverScene.h"#import "HelloWorldScene.h"@implementation GameOverScene@synthesize layer = _layer;- (id)init{ if ((self = [super init])) { self.layer = [GameOverLayer node]; [self addChild:_layer]; } return self;}- (void)dealloc{ [_layer release]; _layer = nil; [super dealloc];}@end@implementation GameOverLayer@synthesize label = _label;-(id) init{if( (self=[super initWithColor:ccc4(255,255,255,255)] )) { CGSize winSize = [[CCDirector sharedDirector] winSize];self.label = [CCLabel labelWithString:@"" fontName:@"Arial" fontSize:32]; _label.color = ccc3(0,0,0); _label.position = ccp(winSize.width/2, winSize.height/2); [self addChild:_label]; [self runAction:[CCSequence actions: [CCDelayTime actionWithDuration:3], [CCCallFunc actionWithTarget:self selector:@selector(gameOverDone)], nil]]; } return self;}- (void)gameOverDone{ [[CCDirector sharedDirector] replaceScene:[HelloWorld scene]];}- (void)dealloc{ [_label release]; _label = nil; [super dealloc];}@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();

2. 关于NSAutoReleasePool, cocos2d-x里也有个模仿实现,这个简单的垃圾回收机制对C++编程来说是个福音;它使用起来和iOS上的NSAutoReleasePool原则一样,参考苹果的文档 http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html

简而言之就是,在使用cocos2d-x中继承自NSObject类的对象指针时,以下两种情况是需要用户多调一个release

 类对象是用户自己new出来的。比如CCSprite *sprite = new CCSprite();

 类对象是通过某个静态函数建立并返回的,比如CCSprite *sprite = CCSprite::spriteWithFile(...),这种情况不需要用户release;但如果你接着调用了sprite->retain(), 那么就需要一个sprite->release()对应