今天的目標就是解決捲動的問題,之後我希望能用不同的方式進行捲動,不過今天,我會使用上次做的小Demo,就是會隨著你的點擊移動的Sprite。這個Demo是希望畫面會跟著那個移動的Sprite而移動。
參考書目:Learning Cocos2D(現在有中文版了!)
首先,如果想要一個基本的遊戲畫面,然後裡面的角色移動時,畫面會跟著角色移動,讓角色保持在畫面的中央附近,這樣的話,你可以思考一下,這些畫面該怎麼去安排。
首先是不會移動的背景,背景有很多種,有些背景會放兩三層,越靠近使用者的背景移動的速度越快,以製造遠近的效果。
再來是角色所在的層,這一層主要是角色的遊戲邏輯所在,遊戲中大部分的動作發生的地方,這可以取較高的z值,讓這一層中的東西比起背景還更接近使用者。
最後是控制層,這一層也是可以放一些互動的界面,像是時間、分數或是模擬按鍵,這一層完全固定在螢幕上的,當畫面移動時這一層的東西都是保持在畫面上固定的位置。但如果你沒有這些物件時也可以不實作這一層。
這樣你就可以用三個classes去實作這三層,但我這邊只會用到背景和動作層,所以我只會實作兩個層。
你要一個Scene
要統整這些層,你要先實作一個Scene去放這些Layer(都是iOS/Cocoa Touch/Objective-C class):
以及它的implementation檔:
[self addChild:scrollingLayer z:1 tag:1];
}
return self;
}
@end
可以看到implementation檔中放了兩個Layers,他們的z值都不一樣。
再來是實作背景類,繼承自CCLayer(iOS/Cocoa Touch/Objective-C class):
#import "CCLayer.h"
@interface StaticBackgroundLayer : CCLayer
@end
和implementation:
背景檔是我去那本書的網站抓的。裡面只是單純的實作一個初始化,依照你使用的設備不同,使用不同的背景畫面,然後把畫面放在螢幕中間。
如果能的話,接下來我會分三個程度介紹捲動:
1.放入一個螢幕兩倍寬的背景。
2.加入有視差(parallax)的node,並且在Layer中捲動背景,裡面每一個層捲動的速度都不一樣。
3.最後是捲動TileMap層,以學習即使創造很大的關卡也能節省記憶體。
我們先實作interface檔:
注意,第一個變數是儲存可捲動之背景,之後要製作atlas。第二個變數是製作tiled map,第三個變數是製作視差node,最後一個變數是我們要使用的遊戲物件。
然後先在GameplayScrollingLayer.m中實作兩個方法:
addScrollingBackground方法是先取得螢幕的大小,然後用螢幕大小製作關卡的實際大小(我這邊和書上不一樣),然後用if迴圈根據使用的設備選用適當的背景圖,最後把背景圖位置設定好,注意這邊是以關卡大小為主,寬度為關卡大小的寬度一半,高度則是螢幕高度的一半。然後再加入這個方法:
-(void) adjustLayer {
float iconXPosition = icon.position.x;
CGSize screenSize = [CCDirector sharedDirector].winSize;
float halfOfTheScreen = screenSize.width/2.0f;
CGSize levelSize = CGSizeMake(screenSize.width * 2, screenSize.height);
if ((iconXPosition > halfOfTheScreen) && (iconXPosition < levelSize.width - halfOfTheScreen)) {
float newXPosition = halfOfTheScreen -iconXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
首先取得icon位置的x座標,然後取得螢幕大小,再取得螢幕寬度的一半大小,最後製作出關卡的小,再用這些東西製作if迴圈,回圈是要進行捲動,條件是,如果icon在整個關卡裡面距離頭尾一半螢幕寬度的範圍內的話,那麼算出新的X,就是一半的螢幕寬度減去icon的x位置,最後就是重新設定層的位置。
最後是加入update方法:
-(void) update:(ccTime)delta {
[self adjustLayer];
}
好,編譯看看吧!這時你會發現icon好像在捲動背景後面,你可以調整icon的z值。
在cocos2D裡面,有一個類別叫做CCParallaxNode,這讓你不用自己去寫code去製作視差。Parallax node世一個特別的Cocos2D parent node,它可以設定好讓他的children按不同比率去捲動,現在來直接體驗看看吧!
打開GameplayScrollingLayer.m,加入以下方法:
-(void) addScrollingBackgroundWithParallax {
CGSize screenSize = [CCDirector sharedDirector].winSize;
CGSize levelSize = CGSizeMake(screenSize.width * 2, screenSize.height);
CCSprite *BGLayer1;
CCSprite *BGLayer2;
CCSprite *BGLayer3;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
BGLayer1 = [CCSprite spriteWithFile:@"chap9_scrolling4.png"];
BGLayer2 = [CCSprite spriteWithFile:@"chap9_scrolling3.png"];
BGLayer3 = [CCSprite spriteWithFile:@"chap9_scrolling2.png"];
}else {
BGLayer1 = [CCSprite spriteWithFile:@"chap9_scolling4iPhone.png"];
BGLayer2 = [CCSprite spriteWithFile:@"chap9_scolling3iPhone.png"];
BGLayer3 = [CCSprite spriteWithFile:@"chap9_scolling2iPhone.png"];
}
parallaxNode = [CCParallaxNode node];
[parallaxNode setPosition:ccp(levelSize.width/2.0f, screenSize.height/2.0f)];
float xOffset = 0;
[parallaxNode addChild:BGLayer1 z:40 parallaxRatio:ccp(1.0f, 1.0f) positionOffset:ccp(0.0f, 0.0f)];
xOffset = (levelSize.width/2) *0.3f;
[parallaxNode addChild:BGLayer2 z:20 parallaxRatio:ccp(0.2f, 1.0f) positionOffset:ccp(xOffset, 0)];
xOffset = (levelSize.width/2) * 0.8f;
[parallaxNode addChild:BGLayer3 z:30 parallaxRatio:ccp(0.7f, 1.0f) positionOffset:ccp(xOffset, 0)];
[self addChild:parallaxNode z:10];
}
他首先加入了三個層,然後根據設備的不同會載入不同的圖。接著初始化parallaxNode變數,設定該變數要放置的位置,然後將剛剛建立的三個層加入,並指定不同的比率和z值。
再來,回到init方法裡面,把:
給comment out,換成:
現在來看一下為什麼要有那些offset,可以進到addChild:z:parallaxRatio:positionOffset:方法裡面:
CGPoint pos = self.position;
pos.x = pos.x * ratio.x + offset.x;
pos.y = pos.y * ratio.y + offset.y;
child.position = pos;
子層的位置,等於CCParallazNode的位置乘上比率後再加上offset。舉例來說,第三個子層的x位置的比率是0.7,你把它設置在1024像素的位置,所以乘起來就是1024 * 0.7 = 716.8,這一層其實歪斜到左邊去,自然要補上offset把它移回來。
至於無限大的捲動關卡,我想寫在下一回好了。
參考書目:Learning Cocos2D(現在有中文版了!)
有哪些層?
首先,如果想要一個基本的遊戲畫面,然後裡面的角色移動時,畫面會跟著角色移動,讓角色保持在畫面的中央附近,這樣的話,你可以思考一下,這些畫面該怎麼去安排。
首先是不會移動的背景,背景有很多種,有些背景會放兩三層,越靠近使用者的背景移動的速度越快,以製造遠近的效果。
再來是角色所在的層,這一層主要是角色的遊戲邏輯所在,遊戲中大部分的動作發生的地方,這可以取較高的z值,讓這一層中的東西比起背景還更接近使用者。
最後是控制層,這一層也是可以放一些互動的界面,像是時間、分數或是模擬按鍵,這一層完全固定在螢幕上的,當畫面移動時這一層的東西都是保持在畫面上固定的位置。但如果你沒有這些物件時也可以不實作這一層。
這樣你就可以用三個classes去實作這三層,但我這邊只會用到背景和動作層,所以我只會實作兩個層。
你要一個Scene
要統整這些層,你要先實作一個Scene去放這些Layer(都是iOS/Cocoa Touch/Objective-C class):
#import <Foundation/Foundation.h>
#import "CCScene.h"
#import "cocos2d.h"
#import "GameplayScrollingLayer.h"
#import "StaticBackgroundLayer.h"
@interface GameScene : CCScene {
#import "CCScene.h"
#import "cocos2d.h"
#import "GameplayScrollingLayer.h"
#import "StaticBackgroundLayer.h"
@interface GameScene : CCScene {
}
@end
@end
以及它的implementation檔:
#import "GameScene.h"
@implementation GameScene
-(id)init {
self = [super init];
if (self != nil) {
// Background Layer
StaticBackgroundLayer *backgroundLayer =
[StaticBackgroundLayer node];
[self addChild:backgroundLayer z:0];
GameplayScrollingLayer *scrollingLayer = [GameplayScrollingLayer node];@implementation GameScene
-(id)init {
self = [super init];
if (self != nil) {
// Background Layer
StaticBackgroundLayer *backgroundLayer =
[StaticBackgroundLayer node];
[self addChild:backgroundLayer z:0];
[self addChild:scrollingLayer z:1 tag:1];
}
return self;
}
@end
可以看到implementation檔中放了兩個Layers,他們的z值都不一樣。
背景class
再來是實作背景類,繼承自CCLayer(iOS/Cocoa Touch/Objective-C class):
#import "CCLayer.h"
@interface StaticBackgroundLayer : CCLayer
@end
和implementation:
#import "StaticBackgroundLayer.h"
#import "cocos2d.h"
@implementation StaticBackgroundLayer
-(id)init {
self = [super init];
if (self != nil) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
CCSprite *backgroundImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Indicates game is running on iPad
backgroundImage =
[CCSprite spriteWithFile:@"chap9_scrolling1.png"];
} else {
backgroundImage =
[CCSprite spriteWithFile:@"chap9_scrolling1iPhone.png"];
}
[backgroundImage setPosition:ccp(screenSize.width/2.0f, screenSize.height/2.0f)];
[self addChild:backgroundImage];
}
return self;
}
@end
#import "cocos2d.h"
@implementation StaticBackgroundLayer
-(id)init {
self = [super init];
if (self != nil) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
CCSprite *backgroundImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// Indicates game is running on iPad
backgroundImage =
[CCSprite spriteWithFile:@"chap9_scrolling1.png"];
} else {
backgroundImage =
[CCSprite spriteWithFile:@"chap9_scrolling1iPhone.png"];
}
[backgroundImage setPosition:ccp(screenSize.width/2.0f, screenSize.height/2.0f)];
[self addChild:backgroundImage];
}
return self;
}
@end
背景檔是我去那本書的網站抓的。裡面只是單純的實作一個初始化,依照你使用的設備不同,使用不同的背景畫面,然後把畫面放在螢幕中間。
捲動層
如果能的話,接下來我會分三個程度介紹捲動:
1.放入一個螢幕兩倍寬的背景。
2.加入有視差(parallax)的node,並且在Layer中捲動背景,裡面每一個層捲動的速度都不一樣。
3.最後是捲動TileMap層,以學習即使創造很大的關卡也能節省記憶體。
我們先實作interface檔:
#import <Foundation/Foundation.h>
#import "CCLayer.h"
#import "cocos2d.h"
@interface GameplayScrollingLayer : CCLayer {
CCSpriteBatchNode *sceneSpriteBatchNode;
CCTMXTiledMap *tileMapNode;
CCParallaxNode *parallaxNode;
CCSprite *icon;
}
@end
#import "CCLayer.h"
#import "cocos2d.h"
@interface GameplayScrollingLayer : CCLayer {
CCSpriteBatchNode *sceneSpriteBatchNode;
CCTMXTiledMap *tileMapNode;
CCParallaxNode *parallaxNode;
CCSprite *icon;
}
@end
注意,第一個變數是儲存可捲動之背景,之後要製作atlas。第二個變數是製作tiled map,第三個變數是製作視差node,最後一個變數是我們要使用的遊戲物件。
然後先在GameplayScrollingLayer.m中實作兩個方法:
-(void)addScrollingBackground {
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CGSize levelSize = CGSizeMake(screenSize.width * 2.0f, screenSize.height);
CCSprite *scrollingBackground;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
//Indicate game is running on iPad
scrollingBackground = [CCSprite spriteWithFile:@"FlatScrollingLayer.png"];
}else {
scrollingBackground = [CCSprite spriteWithFile:@"FlatScrollingLayeriPhone.png"];
}
[scrollingBackground setPosition:ccp(levelSize.width/2.0f, screenSize.height/2.0f)];
[self addChild:scrollingBackground];
}
-(id) init{
CGSize winSize = [CCDirector sharedDirector].winSize;
self = [super init];
if (self != nil) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:@"scene1atlas.plist"];
sceneSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"scene1atlas.png"];
} else {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:@"scene1atlasiPhone.plist"];
sceneSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:@"scene1atlasiPhone.png"];
}
[self addChild:sceneSpriteBatchNode];
self.isTouchEnabled =YES;
icon = [CCSprite spriteWithFile:@"Icon-Small.png"];
icon.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:icon];
[self addScrollingBackground];
[self scheduleUpdate];
}
return self;
}
addScrollingBackground方法是先取得螢幕的大小,然後用螢幕大小製作關卡的實際大小(我這邊和書上不一樣),然後用if迴圈根據使用的設備選用適當的背景圖,最後把背景圖位置設定好,注意這邊是以關卡大小為主,寬度為關卡大小的寬度一半,高度則是螢幕高度的一半。然後再加入這個方法:
-(void) adjustLayer {
float iconXPosition = icon.position.x;
CGSize screenSize = [CCDirector sharedDirector].winSize;
float halfOfTheScreen = screenSize.width/2.0f;
CGSize levelSize = CGSizeMake(screenSize.width * 2, screenSize.height);
if ((iconXPosition > halfOfTheScreen) && (iconXPosition < levelSize.width - halfOfTheScreen)) {
float newXPosition = halfOfTheScreen -iconXPosition;
[self setPosition:ccp(newXPosition, self.position.y)];
}
}
首先取得icon位置的x座標,然後取得螢幕大小,再取得螢幕寬度的一半大小,最後製作出關卡的小,再用這些東西製作if迴圈,回圈是要進行捲動,條件是,如果icon在整個關卡裡面距離頭尾一半螢幕寬度的範圍內的話,那麼算出新的X,就是一半的螢幕寬度減去icon的x位置,最後就是重新設定層的位置。
最後是加入update方法:
-(void) update:(ccTime)delta {
[self adjustLayer];
}
好,編譯看看吧!這時你會發現icon好像在捲動背景後面,你可以調整icon的z值。
使用視差層
在cocos2D裡面,有一個類別叫做CCParallaxNode,這讓你不用自己去寫code去製作視差。Parallax node世一個特別的Cocos2D parent node,它可以設定好讓他的children按不同比率去捲動,現在來直接體驗看看吧!
打開GameplayScrollingLayer.m,加入以下方法:
-(void) addScrollingBackgroundWithParallax {
CGSize screenSize = [CCDirector sharedDirector].winSize;
CGSize levelSize = CGSizeMake(screenSize.width * 2, screenSize.height);
CCSprite *BGLayer1;
CCSprite *BGLayer2;
CCSprite *BGLayer3;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ) {
BGLayer1 = [CCSprite spriteWithFile:@"chap9_scrolling4.png"];
BGLayer2 = [CCSprite spriteWithFile:@"chap9_scrolling3.png"];
BGLayer3 = [CCSprite spriteWithFile:@"chap9_scrolling2.png"];
}else {
BGLayer1 = [CCSprite spriteWithFile:@"chap9_scolling4iPhone.png"];
BGLayer2 = [CCSprite spriteWithFile:@"chap9_scolling3iPhone.png"];
BGLayer3 = [CCSprite spriteWithFile:@"chap9_scolling2iPhone.png"];
}
parallaxNode = [CCParallaxNode node];
[parallaxNode setPosition:ccp(levelSize.width/2.0f, screenSize.height/2.0f)];
float xOffset = 0;
[parallaxNode addChild:BGLayer1 z:40 parallaxRatio:ccp(1.0f, 1.0f) positionOffset:ccp(0.0f, 0.0f)];
xOffset = (levelSize.width/2) *0.3f;
[parallaxNode addChild:BGLayer2 z:20 parallaxRatio:ccp(0.2f, 1.0f) positionOffset:ccp(xOffset, 0)];
xOffset = (levelSize.width/2) * 0.8f;
[parallaxNode addChild:BGLayer3 z:30 parallaxRatio:ccp(0.7f, 1.0f) positionOffset:ccp(xOffset, 0)];
[self addChild:parallaxNode z:10];
}
他首先加入了三個層,然後根據設備的不同會載入不同的圖。接著初始化parallaxNode變數,設定該變數要放置的位置,然後將剛剛建立的三個層加入,並指定不同的比率和z值。
再來,回到init方法裡面,把:
[self addScrollingBackground];
給comment out,換成:
[self addScrollingBackgroundWithParallax];
現在來看一下為什麼要有那些offset,可以進到addChild:z:parallaxRatio:positionOffset:方法裡面:
CGPoint pos = self.position;
pos.x = pos.x * ratio.x + offset.x;
pos.y = pos.y * ratio.y + offset.y;
child.position = pos;
子層的位置,等於CCParallazNode的位置乘上比率後再加上offset。舉例來說,第三個子層的x位置的比率是0.7,你把它設置在1024像素的位置,所以乘起來就是1024 * 0.7 = 716.8,這一層其實歪斜到左邊去,自然要補上offset把它移回來。
至於無限大的捲動關卡,我想寫在下一回好了。
留言
張貼留言