亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Cocos2d開發系列(四)

系統 2074 0

Learn IPhone and iPad Cocos2d Game Delevopment》的第5章。

一、使用多場景

很少有游戲只有一個場景。這個例子是這個樣子的: Cocos2d開發系列(四)

這個 Scene中用到了兩個Layer,一個Layer位于屏幕上方,標有”Here be your Game Scores etc“字樣的標簽,用于模擬游戲菜單。一個Layer位于屏幕下方,一塊綠色的草地上有一些隨機游動的蜘蛛和怪物,模擬了游戲的場景。

1、加入新場景

一個場景是一個 Scene類。加入新場景就是加入更多的Scene類。

有趣的是場景之間的切換。使用 [CCDirector replaceScene]方法轉場時,CCNode有3個方法會被調用:OnEnter、OnExit、 onEnterTransitionDidFinish。

覆蓋這 3個方法時要牢記,始終要調用super的方法,避免程序的異常(比如內存泄露或場景不響應用戶動作)。

-(void) onEnter {

// node的 init 方法后調用.

// 如果使用 CCTransitionScene方法,在轉場開始后調用.

[super onEnter];

}

-(void ) onEnterTransitionDidFinish {

// onEnter方法后調用.

// 如果使用 CCTransitionScene方法,在轉場結束后調用.

[super onEnterTransitionDidFinish];

}

-(void) onExit

{

// node的 dealloc 方法前調用.

// 如果使用CCTransitionScene方法, 在轉場結束時調用.

[super onExit];

}

當場景變化時,有時候需要讓某個 node干點什么,這時這3個方法就派上用場了。

與在 node的init方法和dealloc方法中做同樣的事情不同,在onEnter方法執行時,場景已經初始化了;而在onExit方法中,場景的node仍然是存在的。

這樣,在進行轉場時,你就可以暫停動畫或隱藏用戶界面元素,一直到轉場完成。這些方法調用的先后順序如下(使用 replaceScene 方法):

1. 第2個場景的 scene 方法

2. 第2個場景的 init 方法

3. 第2個場景的 onEnter 方法

4. 轉場

5. 第1個場景的 onExit 方法

6. 第2個場景的 onEnterTransitionDidFinish 方法

7. 第1個場景的 dealloc 方法

二、請稍候??

切換場景時,如果場景的加載是一個比較耗時的工作,有必要用一個類似“ Loading,please waiting…”的場景來過渡一下。用于在轉場時過渡的場景是一個“輕量級”的Scene類,可以顯示一些簡單的提示內容:

typedef enum

{

TargetSceneINVALID = 0 ,

TargetSceneFirstScene,

TargetSceneOtherScene,

TargetSceneMAX,

} TargetScenes;

@interface LoadingScene : CCScene

{

TargetScenes targetScene_;

}

+( id ) sceneWithTargetScene:(TargetScenes)targetScene;

-( id ) initWithTargetScene:(TargetScenes)targetScene;

@end

#import "LoadingScene.h"

#import "FirstScene.h"

#import "OtherScene.h"

@interface LoadingScene (PrivateMethods)

-( void ) update:(ccTime)delta;

@end

@implementation LoadingScene

+( id ) sceneWithTargetScene:(TargetScenes)targetScene;

{

return [[[ self alloc] initWithTargetScene:targetScene] autorelease];

}

-( id ) initWithTargetScene:(TargetScenes)targetScene

{

if (( self = [ super init]))

{

targetScene_ = targetScene;

CCLabel* label = [CCLabel labelWithString: @"Loading ..." fontName: @"Marker Felt" fontSize: 64 ];

CGSize size = [[CCDirector sharedDirector] winSize];

label.position = CGPointMake(size.width / 2 , size.height / 2 );

[ self addChild:label];

[ self scheduleUpdate];

}

return self ;

}

-( void ) update:(ccTime)delta

{

[ self unscheduleAllSelectors];

switch (targetScene_)

{

case TargetSceneFirstScene:

[[CCDirector sharedDirector] replaceScene:[FirstScene scene]];

break ;

case TargetSceneOtherScene:

[[CCDirector sharedDirector] replaceScene:[OtherScene scene]];

break ;

default :

// NSStringFromSelector(_cmd) 打印方法名

NSAssert2( nil , @"%@: unsupported TargetScene %i" , NSStringFromSelector( _cmd ), targetScene_);

break ;

}

}

-( void ) dealloc

{

CCLOG( @"%@: %@" , NSStringFromSelector( _cmd ), self );

[ super dealloc];

}

@end

首先,定義了一個枚舉。這個技巧使 LoadingScene 能用于多個場景的轉場,而不是固定地只能在某個場景的切換時使用。繼續擴展這個枚舉的成員,使 LoadingScene 能適用與更多目標 Scene 的轉場。

sceneWithTargetScene 方法中返回了一個 autorelease 的對象。在 coco2d 自己的類中也是一樣的,你要記住在每個靜態的初始化方法中使用 autorelease

方法中,構造了一個 CCLabel ,然后調用 scheduleUpdate 方法。 scheduleUpdate 方法會在下一個時間(約一幀)后調用 update 方法。在 update 方法中,我們根據 sceneWithTargetScene 方法中指定的枚舉參數,切換到另一個 scene 。在這個 scene 的加載完成之前, LoadingScene 會一直顯示并且凍結用戶的事件響應。

我們不能直接在初始化方法 initWithTargetScene 中直接切換 scene ,這會導致程序崩潰。記住,在一個 Node 還在初始化的時候,千萬不要在這個 scene 上調用 CCDirector replaceScene 方法。

LoadingScene 的使用很簡單,跟一般的 scene 一樣:

CCScene * newScene = [ LoadingScene sceneWithTargetScene : TargetSceneFirstScene ];

[[ CCDirector sharedDirector ] replaceScene :newScene];

三、使用 Layer

Layer類似Photoshop中層的概念,在一個scene中可以有多個Layer:

typedef enum

{

LayerTagGameLayer ,

LayerTagUILayer ,

} MultiLayerSceneTags;

typedef enum

{

ActionTagGameLayerMovesBack ,

ActionTagGameLayerRotates ,

} MultiLayerSceneActionTags;

@class GameLayer ;

@class UserInterfaceLayer ;

@interface MultiLayerScene : CCLayer

{

bool isTouchForUserInterface ;

}

+( MultiLayerScene *) sharedLayer;

@property ( readonly ) GameLayer* gameLayer;

@property ( readonly ) UserInterfaceLayer* uiLayer;

+( CGPoint ) locationFromTouch:( UITouch *)touch;

+( CGPoint ) locationFromTouches:( NSSet *)touches;

+( id ) scene;

@end

@implementation MultiLayerScene

static MultiLayerScene* multiLayerSceneInstance;

+( MultiLayerScene *) sharedLayer

{

NSAssert ( multiLayerSceneInstance != nil , @"MultiLayerScene not available!" );

return multiLayerSceneInstance ;

}

-( GameLayer *) gameLayer

{

CCNode * layer = [ self getChildByTag : LayerTagGameLayer ];

NSAssert ([layer isKindOfClass :[ GameLayer class ]], @"%@: not a GameLayer!" , NSStringFromSelector ( _cmd ));

return ( GameLayer *)layer;

}

-( UserInterfaceLayer *) uiLayer

{

CCNode * layer = [[ MultiLayerScene sharedLayer ] getChildByTag : LayerTagUILayer ];

NSAssert ([layer isKindOfClass :[ UserInterfaceLayer class ]], @"%@: not a UserInterfaceLayer!" , NSStringFromSelector ( _cmd ));

return ( UserInterfaceLayer *)layer;

}

+( CGPoint ) locationFromTouch:( UITouch *)touch

{

CGPoint touchLocation = [touch locationInView : [touch view ]];

return [[ CCDirector sharedDirector ] convertToGL :touchLocation];

}

+( CGPoint ) locationFromTouches:( NSSet *)touches

{

return [ self locationFromTouch :[touches anyObject ]];

}

+( id ) scene

{

CCScene * scene = [ CCScene node ];

MultiLayerScene * layer = [ MultiLayerScene node ];

[scene addChild :layer];

return scene;

}

-( id ) init

{

if (( self = [ super init ]))

{

NSAssert ( multiLayerSceneInstance == nil , @"another MultiLayerScene is already in use!" );

multiLayerSceneInstance = self ;

GameLayer * gameLayer = [ GameLayer node ];

[ self addChild : gameLayer z : 1 tag : LayerTagGameLayer ];

UserInterfaceLayer * uiLayer = [ UserInterfaceLayer node ];

[ self addChild : uiLayer z : 2 tag : LayerTagUILayer ];

}

return self ;

}

-( void ) dealloc

{

CCLOG ( @"%@: %@" , NSStringFromSelector ( _cmd ), self );

[ super dealloc ];

}

@end

MultiLayerScene 中使用了多個 Layer: 一個 GameLayer h 和一個 UserInterfaceLayer

MultiLayerScene 使用了靜態成員 multiLayerSceneInstance 來實現單例。 MultiLayerScene也是一個Layer,其node方法實際上調用的是實例化方法init——在其中,我們加入了兩個Layer,分別用兩個枚舉 LayerTagGameLayer LayerTagUILayer 來檢索 , 如屬性方法 gameLayer和uiLayer所示。

uiLayer是一個UserInterfaceLayer,用來和用戶交互,在這里實際上是在屏幕上方放置一個菜單,可以把游戲的一些統計數字比如:積分、生命值放在這里:

typedef enum

{

UILayerTagFrameSprite ,

} UserInterfaceLayerTags;

@interface UserInterfaceLayer : CCLayer

{

}

-( bool ) isTouchForMe:( CGPoint )touchLocation;

@end

@implementation UserInterfaceLayer

-( id ) init

{

if (( self = [ super init ]))

{

CGSize screenSize = [[ CCDirector sharedDirector ] winSize ];

CCSprite * uiframe = [ CCSprite spriteWithFile : @"ui-frame.png" ];

uiframe. position = CGPointMake ( 0 , screenSize. height );

uiframe. anchorPoint = CGPointMake ( 0 , 1 );

[ self addChild :uiframe z : 0 tag : UILayerTagFrameSprite ];

// Label模擬UI控件( 這個Label沒有什么作用,僅僅是演示) .

CCLabel * label = [ CCLabel labelWithString : @"Here be your Game Scores etc" fontName : @"Courier" fontSize : 22 ];

label. color = ccBLACK ;

label. position = CGPointMake (screenSize. width / 2 , screenSize. height );

label. anchorPoint = CGPointMake ( 0.5f , 1 );

[ self addChild :label];

self . isTouchEnabled = YES ;

}

return self ;

}

-( void ) dealloc

{

CCLOG ( @"%@: %@" , NSStringFromSelector ( _cmd ), self );

[ super dealloc ];

}

-( void ) registerWithTouchDispatcher

{

[[ CCTouchDispatcher sharedDispatcher ] addTargetedDelegate : self priority :- 1 swallowsTouches : YES ];

}

// 判斷觸摸是否位于有效范圍內 .

-( bool ) isTouchForMe:( CGPoint )touchLocation

{

CCNode * node = [ self getChildByTag : UILayerTagFrameSprite ];

return CGRectContainsPoint ([node boundingBox ], touchLocation);

}

-( BOOL ) ccTouchBegan:( UITouch *)touch withEvent:( UIEvent *)event

{

CGPoint location = [ MultiLayerScene locationFromTouch :touch];

bool isTouchHandled = [ self isTouchForMe :location];

if (isTouchHandled)

{

// 顏色改變為紅色,表示接收到觸摸事件 .

CCNode * node = [ self getChildByTag : UILayerTagFrameSprite ];

NSAssert ([node isKindOfClass :[ CCSprite class ]], @"node is not a CCSprite" );

(( CCSprite *)node). color = ccRED ;

// Action: 旋轉 +縮放 .

CCRotateBy * rotate = [ CCRotateBy actionWithDuration : 4 angle : 360 ];

CCScaleTo * scaleDown = [ CCScaleTo actionWithDuration : 2 scale : 0 ];

CCScaleTo * scaleUp = [ CCScaleTo actionWithDuration : 2 scale : 1 ];

CCSequence * sequence = [ CCSequence actions :scaleDown, scaleUp, nil ];

sequence. tag = ActionTagGameLayerRotates ;

GameLayer * gameLayer = [ MultiLayerScene sharedLayer ]. gameLayer ;

// 重置 GameLayer 屬性 , 以便每次動畫都是以相同的狀態開始

[gameLayer stopActionByTag : ActionTagGameLayerRotates ];

[gameLayer setRotation : 0 ];

[gameLayer setScale : 1 ];

// 運行動畫

[gameLayer runAction :rotate];

[gameLayer runAction :sequence];

}

return isTouchHandled;

}

-( void ) ccTouchEnded:( UITouch *)touch withEvent:( UIEvent *)event

{

CCNode * node = [ self getChildByTag : UILayerTagFrameSprite ];

NSAssert ([node isKindOfClass :[ CCSprite class ]], @"node is not a CCSprite" );

// 色彩復原

(( CCSprite *)node). color = ccWHITE ;

}

@end

為了保證 uiLayer總是第一個收到touch事件,我們在 registerWithTouchDispatcher 方法中使用-1的priority。并且用 isTouchForMe 方法檢測touch是否處于Layer的范圍內。如果在,touchBegan方法返回YES,表示“吃掉” touch事件(即不會傳遞到下一個Layer處理);否則,返回NO,傳遞給下一個Layer(GameLayer)處理。

而在 GameLayer中, registerWithTouchDispatcher 的priority是0

以下是 GameLayer代碼:

@interface GameLayer : CCLayer

{

CGPoint gameLayerPosition ;

CGPoint lastTouchLocation ;

}

@end

@interface GameLayer (PrivateMethods)

-( void ) addRandomThings;

@end

@implementation GameLayer

-( id ) init

{

if (( self = [ super init ]))

{

self . isTouchEnabled = YES ;

gameLayerPosition = self . position ;

CGSize screenSize = [[ CCDirector sharedDirector ] winSize ];

CCSprite * background = [ CCSprite spriteWithFile : @"grass.png" ];

background. position = CGPointMake (screenSize. width / 2 , screenSize. height / 2 );

[ self addChild :background];

CCLabel * label = [ CCLabel labelWithString : @"GameLayer" fontName : @"Marker Felt" fontSize : 44 ];

label. color = ccBLACK ;

label. position = CGPointMake (screenSize. width / 2 , screenSize. height / 2 );

label. anchorPoint = CGPointMake ( 0.5f , 1 );

[ self addChild :label];

[ self addRandomThings ];

self . isTouchEnabled = YES ;

}

return self ;

}

// node加上一個MoveBy的動作(其實就是在圍繞一個方框在繞圈)

-( void ) runRandomMoveSequence:( CCNode *)node

{

float duration = CCRANDOM_0_1 () * 5 + 1 ;

CCMoveBy * move1 = [ CCMoveBy actionWithDuration :duration position : CGPointMake (- 180 , 0 )];

CCMoveBy * move2 = [ CCMoveBy actionWithDuration :duration position : CGPointMake ( 0 , - 180 )];

CCMoveBy * move3 = [ CCMoveBy actionWithDuration :duration position : CGPointMake ( 180 , 0 )];

CCMoveBy * move4 = [ CCMoveBy actionWithDuration :duration position : CGPointMake ( 0 , 180 )];

CCSequence * sequence = [ CCSequence actions :move1, move2, move3, move4, nil ];

CCRepeatForever * repeat = [ CCRepeatForever actionWithAction :sequence];

[node runAction :repeat];

}

// 模擬一些游戲對象 ,為每個對象加上一些動作(繞圈) .

-( void ) addRandomThings

{

CGSize screenSize = [[ CCDirector sharedDirector ] winSize ];

for ( int i = 0 ; i < 4 ; i++)

{

CCSprite * firething = [ CCSprite spriteWithFile : @"firething.png" ];

firething. position = CGPointMake ( CCRANDOM_0_1 () * screenSize. width , CCRANDOM_0_1 () * screenSize. height );

[ self addChild :firething];

[ self runRandomMoveSequence :firething];

}

for ( int i = 0 ; i < 10 ; i++)

{

CCSprite * spider = [ CCSprite spriteWithFile : @"spider.png" ];

spider. position = CGPointMake ( CCRANDOM_0_1 () * screenSize. width , CCRANDOM_0_1 () * screenSize. height );

[ self addChild :spider];

[ self runRandomMoveSequence :spider];

}

}

-( void ) dealloc

{

CCLOG ( @"%@: %@" , NSStringFromSelector ( _cmd ), self );

// don't forget to call "super dealloc"

[ super dealloc ];

}

-( void ) registerWithTouchDispatcher

{

[[ CCTouchDispatcher sharedDispatcher ] addTargetedDelegate : self priority : 0 swallowsTouches : YES ];

}

-( BOOL ) ccTouchBegan:( UITouch *)touch withEvent:( UIEvent *)event

{

// 記錄開始touch時的位置 .

lastTouchLocation = [ MultiLayerScene locationFromTouch :touch];

// 先停止上一次動作,以免對本次拖動產生干擾 .

[ self stopActionByTag : ActionTagGameLayerMovesBack ];

// 吃掉所有 touche

return YES ;

}

-( void ) ccTouchMoved:( UITouch *)touch withEvent:( UIEvent *)event

{

// 記錄手指移動的位置

CGPoint currentTouchLocation = [ MultiLayerScene locationFromTouch :touch];

// 計算移動的距離

CGPoint moveTo = ccpSub ( lastTouchLocation , currentTouchLocation);

// 上面的計算結果要取反 .因為接下來是移動前景,而不是移動背景

moveTo = ccpMult (moveTo, - 1 );

lastTouchLocation = currentTouchLocation;

// 移動前景——修改 Layer的位置,將同時改變Layer所包含的node self . position = ccpAdd ( self . position , moveTo);

}

-( void ) ccTouchEnded:( UITouch *)touch withEvent:( UIEvent *)event

{

// 最后把 Layer的位置復原 .Action: 移動 +漸慢

CCMoveTo * move = [ CCMoveTo actionWithDuration : 1 position : gameLayerPosition ];

CCEaseIn * ease = [ CCEaseIn actionWithAction :move rate : 0.5f ];

ease. tag = ActionTagGameLayerMovesBack ;

[ self runAction :ease];

}

@end

為了讓程序運行起來更有趣, GameLayer中加入了一張青草的背景圖,以及一些游戲對象,并讓這些對象在隨機地移動。這部分內容不是我們關注的,我們需要關注的是幾個touch方法的處理。

1、 ccTouchBegan

由于 GameLayer是最后收到touch事件的Layer,我們不需要檢測touch是否在Layer范圍(因為傳給它的都是別的Layer“吃剩下”的touch)。所以GameLayer的touchBegan方法只是簡單的返回YES(“吃掉”所有touch)。

2 ccTouchMoved:

在這里我們計算手指移動的距離,然后讓 Layer作反向運動。為什么要作“反向”運動? 因為我們想制造一種屏幕隨著手指劃動的感覺,例如: 當手向右劃動時,屏幕也要向右運動。當然,iPhone不可能真的向右運動。要想模擬屏幕向右運動,只需讓游戲畫面向左運動即可。因為當運動物體在向前移動時,如果假設運動物體固定不動,則可以認為是參照物(或背景)在向后運動。

3、 ccTouchEnded:

在這里,我們把 Layer的位置恢復到原位。

四、其他

這一章還討論了很多有用的東西,比如“關卡”。是使用 Scene還是Layer作為游戲關卡?

作者還建議在設計 Sprite時使用聚合而不要使用繼承。即Sprite設計為不從CCNode繼承,而設計為普通的NSObject子類(在其中聚合了CCNode)。

此外還討論了 CCTargetToucheDelegate、CCProgressTimer、CCParallaxNode、vCCRibbon和CCMotionStreak。

這些東西可以豐富我們的理論知識,但沒有必要細讀。

Cocos2d開發系列(四)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 日本一区二区三区免费在线观看 | 日韩欧美精品在线视频 | 日韩美女va在线毛片免费知 | 91精品刘玥在线播放 | 荔枝污| 亚洲第一视频在线播放 | 99爱在线精品视频网站 | 最新国产午夜精品视频成人 | 国产精品久热 | 99视频精品全国在线观看 | 综合网婷婷 | 精品不卡| 日韩欧美亚洲国产 | 久草热久 | 天天操91 | 国产精品视频色拍拍 | 91精品视频网 | 欧美丰满大乳大屁股毛片 | 男人天堂黄色 | 久久久久国产精品四虎 | 亚洲精品一区二区久久 | 国产精品合集一区二区 | 99re这里有免费视频精品 | 五月婷婷激情五月 | japanese国产高清麻豆 | 久久综合九色综合亚洲小说 | 四虎影院在线 | 成人18毛片 | 午夜三级影院 | 成 人国产在线观看高清不卡 | 亚洲图片欧美 | 色综合久久综合网欧美综合网 | 婷婷操 | 97成人资源| 久久99国产精品久久99小说 | 特级一级全黄毛片免费 | 99热这里只有免费国产精品 | 国产一区二区三区高清视频 | 国产精选一区二区 | 欧美激情日本一道免费视频 | 久久精品国产6699国产精 |