下面添加遊戲背景圖片和玩家操控的飛機。打開HelloWorldLayer.m文件,首先定義4個變量,實現代碼以下。node
程序清單:codes/13/13.14/AirfightGame/AirfightGame/HelloWorldLayer.mios
// 精靈表單tag static NSInteger kTagBatchNode = 1; // 玩家飛機變量 CCSprite* planeSprite; // 屏幕寬度、高度的變量 NSInteger screenWidth , screenHeight;
而後在init方法中添加背景圖片,實現代碼以下(程序清單同上)。算法
-(id) init { if( (self=[super init]) ) { // ①本例使用精靈表單來優化遊戲性能, // CCSpriteBatchNode中的全部CCSprite只會被渲染1次,所以能夠提升遊戲的FPS batchNode = [CCSpriteBatchNode batchNodeWithFile:@"airfightSheet.png"]; batchNode.position = CGPointZero; [self addChild:batchNode z:0 tag:kTagBatchNode]; // 獲取屏幕寬度和高度 CGSize winSize = [[CCDirector sharedDirector] winSize]; screenWidth = winSize.width; screenHeight = winSize.height; // ②添加背景圖片 CCSprite* bgSprite = [CCSprite spriteWithSpriteFrameName:@"bg1.png"]; bgSprite.position = ccp(screenWidth/2,screenHeight/2); [batchNode addChild:bgSprite]; } return self; }
以上代碼①建立了一個CCSpriteBatchNode使用精靈表單加載精靈,優化遊戲性能。接下來獲取屏幕寬度和高度。代碼②初始化了一個背景圖片精靈,添加到CCSpriteBatchNode當中。ide
最後在onEnter方法中添加玩家飛機,實現代碼以下(程序清單同上)。性能
// 節點調用init方法之後將會調用此方法 -(void) onEnter{ [super onEnter]; // ③添加玩家飛機精靈 planeSprite = [CCSprite spriteWithSpriteFrameName:@"plane0.png"]; planeSprite.position = ccp(screenWidth/2, planeSprite.contentSize.height/2+5); [batchNode addChild:planeSprite]; }
代碼③初始化了一個玩家飛機精靈,經過屏幕的寬度和高度設置相對座標,添加到CCSpriteBatchNode當中。測試
再次編譯並運行遊戲,資源加載完畢後將顯示背景圖片和玩家飛機。模擬器顯示效果如圖13.61所示。優化
如今,玩家飛機精靈已經顯示在屏幕當中了,可是這只是一個靜態效果,咱們可使用動畫技術,讓玩家飛機精靈實現飛行動畫效果,這樣可使得遊戲效果更加逼真。動畫
打開HelloWorldLayer.m文件,在私有分類中添加一個新的輔助方法,由於以後不少地方都須要獲取動畫幀,因此將獲取動畫幀的代碼封裝起來,從而達到代碼重用的效果。示例代碼以下:spa
-(CCAnimation*) getAnimationByName:(NSString*)animName delay:(float) delay animNum:(int) num;
該方法的做用是根據動畫幀的名字和動畫幀的數量,以及動畫幀與幀之間的間隔時間建立一個CCAnimation動畫。使用該方法要注意以下兩點。.net
q
動畫幀的命名必須帶序號,好比xxx1.png、xxx2.png等。
q
動畫幀的命名必須連續,並且必須從0開始命名。
接下來是該方法的具體實現,實現代碼以下(程序清單同上)。
-(CCAnimation*)getAnimationByName:(NSString *)animName delay:(float)delay animNum: (int)num{ NSMutableArray *animeFrames = [NSMutableArray arrayWithCapacity:num]; for (int i=0; i< num; i++) { // 獲取動畫圖片名稱 NSString *frameName = [NSString stringWithFormat:@"%@%d.png",animName,i]; // 根據圖片名稱獲取動畫幀 CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:frameName]; [animeFrames addObject:frame]; } return [CCAnimation animationWithSpriteFrames:animeFrames delay:delay]; }
回到onEnter方法中,在③添加玩家飛機精靈部分代碼以後添加動畫效果,實現代碼以下(程序清單同上)。
// ④玩家飛機動畫(尾部噴火) CCAnimation* planeFlyAnimation = [self getAnimationByName:@"plane" delay:0.08 animNum:2]; // 重複動做 id planeFlyAction = [CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:planeFlyAnimation]]; // 執行動做,達到飛機尾部噴火效果 [planeSprite runAction:planeFlyAction];
這段代碼調用輔助方法getAnimationByName: delay: animNum:建立了玩家飛機飛行動畫,而後使用CCRepeatForever和CCAnimation建立了一個重複飛行動做,最後調用planeSprite的runAction方法播放動畫效果。再次編譯並運行遊戲,屏幕上玩家飛機的尾部會產生不斷的噴火效果,給玩家的感受就是飛機在不斷地向前飛行。
如今,咱們要完成控制玩家飛機的移動了。找到onEnter方法,在④玩家飛機動畫部分代碼後添加touch事件,實現代碼以下(程序清單同上)。
// ⑤這是一種新的方式來激活層的touch事件 [[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
這是一種新的方式來激活層的touch事件,老的方式是設置層的isTouchEnabled屬性爲「YES」。cocos2d中CCLayer默認是採用addTargetedDelegate: priority: swallowsTouches:這種方式進行touch事件處理的。每次touch事件發生時,先調用ccTouchBegan: withEvent:方法,該方法對每一個UITouch進行響應並返回一個BOOL值,若爲「YES」,則代表用戶觸摸事件已經被處理,後續的ccTouchMoved: withEvent:、ccTouchEnabled: withEvent:和ccTouchCancelled: withEvent:會接着響應,其餘事件則不會再去進行監聽。ccTouchBegan: withEvent:方法返回的值若是爲假,則會繼續交給其餘註冊過的類型進行處理。
接下來是ccTouchBegan: withEvent:方法和 ccTouchMoved: withEvent:方法的處理,實現代碼以下(程序清單同上)。
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{ return YES; } - (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event { // 把touch座標轉換成局部node座標 CGPoint touchLocation = [self convertTouchToNodeSpace:touch]; // 把舊座標也轉換成局部node座標 CGPoint oldTouchLocation = [touch previousLocationInView:touch.view]; oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation]; oldTouchLocation = [self convertToNodeSpace:oldTouchLocation]; // ccpSub計算兩點的差別,即計算touch偏移量,把當前的點座標減去上一個點座標 CGPoint translation = ccpSub(touchLocation, oldTouchLocation); // ccpAdd讓兩個座標相加 CGPoint newPos = ccpAdd(planeSprite.position, translation); // 給玩家飛機精靈設置新的座標位置 planeSprite.position = newPos; }
再次編譯並運行遊戲,在模擬器中使用鼠標選擇玩家飛機,而後移動鼠標,玩家飛機會隨着鼠標軌跡移動。模擬器顯示效果如圖13.62所示。
如今,玩家飛機已經開始飛行,可是背景圖片一直都沒有變化,咱們將爲遊戲添加連續滾動的背景,製做出更加逼真的飛行效果。步驟以下。
①打開HelloWorldLayer.m文件,添加一個變量:
CCParallaxNode* backgroundNode;
咱們定義了一個CCParallaxNode(視差視圖)變量,用來完成背景滾動效果。當移動時,會看到離咱們越近的物體移動得越快,越遠的物體,好比遠處的山會移動得很慢,而最遠處的物體,好比太陽幾乎不動,這種現象叫視差,而在遊戲中模仿視差,可讓玩家感受到遊戲中的角色的確是在移動。CCParallaxNode能夠很容易地創建一個視差層,你能夠控制每一層的視差率、位置和層級的高低;而CCFollow可讓你的層鏡頭跟隨目標,因此,這裏咱們會使用CCParallaxNode和CCFollow給遊戲添加連續滾動的背景效果。
②找到init方法,註釋掉以前②添加背景圖片的代碼;再找到onEnter方法,添加兩個背景圖片用於拼接,實現代碼以下(程序清單同上)。
// ②添加連續滾動的背景 //初始化CCParallaxNode添加到當前層中 backgroundNode = [CCParallaxNode node]; [self addChild:backgroundNode z:-1]; // ratio指在CCParallaxNode移動時,添加進去的背景圖片精靈的移動速度和CCParallaxNode的比率 CGPoint ratio = ccp(1.0,1.0); // 屏幕高度480是iPhone Retina(3.5-inch),568是iPhone Retina(4-inch) NSString *bgName; if (screenHeight == 480) { bgName = @"bg1.png"; // 640*960 }else{ bgName = @"bg2.png"; // 640*1136 } // 第1張背景圖 CCSprite *bgSprite1 = [CCSprite spriteWithFile:bgName]; // setAliasTexParameters用於解決拼接的地圖在鏈接滾動時容易造成黑色縫隙的問題 [[bgSprite1 texture] setAliasTexParameters]; bgSprite1.anchorPoint = ccp(0,0); [backgroundNode addChild:bgSprite1 z:1 parallaxRatio:ratio positionOffset:ccp(0,0)]; // 第2張背景圖 CCSprite *bgSprite2 = [CCSprite spriteWithFile:bgName]; [[bgSprite2 texture] setAliasTexParameters]; bgSprite2.anchorPoint = ccp(0,0); // positionOffset時在第2張背景圖與第1個背景圖拼接處減去1個像素,能夠消除地圖拼接的縫隙 [backgroundNode addChild:bgSprite2 z:1 parallaxRatio:ratio positionOffset:ccp(0, winSize.height - 1)]; // 添加開始連續滾動背景的代碼 const int MAX_WIDTH = 320; const int MAX_HEIGHT = 480 * 100; CCSprite *hiddenPlaneSprite = [CCSprite spriteWithSpriteFrameName:@"plane0.png"]; hiddenPlaneSprite.visible = NO; hiddenPlaneSprite.position = ccp(winSize.width / 2, winSize.height / 2); [batchNode addChild:hiddenPlaneSprite z:-4 tag:1024]; id move = [CCMoveBy actionWithDuration:300.0f position:ccp(0,MAX_HEIGHT)]; [hiddenPlaneSprite runAction:move]; // 讓背景開始滾動,背景跟隨隱形飛機移動 [backgroundNode runAction:[CCFollow actionWithTarget:hiddenPlaneSprite worldBoundary:CGRectMake(0, 0, MAX_WIDTH, MAX_HEIGHT)]];
上面代碼首先初始化了一個CCParallaxNode,並把它添加到當前層中。初始化兩個背景圖片精靈,設置錨點爲(0,0),並把兩個背景圖片精靈都添加到CCParallaxNode當中。CCParallaxNode的ratio屬性表示在CCParallaxNode移動時,添加進去的背景圖片精靈的移動速度和CCParallaxNode的比率。假設CCParallaxNode是以每秒10像素移動的,若是設置的ratio是(1,1),那麼bgSprite1和bgSprite2的X軸方向的速度也是每秒10像素,Y軸方向的速度也是每秒10像素。若是須要飛行速度加快,則能夠將ratio設置爲(1,2),讀者能夠自行設置來測試背景圖片移動的速度。在兩個背景圖片精靈上面都調用了setAliasTexParameters方法,用於解決拼接的地圖在鏈接滾動時容易造成黑色縫隙的問題。同時,在設置positionOffset時,在第2張背景圖與第1張背景圖拼接處減去1個像素,能夠消除地圖拼接的縫隙。
接下來是添加連續滾動背景的代碼。這裏建立了一個隱形的飛機精靈,由於想要實現背景的連續滾動,須要背景可以跟隨一個不斷向上前進的CCNode纔會產生連續向下滾動的效果。讓隱形的飛機精靈執行一個moveBy動做,在必定的時間範圍內運行必定的距離,而backgroundNode則執行一個CCFollow跟隨動做,跟隨隱形飛機移動,從而達到背景滾動的效果。
③這裏還須要一個算法來計算backgroundNode的座標位置。讀者能夠在配套光盤的Resource目錄下找到兩個預先製做好的CCParallaxNode的擴展類:CCParallaxNode-Extras.h和CCParallaxNode-Extras.m,將這兩個文件添加到項目的「AirfightGame」組中。
④打開HelloWorldLayer.m文件,在私有分類中添加一個更新背景圖片滾動的方法,示例代碼以下:
-(void) updateBackground:(ccTime)delay;
下面是該方法的具體實現,實現代碼以下(程序清單同上)。
-(void) updateBackground:(ccTime)delay{ CCSprite *sprite; int index = 0; CCARRAY_FOREACH([backgroundNode children],sprite){ CGPoint pt = [backgroundNode convertToWorldSpace:sprite.position]; if (pt.y <= -sprite.contentSize.height) { // sprite.contentSize.height表示精靈的高度,即背景圖片的高度 [backgroundNode incrementOffset:ccp(0, (sprite.contentSize.height - 1) * 2.0f) forChild:sprite]; } index++; } }
updateBackground的做用是循環遍歷backgroundNode中的背景圖片精靈,即時修改背景圖片精靈的座標位置。
⑤找到onEnter方法,在最後添加遊戲的主循環代碼,實現代碼以下。
// ⑥遊戲主循環,每幀都調用的更新方法 // 這樣以默認cocos2d的刷新頻率1/60.0s調用(void)update:(ccTime)delta一次 [self scheduleUpdate];
這個方法是遊戲的主循環。任何遊戲都包括一個遊戲主循環,用來更新遊戲的狀態、玩家和敵人的數量、碰撞處理的邏輯等。
定義一個update方法,調用更新背景圖片滾動的方法,實現代碼以下(程序清單同上)。
-(void) update:(ccTime)delta{ [self updateBackground:delta]; }
再次編譯並運行遊戲,能夠看到一個連續滾動的遊戲背景效果。
————本文節選自《瘋狂ios講義(下)》