本节书摘来异步社区《Cocos2d 跨平台游戏开发指南(第2版)》一书中的第2章,第2.9节,作者: 【印度】Siddharth Shekar(谢卡)译者: 武传海 责编: 胡俊英,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.9 滚动难度级别选择场景
假如你的游戏有多个难度级别,例如有20个等级,那么只用一个单独的难度级别选择场景来显示所有的级别选择按钮是可以的。但是,要是有更多等级呢?在本部分中,我们将修改前面编写的代码,创建一个节点,并进行初始化,从而产生一个可以滚动的难度级别选择场景。
2.9.1 准备工作
我们将创建一个新类,将其命名为LevelSelectionLayer,它继承自CCNode类。然后,把我们在前面添加到LevelSelectionScene中的所有代码复制到其中。这样一来,我们就有了一个单独的类,在游戏中可以根据实际需要的次数对其进行多次实例化。
2.9.2 操作步骤
在LevelSelectionLayer.h文件中,修改代码如下:
#import "CCNode.h"
@interface LevelSelectionLayer : CCNode {
NSMutableArray *buttonSpritesArray;
}
-(id)initLayerWith:(NSString *)filename
StartlevelNumber:(int)lvlNum
widthCount:(int)widthCount
heightCount:(int)heightCount
spacing:(float)spacing;
@end
在上述代码中,我们修改了init函数,替换掉了硬编码值,使得我们能够创建出一个更富弹性的游戏难度选择层。
在LevelSelectionLayer.m文件中,添加如下代码:
#import "LevelSelectionLayer.h"
#import "LevelSelectionBtn.h"
#import "GameplayScene.h"
@implementation LevelSelectionLayer
- (void)onEnter{
[super onEnter];
self.userInteractionEnabled = YES;
}
- (void)onExit{
[super onExit];
self.userInteractionEnabled = NO;
}
-(id)initLayerWith:(NSString *)filename StartlevelNumber:(int)lvlNum
widthCount:(int)widthCount heightCount:(int)heightCount spacing:
(float)spacing{
if(self = [super init]){
CGSize winSize = [[CCDirector sharedDirector]viewSize];
self.contentSize = winSize;
buttonSpritesArray = [NSMutableArray array];
float halfWidth = self.contentSize.width/2 - (widthCount-1) *
spacing *
0.5f;
float halfHeight = self.contentSize.height/2 + (heightCount-1) *
spacing
* 0.5f;
int levelNum = lvlNum;
for(int i = 0; i < heightCount; ++i){
float y = halfHeight - i * spacing;
for(int j = 0; j < widthCount; ++j){
float x = halfWidth + j * spacing;
LevelSelectionBtn* lvlBtn = [[LevelSelectionBtn alloc]
initWithFilename:filename StartlevelNumber:levelNum];
lvlBtn.position = CGPointMake(x,y);
lvlBtn.name = [NSString stringWithFormat:@"%d",levelNum];
[self addChild:lvlBtn];
[buttonSpritesArray addObject: lvlBtn];
levelNum++;
}
}
}
return self;
}
-(void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event{
CGPoint location = [touch locationInNode:self];
CCLOG(@"location: %f, %f", location.x, location.y);
CCLOG(@"touched");
for (CCSprite *sprite in buttonSpritesArray)
{
if (CGRectContainsPoint(sprite.boundingBox, location)){
CCLOG(@" you have pressed: %@", sprite.name);
CCTransition *transition = [CCTransition transitionCross
FadeWithDuration:0.20];
[[CCDirector sharedDirector]replaceScene:[[GameplayScene
alloc]initWithLevel:sprite.name] withTransition:transition];
}
}
}
@end
其中,粗体代码是主要的修改内容。首先,我们通过onEnter与onExit函数来启用、禁用触摸功能。另一个主要的改变是,我们把节点的contentsize值设置为winSize。并且,在指定按钮的左上角坐标时,我们并没有使用winSize,而使用了节点的contentsize值。
下面,让我们转到LevelSelectionScene类,在LevelSelectionScene.h文件中添加如下代码:
#import "CCScene.h"
@interface LevelSelectionScene : CCScene{
int layerCount;
CCNode *layerNode;
}
+(CCScene*)scene;
@end
如上所示,我们修改了头文件,在其中添加了两个全局变量。
- layerCount变量保存你添加的所有层与节点。
- layerNode变量是一个空节点,方便我们能把所有图层节点添加到它,这样就可以向后或向前移动它,而不用分别移动每个层节点。
接着,在LevelSelectionScene.m文件中,添加如下代码:
#import "LevelSelectionScene.h"
#import "LevelSelectionBtn.h"
#import "GameplayScene.h"
#import "LevelSelectionLayer.h"
@implementation LevelSelectionScene
+(CCScene*)scene{
return[[self alloc]init];
}
-(id)init{
if(self = [super init]){
CGSize winSize = [[CCDirector sharedDirector]viewSize];
layerCount = 1;
//Basic CCSprite - Background Image
CCSprite* backgroundImage = [CCSprite spriteWithImageNamed:
@"Bg.png"];
backgroundImage.position = CGPointMake(winSize.width/2,
winSize.height/2);
[self addChild:backgroundImage];
CCLabelTTF *mainmenuLabel = [CCLabelTTF labelWithString:
@"LevelSelectionScene" fontName:@"AmericanTypewriter-Bold"
fontSize:
36.0f];
mainmenuLabel.position = CGPointMake(winSize.width/2, winSize.
height
* 0.8);
[self addChild:mainmenuLabel];
//empty node
layerNode = [[CCNode alloc]init];
[self addChild:layerNode];
int widthCount = 5;
int heightCount = 5;
float spacing = 35;
for(int i=0; i<3; i++){
LevelSelectionLayer* lsLayer = [[LevelSelectionLayer
alloc]initLayerWith:@"btnBG.png"
StartlevelNumber:widthCount * heightCount * i + 1
widthCount:widthCount
heightCount:heightCount
spacing:spacing];
lsLayer.position = ccp(winSize.width * i, 0);
[layerNode addChild:lsLayer];
}
CCButton *leftBtn = [CCButton buttonWithTitle:nil
spriteFrame:[CCSpriteFrame frameWithImageNamed:@"left.png"]
highlightedSpriteFrame:[CCSpriteFrame frameWithImageNamed:
@"left.png"]
disabledSpriteFrame:nil];
[leftBtn setTarget:self selector:@selector(leftBtnPressed:)];
CCButton *rightBtn = [CCButton buttonWithTitle:nil
spriteFrame:[CCSpriteFrame frameWithImageNamed:@"right.png"]
highlightedSpriteFrame:[CCSpriteFrame frameWithImageNamed:
@"right.png"]
disabledSpriteFrame:nil];
[rightBtn setTarget:self selector:@selector(rightBtnPressed:)];
CCLayoutBox * btnMenu;
btnMenu = [[CCLayoutBox alloc] init];
btnMenu.anchorPoint = ccp(0.5f, 0.5f);
btnMenu.position = CGPointMake(winSize.width * 0.5, winSize.height
*
0.2);
btnMenu.direction = CCLayoutBoxDirectionHorizontal;
btnMenu.spacing = 300.0f;
[btnMenu addChild:leftBtn];
[btnMenu addChild:rightBtn];
[self addChild:btnMenu z:4];
}
return self;
}
-(void)rightBtnPressed:(id)sender{
CCLOG(@"right button pressed");
CGSize winSize = [[CCDirector sharedDirector]viewSize];
if(layerCount >=0){
CCAction* moveBy = [CCActionMoveBy actionWithDuration:0.20
position:ccp(-winSize.width, 0)];
[layerNode runAction:moveBy];
layerCount--;
}
}
-(void)leftBtnPressed:(id)sender{
CCLOG(@"left button pressed");
CGSize winSize = [[CCDirector sharedDirector]viewSize];
if(layerCount <=0){
CCAction* moveBy = [CCActionMoveBy actionWithDuration:0.20
position:ccp(winSize.width, 0)];
[layerNode runAction:moveBy];
layerCount++;
}
}
@end
2.9.3 工作原理
在上述代码中,重要的代码都已经使用粗体标识出来。除了添加通用的背景与文本以外,我们还把layerCount初始化为1,并且也对空节点layerNode进行了初始化。
接着,我们创建了一个for循环,在其中,通过传递btnBg图像中每个选择层的初值、宽度值、高度值与按钮间的spacing值,我们添加了3个难度级别选择层。
并且,请注意这些层是如何以屏幕宽度单位进行定位的。第一个层对玩家是可见的。其他连续层被添加到屏幕之外,这与创建视差效果时添加第二张脱屏图像所采用的方式一样。
然后,把每个级别选择层添加到layerNode之中。
此外,我们还创建了左移与右移按钮,每次单击它们,都能把layerNode向左或向右移动。相应地,我们创建了leftBtnPressed和rightBtnPressed两个函数,当按下左移按钮或右移按钮时,它们就会被调用执行。
首先,让我们一起看一下B函数。一旦按下右移按钮,rightBtnPressed函数就会被调用执行,先向控制台输出相应信息,而后获取窗口大小,接着检查layerCount值是否大于0,由于前面我们把layerCount设置为1,所以判断结果为真,创建moveBy动作,指定沿x轴负方向移动窗口宽度大小,沿y轴方向移动为0,因为我们只想让移动沿着x轴进行,而沿y轴方向不做移动。最后,设置移动动作的持续时间为0.20f。
然后,layerNode执行moveBy动作,并且把layerCount值减1。
而在leftBtnPressed函数中,会把层向相反方向,即往左移动。运行游戏,观察LevelSelectionScene中的变化,如图2-14所示。
由于不能往左移,所以按左移按钮不会有任何作用。然而,如果按右移按钮,你将会看到场景层发生滚动,显示出下一部分按钮,如图2-15所示。