IOSWinScene   

在這一章裡,我們將會添加新的場景。當你幹掉一定數量的怪物時,在螢幕上顯示「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

319

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

139

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

439

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

399

20 [_layer release];

21 _layer = nil;

22 [super dealloc];

23}

24

25@end

26@implementation GameOverLayer

27@synthesize label = _label;

28

29-(id) init

109

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 selector:@selector(gameOverDone)],

45 nil]];

46 }

47 return self;

48}

49

50- (void)gameOverDone

719

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

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()對應

之後回到問題上來,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的背景音樂,並在你輸或贏時顯示一個提示介面。
創作者介紹
創作者 shadow 的頭像
shadow

資訊園

shadow 發表在 痞客邦 留言(0) 人氣()