[转注自官网]Cocos2d-x Tutorial 7 - 锦上添花(Glede Edition for 2.0.3)

Chapter7 – 锦上添花

到上一章,一个简单的游戏基本已经完成了,我们还可以加一些小的装饰,让这个游戏看起来更专业一点。

这一章里我们添加一个新的场景。当大师兄消灭一定数量的妖怪后,在屏幕上显示"You Win",如果有一只妖怪跑出屏幕了,就显示"You Lose"。

现在我们要在工程中创建两个新文件:"GameOverScene.cpp" 和 "GameOverScene.h",并且把它们放到Cocos2dxSimpleGame\Classes文件夹里。

GameOcerScene.h的C++代码

#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();

    CREATE_FUNC(GameOverLayer);



    void gameOverDone();



    CC_SYNTHESIZE_READONLY(cocos2d::CCLabelTTF*, _label, Label);

};



class GameOverScene : public cocos2d::CCScene

{

public:

    GameOverScene():_layer(NULL) {};

    ~GameOverScene();

    bool init();

    CREATE_FUNC(GameOverScene);



    CC_SYNTHESIZE_READONLY(GameOverLayer*, _layer, Layer);

};



#endif // _GAME_OVER_SCENE_H_

相比之下objc的代码似乎简洁的多……

#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

 

TIPS

1.为什么objc的头文件里这么简洁?因为objc能直接在.m文件(源代码文件,相当于C++的.cpp)里直接实现而不需要用声明,真的不需要……

2.译者:不好意思才疏学浅我也研究的不是很懂这一段。基本就是说objc(或者是objc的cocos2d-iphone)里有一个node()方法直接整合了new、init、autorelease。而在C++里则需要自己去定义。当然cocos2d-x为了方便广大C++程序猿,提供了一个CREATE_FUNC(在旧版本是很多不同的静态构造器……就不提及了)宏,在声明里使用CREATE_FUNC宏能够快速建立一个默认的create方法,不带参数,返回的是一个该类的对象,并且自动调用了init和autorelease方法(这就是为什么说使用create创建的对象都不需要手动去release的原因)。一行宏代码:

CREATE_FUNC(GameOverScene);

等价于一下面一段代码

static GameOverScene* create() 

{ 

    GameOverScene *pRet = new GameOverScene(); 

    if (pRet && pRet->init()) 

    { 

        pRet->autorelease(); 

        return pRet; 

    } 

    else 

    { 

        delete pRet; 

        pRet = NULL; 

        return NULL; 

    } 

}

3.关于构造器和init方法。我们知道C++里的构造器是不能写返回值的(但是objc里的init能写),所以需要写try-catch来处理异常。但是try-catch这个东西android SDK不支持,所以cocos2d-x对于新建对象的处理分两步,先呼叫构造器分配空间,然后立马调用init()方法初始化。事实上iOS也采用了这个方法设计接口,比如[[NSString alloc] init](先是分配空间,然后初始化)。三星的bada也是这样用C++的类的。

4.objc里有个@synthesize,表示这是类里的一个组成,能够自动生成setter/getter方法。在C++里他们要保持objc的习惯,也在cocos2dx里定义了CC_SYNTHESIZE系列宏去模仿它们,以自动定义内联的setter/getter方法,CC_SYNTHESIZE_READONLY表示只生成getter不生成setter使它的指针不能被更改。(对应的在objc里还有@property,cocos2d-x里也有CC_PROPERTY系列的宏)

 

GameOverScene.cpp 的C++代码

// cpp with cocos2d-x

#include "GameOverScene.h" 

#include "HelloWorldScene.h" 



USING_NS_CC;



bool GameOverScene::init()

{

    if( CCScene::init() )

    {

        this->_layer = GameOverLayer::create();

        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::create("","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::create(

        CCDelayTime::create(3),

        CCCallFunc::create(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代码

// 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。Scene,场景,可以含有多个layer,在这个场景里只有一个layer。在这个layer中心有个label要显示"You Win"或者是"You Lose",三秒之后场景结束。

 

TIPs:

1.注意到GameOverLayer._label、GameOverScene._layer。它们在objc里被声明为@property(nonatomic, retain)。这意味着当没有指针指向它们的时候它们是不会被自动释放的,而会在dealloc时里被释放。在C++里,对于create的对象,我们要在create之后手动调用retain方法,然后在析构函数里手动调用release方法。就像上面的代码里显式地做的。当然你也可以用一行CC_SAFE_RELEASE_NULL来代替,这很方便。

2.很炫的是NSAutoReleasePool被引入到cocos2d-x中了,这是一个很不错的垃圾回收器,对于C++编程来说都不错,尤其对于刚从java转到C++的程序员来说简直如鱼得水(C++程序猿自满的手动控制回收呢!节操呢!)。这个用法和iOS是一样的,请参考:http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/Classes/NSAutoreleasePool_Class/Reference/Reference.html.

 

在cocos2d-x中,仅有下面两种情况需要调用release方法

• 你使用构造器和new创建的对象,比如:CCSprite* sprite = new CCSprite(); 那么你需要调用release()方法释放它。
• 你使用类的静态方法create创建了对象,比如CCSprite* sprite = CCSprite::create(...),这样它是自动释放的,你不需要调用release方法,但是!如果你在create之后调用了sprite->retain(),那么你就也要调用sprite->release(),它才会被释放。

 

回到项目中来,我们要在符合条件的情况调用GameOverScene出来,一个是杀掉比如30个妖怪获得胜利;另一个是有任意一只妖怪抛出屏幕了。首先搞定第一个情况吧。

首先在HelloWorldScene.h中声明一个成员变量来计数比如

protected:

    int _projectilesDestroyed;

 

然后来到HelloWorldScene.cpp,先包含GameOverScene的头文件

#include "GameOverScene.h"  

C++不允许我们直接在声明的时候初始化其默认值,所以我们在构造器里初始化计数变量

HelloWorld::HelloWorld():_projectilesDestroyed(0)

    ,_targets(NULL)

    ,_projectiles(NULL)

{

    _targets = new CCArray();

    _projectiles = new CCArray(); 

} 

然后在碰撞检测成功的那段代码里添加上如下代码

        // 计数干掉的妖怪数量,如果超过30就跳转到游戏结束场景

        _projectilesDestroyed++;                       

        if (_projectilesDestroyed > 30)

        {

            GameOverScene *gameOverScene = GameOverScene::create();

            gameOverScene->getLayer()->getLabel()->setString("You Win!");

            CCDirector::sharedDirector()->replaceScene(gameOverScene);

        }

 

对于另外一种情况,很简单当妖怪走到屏幕左侧时会调用spriteMoveFinished方法,我们直接在该方法里加入失败的场景转换。在" if(sprite->getTag() == 1) "的分支里添加如下代码:

GameOverScene *gameOverScene = GameOverScene::create();

gameOverScene->getLayer()->getLabel()->setString("You Lose :[");

CCDirector::sharedDirector()->replaceScene(gameOverScene);    

 

现在全部完工了,生成跑一遍吧。该有的效果都有了,妖怪满天飞子弹满天飞,可爱的音乐音效还有完整的游戏结束画面,一个完整的游戏已经完成了,欢呼吧~(我赌这是你们的第一个)

[转注自官网]Cocos2d-x Tutorial 7 - 锦上添花(Glede Edition for 2.0.3)

你可能感兴趣的:(cocos2d-x)