[转注自官网]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);    

 

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

posted @ 2012-10-23 16:50  Shine Team  阅读(1231)  评论(1编辑  收藏  举报