使用Cocos2D和Box2D制作《Jetpack Joyride》part-2

本文由游戏帮翻译,泰然授权转载,转载请联系原文作者!

作者:Bogdan Vladu

前面我们已经制作出带有背景美工元素的可行关卡(第1部分详见此处)。

在第2部分中,我们将着眼于添加游戏活动元素,包括激光、硬币、飞行角色及初始触碰活动等。

要继续本教程,你需持有我们第1部分谈到的RocketMouse项目内容。

clip_image001

Jetpack Joyride from raywenderlich.com



入门指南

制作《Jetpack Joyride》游戏的下一步是,添加可供玩家直接互动的游戏元素:激光和硬币。添加这些元素也是学习如何基于LevelHelper落实动画和激光的绝佳方式。

由于我们希望激光间断运作,所以我们需要将其制作成动画效果。至于硬币,我们希望把握玩家同其互动的时间点,但我们不希望玩家弹开它们。要做到这点,我们需要赋予其传感能力。

把握动画和触碰反应基本要素后,我们会将话题转移至游戏的其他元素,包括游戏玩家。我们将学习如何创造标签,追踪触碰活动,然后将其同若干真实代码整合。

在开始内容前,请打开你的当前LevelHelper项目,将关卡状态设置成level03。在进行此过程时,将这一新关卡添加至Xcode项目中,按照下列操作更新代码,将关卡状态设置成level 3:

lh = [[LevelHelperLoader alloc] initWithContentOfFile:@”level03″];

着手动画元素:添加激光

我们对于玩法动画元素的运用相当简单。当玩家同激光接触时,我们只需测试动画帧所处位置。若动画帧处于激光关闭状态,玩家就不会有性命之忧,如果处于激光开启状态,玩家就会死亡。

要创造激光动画,我们需要查看LevelHelper右侧的图像列表,右击其中的一个激光图像。然后在菜单中选择“Open SpriteHelper scene”。

clip_image003

openSHSceneFromSpritesList

SpriteHelper会呈现正确场景。转换至Animation版块,点击Add Frame。这一操作将给动画添加两个精灵。

通过点击上下箭头,以正确顺利安排游戏画面。将速度设定成3,选中Start At Launch和Loop Forever选项。

clip_image005

SHFirstAnimation

完成这些后,点击Command-S保存画面。

现在我们回到LevelHelper,着手处理我们刚刚创建的动画。此时你会发现Animations版块已被添加至LevelHelper中,如下图所示。

clip_image007

animationsInLH

激活动画有两种方式。我将逐一进行陈述。

第一种方式就是将动画拖到关卡(游戏邦注:这和操作精灵的方式类似)。

clip_image009

dragAnimationInLevel

第二种方式是,将动画同已存在于关卡中的精灵连接起来。在这种情况下,我会将动画添加至激光画面中。

clip_image011

spriteSectionAnimExample

现在进入精灵版块。我们还没有将激光精灵添加至关卡中,所以现在要将列表中的一个精灵拖到关卡中,要选定此精灵。然后跳到General Properties版块,从列表中选择激光动画。

clip_image013

spriteSectionAnimExample 2

继续在关卡中添加激光,直到达到满意的危险系数。你可以基于选择精灵时出现的控键旋转和调节laser。

我的关卡现在呈现如下样式:

clip_image015

levelWithLasers

若查看laser形状,你会发现精灵现在呈现四方形模式。

clip_image017

laserShapeFull

就我们的游戏来说,我们希望老鼠只在击中真正的激光时死去,所以我们不妨缩小模型的尺寸。

要做到这点,我们可以采用定义小猫和小狗形状的方式,或者我们可以通过预定义的数值修改Physics菜单下的Shape Border属性。现在你已知道如何通过位点创造物件形状,下面就来尝试第二种方式。

在LevelHelper的Images版块中选择laser1图像,然后右击,选择Open SpriteHelper Scene。

当呈现SpriteHelper scene时,点击“laser1”精灵。由于激光是个动画,我们需要修改动画首个帧数的物理属性——在此就是指“laser1”精灵。动画包含第一个帧数的物理属性。

选中laser1后,进入Physics菜单,在Shape Border数值中输入70和40。精灵的最终模型大小是原本尺寸减去你所输入的数值。你可以在画面上看到视觉表征。

clip_image019

laserShapeDefine

回到LevelHelper,我们会看到所有laser都被更新为正确形状。

现在选中所有laser,将它们添加到我们的parallax(游戏邦注:影视编辑软件)。按比例输入1和0。

保存关卡,若创建和运行游戏后你会发现,它现在具备动态laser。

clip_image021

AnimatingLasers

处理传感设置:添加硬币

现在就轮到硬币。记住,我们想要创建硬币传感器,这样老鼠就能够直接穿过它们,不会弹开,但我们依然能够察觉到触碰活动的发生。

要创造硬币传感器,从LevelHelper中的精灵列表中选择硬币精灵,右击,然后选择Open SpriteHelper Scene。

clip_image023

openCoinSHScene

开放SpriteHelper scene后,选择硬币精灵,选中Physics菜单中的Is Sensor、Is Circle和Can Sleep选项。然后保存画面。

* Is Sensor:让身体触发触碰活动,但不呈现触碰反应

* Is Circle:让身体的形状呈现圆形模式

* Can Sleep:加速物理元素的模拟过程

clip_image025

setCoinSensor

现在将硬币精灵添加到关卡,将其放在你认为合适的位置。通过Clone Tool复制硬币,基于你青睐的位移方向。

clip_image027

cloneToolDirection

现在我的关卡呈现如下模式:

clip_image029

levelWithCoins

将硬币添加至parallax,将比例调成1和0,然后保存关卡。编辑和运行你的项目,现在你拥有硬币元素。

clip_image031

AddingCoins

添加玩家元素

现在我们拥有包含基本要素的不错关卡,下面就来创建玩家角色及其相应的动画内容。

打开SpriteHelper,进入File\New创建空的精灵工作表。然后在Finder中点击目录(游戏邦注:这里保存游戏的美工元素)。在此你需要将所有老鼠精灵和火箭火焰拖到SpriteHelper窗口。

clip_image033

mouseAnimationDrag

现在我们需要通过刚输入的美工元素创建单个画面。这是创建动画前的必要步骤。LevelHelper和SpriteHelper支持的所有引擎都将动画帧数视作图像文件的组成元素。

现在切换到Sheet Editor菜单,取消选定Crop,因为我们不希望改变老鼠动画的帧数。点击Pack Sprites,将所有精灵都分配到精灵表单中。

clip_image035

packMouseFrames

然后,切换到SpriteHelper的Animation版块。我们将在此创建所有必要的动画内容,和我创建laser的过程一样。

运用你先前学到的技巧,创建包含下述属性的5个动画内容。

若你忘记如何创建动画,点击+按键,双击进行重命名。然后选定动画的规定帧数,设定速度,记得给需要进行循环的动画选定Loop Forever。

1)动画名称:mouseRun

Loop Forever: YES

Speed: 0.400(默认)

Repetition: 1.000(默认)

Frames: rocketmouse_1_run, rocketmouse_2_run, rocketmouse_3_run, rocketmouse_4_run

clip_image037

mouseRun

2)动画名称:flame

Start At Launch: YES

Loop Forever: YES

Speed: 0.400(默认)

Repetition: 1.000(默认)

Frames: rocket_flame1, rocket_flame2

clip_image039

rocketFlame

3)动画名称:mouseFly

Start At Launch: YES

Loop Forever: YES

Speed: 0.400(默认)

Repetition: 1.000(默认)

Frames: rocketmouse_5_fly

clip_image041

mouseFly

4)动画名称:mouseDie

Start At Launch: YES

Loop Forever: NO

Speed: 0.400(默认)

Repetition: 1.000(默认)

Frames: rocketmouse_7_die, rocketmouse_8_die

clip_image043

mouseDie

5)动画名称:mouseFall

Start At Launch: YES

Loop Forever: NO

Speed: 0.400(默认)

Repetition: 1.000(默认)

Frames: rocketmouse_6_fall

clip_image045

mouseFall

完成这些操作后,点击Command-S,保存画面。在Save对话框中,点击Xcode项目Resources文件夹中的Images文件,将画面保存为“mouse”。

clip_image047

saveMouseImage

回到LevelHelper,我们将在Animation区块中找到所有新创建的动画内容。

将mouseRun动画拖到主页面(有红色边界的页面),然后置于页面左侧。

然后拖动火焰精灵,将其放置在老鼠背后的红色坦克下。

最终画面如下:

clip_image049

mousePlacement

仔细观察你会发现,火焰精灵位于红色坦克之上。不妨将其放在坦克之后,这样火焰看起来会更像是由坦克产生的。完成这一操作后,选择火焰精灵,然后在General Properties中将Z Order调成-1。

clip_image051

zOrderOnFlame

若你此时通过Scene Tester运行关卡,你可能会看到老鼠,但也可能没看到。

这是因为精灵是通过成批节点进行渲染。要将老鼠放在关卡其他精灵顶部,我们需要改变成批节点的Z Order。

切换到LevelHelper的Images区块,双击mouse.png的Z Order字段。输入数值4。

我们希望玩家把握所有内容,我们很快将添加更多图片。这就是为什么我们要在这里设置一个更大的数值。

clip_image053

batchZOrder

现在运行关卡,你会看到老鼠在画面中奔跑,但没有发生任何触碰活动。

它其实有何小狗和小猫发生触碰,但由于老鼠和其他物件都处于静止状态,所以什么也没有发生。

现在是时候把老鼠和火焰精灵设置成动态模式。打开老鼠的SpriteHelper情境:

clip_image055

openMouseSH

然后选择所有精灵,将其设置成动态模式。我们不希望老鼠在和其他精灵触碰时发生旋转,所以还要选择Fixed Rotation option。

clip_image057

makeMouseDynamic

至于两个火箭火焰精灵,我们还需要勾选Is Sensor选项。这是因为随后我们将把火焰和火箭坦克结合起来。

clip_image059

flameProperties

完成操作后保存火箭坦克。

再次运行Scene Tester,我们将会看到老鼠在画面中掉落。这是老鼠对地心引力所做出的反应。不妨让它继续停留在屏幕上。

点击Physic Boundaries按键。

clip_image061

pressOnPBoundary

在Physic Boundaries窗口点击Create。

clip_image063

createPBoundary 1

我们现已创建边界,但想要对其进行编辑,使其出现在地板中间的老鼠的下方,老鼠会在这块区域中行走。点击Physic Boundaries窗口中的Edit。

clip_image065

pressOnEditBoundary

现在你会在物理边界的拐角处看到4个红色控键。你可以拖曳任何底部控键移动边界,确保边界始终处在老鼠的脚下。

clip_image067

dragPBoundaryHandle

你将看到类似的画面:

clip_image069

correctHandleBoundary

若你对物理边界没有什么意见,点击Editing按键,中断编辑过程。

clip_image071

stopEditingPBoundary

在Scene Tester中运行关卡,你将看到老鼠和小猫&小狗发生触碰,然后继续停留在屏幕中,但这里还存在另一问题。

这次,火焰在屏幕中落下,它有传感设置,所以不会和任何物体触碰,不会附着于老鼠的身体。但我们可以通过创建远距离接合点,将火焰和老鼠联系起来。

现在切换到LevelHelper中的Joints版块,选择列表中的Distance Joint,点击绿色+按键。

clip_image073

createDJoint

现在我们可以选择接合点要连接的精灵。

选择列表中的接合点,然后是Body A的属性,点击圆形图标。然后将老鼠拖至火焰精灵。当文本显示rocket_flame_1时,放开老鼠。

clip_image075

connectJointFlame

现在在Body B中重复相同操作,但这次要选择老鼠。

clip_image077

connectJointMouse

现在将接合点的定位点拉得更近些。

在接合点属性中选择A,拖曳显示火焰精灵的手柄,将锚点置于坦克之上。

clip_image079

jointAnchorA

在其他定位点中重复这一操作。选择锚点B,拖曳把手,将此锚点置于其他定位点的旁边。

clip_image081

jointAnchorB

若你在Scene Tester中运作关卡,你会看到火焰现在附着于老鼠身上。

现在我们已完成进入编码工作前所需的所有内容。

创建标签,执行触碰活动

要执行精灵间的触碰活动,我们需要记录同类精灵与其他类型精灵的触碰活动。

我们需要通过标签将精灵分类。所以所有有关小狗的精灵都会被标上“DOG” ,而所有涉及小猫的精灵都会被标上“CAT”。

要创建这些标签,需在LevelHelper中点击Define Tags按键。

clip_image083

defineTagsButton

在Define Tags窗口中,给新标签设定名称,点击Add按键创建新标签。需要创建如下标签:DOG、CAT、LASE、COIN和PLAYER。

clip_image085

pressAddTag

明确定义标签后,我们需要将这些标签分配到各个精灵中。

选择左边精灵列表中的所有小狗精灵。然后在General  Properties中将这些DOG标签分配到上述精灵。

clip_image087

assignDogTag

然后在所有精灵中重复这一操作:将PLAYER标签分配至老鼠精灵中,CAT分配至各小猫精灵,LASER分配到各种激光精灵,COIN分配到硬币精灵中。

完成这些操作后,以Command-S保存关卡。

编写游戏逻辑的代码

我们现在准备着手编写游戏代码。所以我们重新回到Xcode,打开我们的项目,开始真正的趣味之旅。

首先,确保将新的美工元素(游戏邦注:即mouse.png)纳入其中。此时的资源文件夹将呈如下模式:

clip_image089

newArtAndLevels

为控制parallax的运作,让玩家能够进行跳跃,我们需要设定若干指示LevelHelper物件的变量。将HelloWorldScene.h的这些代码添加至类别定义中。

LHParallaxNode* paralaxNode;
LHSprite*   player;
b2Body*     playerBody;
LHSprite*   rocketFlame;

随后在类别定义之外,即@end和+(id)scene之间,添加下列方法标记(method signature):

-(void) retrieveRequiredObjects;

在HelloWorldScene.mm中定义新方法(可以选择任何适当位置,但要确保其处在类别定义之中)。我将其放置于初始方法之后。

-(void) retrieveRequiredObjects
{
//Retrieve pointers to parallax node and player sprite.
paralaxNode = [lh paralaxNodeWithUniqueName:@"Parallax_1"];
NSAssert(paralaxNode!=nil, @”Couldn’t find the parallax!”);
player = [lh spriteWithUniqueName:@"player"];
NSAssert(player!=nil, @”Couldn’t find the player!”);
playerBody = [player body];
NSAssert(playerBody!=nil, @”Error taking the body from the player LHSprite.”);
rocketFlame = [lh spriteWithUniqueName:@"flame"];
NSAssert(rocketFlame!=nil, @”Couldn’t find flame sprite!”);
[rocketFlame setVisible:NO]; //You can do it in LH, but I do it here so you guys can see it in LH
}

这里我们将把目光转至parallax,赋予LevelHelper parallax标识名称。

clip_image091

parallaxName

再来就是玩家精灵,也是赋予它们标识名称。若标识名称原先没有修改,要将其改成“player”,如下面的截图所示。

clip_image093

playerName

同样,你得把火焰的标识名称改成“flame”,以同代码相匹配。

若你通过spriteWithUniqueName查看精灵,你将得到一个LHSprite*实例,这是源自CCSprite的一个类别。通过此LHSprite*实例,我们将指示器转移到玩家的Box2d身体,因为我们随后要这一身体进行跳跃。

现在我们转移到火焰精灵,将此画面隐藏起来。只有当玩家飞起来的时候,我们才会看到此火焰。

现在我们已完整定义此新方法,不妨将其称作:

lh = [[LevelHelperLoader alloc] initWithContentOfFile:@”level03″]; // or level02 if you never changed it
[lh addObjectsToWorld:world cocos2dLayer:self];
if([lh hasPhysicBoundaries])
[lh createPhysicBoundaries:world];
if(![lh isGravityZero])
[lh createGravity:world];
// Add this
[self retrieveRequiredObjects]; // Retrieve all objects after we’ve loaded the level.

编辑和运行代码,若火焰运行时无法为我们所见,那么操作就圆满完成。

clip_image095

MouseInvisibleFlame

让玩家在空中飞行

现在我们重新取回加载关卡中的玩家,让其在空中飞行。

在HelloWorldScene.h类别定义中添加如下内容:

float  playerVelocity;
bool   playerWasFlying;
bool   playerShouldFly;

通过这一操作,我们让老鼠在用户触摸屏幕时飞入空中。

在HelloWorldScene.mm中将触控功能替换成如下内容:

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
playerVelocity = 0.5f;
playerShouldFly = true;
[rocketFlame setVisible:YES];
[player startAnimationNamed:@"mouseFly"];
}
////////////////////////////////////////////////////////////////////////////////
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}
////////////////////////////////////////////////////////////////////////////////
-(void) cancelPlayerFly
{
playerShouldFly = false;
[rocketFlame setVisible:NO];
playerWasFlying = true;
playerVelocity = 0.0f;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self cancelPlayerFly];
}
- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self cancelPlayerFly];
}

在ccTouchesBegan方法中,当用户触控屏幕时,玩家就会开始飞翔,火箭的火焰就会进入我们的视野,飞翔动画就会出现在玩家精灵中。

接着我们就会在玩家的LHSprite*实例(游戏邦注:这由我们通过retrieveRequiredObjects方法取回)中启动“mouseFly” 动画。

当玩家停止触控屏幕,或取消触控时,我们就会通过调用“cancelPlayerFly”新方法让玩家停止飞翔。然后我们会继续隐藏火焰,因为玩家已不在空中飞翔。

但这远远不够。我们设定呈现玩家飞翔的时间,但我们没有落实真实的飞行操作。这需要我们在“tick”方法末尾添加如下内容:

if(playerShouldFly)
{
playerBody->ApplyLinearImpulse(b2Vec2(0, playerVelocity), playerBody->GetWorldCenter());
playerVelocity += 0.01f;
if(playerVelocity > 1.5f)
playerVelocity = 1.5f;
}

这里,我们会判断玩家是否应该飞翔,及测试操作是否正确,我们在老鼠的box2d身体中施加水平线性冲量。

然后我们会将速率变得越来越快,这样玩家就仿佛真的飞离地面,而且速度越来越快。

若玩家的速度到达一定程度(1.5),我们会停止加速。

编辑和运行游戏,现在你可以通过触控屏幕让老鼠在空中飞翔。

clip_image097

标签: Jetpack Joyride, cocos2d教程

?>