免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!


本教程由 无敌葫芦娃翻译,校对:蓝羽、子龙山人


教程截图:

如果你曾经在COCOS2D中手工布局过游戏关卡,你应该有体会那是一件多么痛苦的事情。

在没有工具帮助的情况下,你会发现你老是先猜测一个坐标位置,再运行测试看效果,然后调整坐标位置再测试,不断地重复着这些事情。

还好,因为一些工具的出现,制作游戏关卡的工作会变得更简单一些了。

在这个教程里,我将向你展示怎样利用SpriteHelper 和 LevelHelper 这两个工具,同时结合Cocos2d和Box2d来创建一个简单的Platformer游戏。

为了完成这篇教程的学习,你需要购买LevelHelper 和SpriteHelper这两个软件,但是如果你没有这两个软件,你可以先只是读一下这篇教程,学习一下这两个工具如何使用,在本教程的最后,你可以下载代码运行看看效果。

本教程假设你已经熟悉怎样使用COCOS2D和BOX2D。如果你还是个COCOS2D或者BOX2D的新手,建议你先看一下我的博客上面的其他COCOS2D和BOX2D的教程。

透露一下:我是从原作者那里免费获得这两个软件的,而且这两个工具也在我的网站上做了广告,但是我的评论和建议是以我作为一个游戏开发者的角度出发的,我绝对不是托。

游戏概览

在教程里,我们将制作一个非常简单的游戏,玩家扮演一个小天使试着从关卡的这头跳到另一个头,同时要避免不从空缺处掉下去,否则就game over了。

我们将使用LevelHelper 和 SpriteHelper这两个工具, 来创建一个Actor在滚动关卡中运动的游戏,以后的教程里我们还会增加一些的其他好玩的特征。

要制作这个游戏,请先下载一些由我妻子为大家制作的图片。

这个游戏 就叫做 Raycast ,因为之后 我会将 BOX2D RayCasting 整合到游戏中,并且我认为这个游戏非常棒!

Getting Started

打开XCODE,到File\New\New Project, choose iOS\cocos2d\cocos2d_box2d,点击NEXR,将新的工程命名为Raycast,点击Next,选一个文件夹保存我们的工程,点击Create。

从你下载的 游戏资源中,将字体和声音等资源文件拖拽到工程中,别忘了选择 “Copy items into destination group’s folder (if needed)” 单击Finish。

现在 在HelloWorldLayer.h 和 HelloWorldLayer.mm 文件中包含了一些示例的Box2D代码,这里我们删掉这些代码,创建一个空的Layer。

按住Control 单击HelloWorldLayer.h 和 HelloWorldLayer.mm,选定,右击彻底删除这两个文件。

然后选择File\New\New File, 选择 iOS\Cocoa Touch\Objective-C class, 点击 Next. 基类为 NSObject, 点击NEXT, 将新类命名为 ActionLayer.mm, 点击 Save.

把 ActionLayer.h 替换成下面的代码:

#import "cocos2d.h"

@interface ActionLayer : CCLayer {}

+ (id)scene;

@end

这里声明了一个CCLayer的子类,同时定义了一个静态方法来创建一个新的CCScene。

在 ActionLayer.mm 加入:

#import "ActionLayer.h"

@implementation ActionLayer

+ (id)scene {    CCScene *scene = [CCScene node];    

    ActionLayer *layer = [ActionLayer node];    [scene addChild:layer];    

return scene;}

@end

这里实现了刚刚的那个静态方法,用来创建一个新的CCScene,同时把该层当作唯一的孩子添加进去。

最后, 在 AppDelegate.mm 做如下的改动:

// Replace #import "HelloWorldLayer.h" with this:#import "ActionLayer.h"

// Replace call to runWithScene with this:[[CCDirector sharedDirector] runWithScene: [ActionLayer scene]];

编译运行工程,这个空的Scene将作为我们的起点。

SpriteHelper and Sprite Sheet Generation

SpriteHelper是一个结合了Texture Packer(spritesheet生成工具)和Physics Editor(物理shape定义工具)的部分功能的工具。

如果要比较他们之间的特点,TexturePacker和Physics Editor更简洁,他们有许多好用的特性,像抖动,支持命令行,与COCOS2D无缝集成,自动的外形追踪(shape-trace)等等。

但是 SpriteHelper 真正的闪光之处在于它和LevelHelper的完美组合,一旦你使用了SpriteHelper创建了 Spritesheet 和shapes,你可以使用LevelHelper来做场景展示,而如果你是用其他的工具,那么这个过程将不得不手动完成。

那么我们试一下!打开SpriteHelper出现以下的窗口:

打开Finder找到你下载的 图片等资源,将目录中所有的图片拖拽到SpriteHelper的方格区域。

这些图片将会互相重叠着出现,按照如下设置,让SpriteHelper自动排布这些Sprites.

确保Crop 处于未选定状态。如果这个选定了,他将移走sprites周围的透明域,但是我们不希望他这样做,因为透明的区域对于我们做的动画来讲很重要。

确保 Save SD 选中,我们的图片是HD版本,选定SD在我们保存时,将会自动的创建一个缩放了一半的SD版本。

确保NPOT未选中。这样将以NPOT(NON POWER OF TWO)的形式保存Spritesheet,

这样在iPad, iPhone 4, and iPod Touch 4th,这些能读NPOT textures的设备上就不必像在旧设备上那样将他们膨胀到最接近的POT(POWER OF TWO )形式,因而节省了texture 内存。

注意你已经在CCCONFIG.H中将COCOS2D中的CC_TEXTURE_NPOT_SUPPORT 激活。

最后,点击Pack Sprites, spritehelper将会自动地排列Sprites.

至此,你已经完成了使用SpriteHelper制作sprite sheet的部分,接下来让我们进入物理SHAPE定义部分。

SpriteHelper and Physics Shape Definitions

对于每一个你加入SpriteHelper中的Sprite,你可以将物理的Body映射到sprite上。

默认的,他映射一个与sprite的边界吻合的矩形Body。你可以通过设置shape border使这个矩形略小,通过设置 is Circle 将他设置成圆形,或者通过 Create Shape 按键 创建你自己的多边形。

你也可以设置其他的物理属性:

  • IS SENSOR: 如果这个为TRUE,这个物体可以检测碰撞,但是可以穿过其他的物体。
  • SHAPE TYPE:  Static 意味着 “不动”,Kinematic 意味着“通过设置速度移动,对于力或者摩擦不作响应”,Dynamic意味着“在BOX2D中的正常运动”通常对于地面要使用 static,对于其他的大多数物体使用dymatic 。
  • DENSITY: 移动body的难度(惯性)
  • RESTITUTION :BODY的弹性(0没有弹性,1完全弹性)
  • MaskBit: 一个用于标识BODY属于哪一类。
  • GroupIndex :任何有着相同的Group Index的body将有一直有碰撞产生(如果Index是正的)或者从不产生碰撞(如果Index是负的),这种关系屏蔽了 Category和mask的设置,我个人更偏向于使用mask和category bits而不是 group indices,因为我发现 他们更容易使用。

我发现如果你不事先在纸上将category写出来, 设置MASK BITS 和 CATEGORY BITS 将会很困难。 这里是在这个游戏中我们将使用的CATEGORY和MASKS(注意到所有的categories 必须是2的几次方,因为他们都是存放在一个bitfiled中)

•Laser: Category 1. 应该可以和heros (2)碰撞, 所以他的 mask bit 是 2.

•Hero: Category 2. 应该可以和 lasers (1) 与 clouds (4),碰撞, 所以他的 mask bit 是 5.

•Cloud: Category 4. 应该可以和 heros (2) 与 monsters (8)碰撞, 所以他的 mask 是 10.

•Monster: Category 8.应该可以和 clouds (4)碰撞, 所以他的 mask bit 是 4.

经过以上设置, 设置 shapes 和 properties 将变得简单! 检查每一个 shape 按照如下设置.

Monster

这里设置BODY为Dynamic,用一个带有一点边界的圆形SHAPE作为MONSTER的shape。

Cloud

这里,将body设置为static(因为他不动),用一个带有大边界的方形的SHAPE更好的与cloud匹配。我们也设置了一点摩擦,防止hero在它上面滑动的太厉害。

Laserbeam

设置 body为kinematic,因为将通过手动设置速度来移动laser,并且我们不想让其他任何的力(像重力)影响它,我们也把它设成 sensor,让它不会被其它任何body弄停。

Hero (一次选中所有 4 个sprites)

这是一个Dynamic 的body,他有一个 SHAPE边界来更好的覆盖主body,Friction被设置的很大,用来防止滑动,并且bounciness 被减少了,我们也将body设置成Fixed ROTATION ,这样角色body就不会旋转。

完成!

到此 使用SpriteHelper完成了,到FILE\SAVE ,如果 弹出框出现点击yes,在你的设备上保存为“Sprites”(SPRITER将会自动将.PNG添加到保存位置)。另一个SAVEAS对话框将会出现,再次输入“Sprites”(SpriteHelper 将自动将.pshs添加到该位置)。

如果你没有LevelHelper,你可以使用SpriteHelper来生成一些代码,你可以使用它们在你的游戏中生成 SPRITE SHEET 和物理数据,但是我们有 levelhelper,那么让我们继续!

Getting Started with LevelHelper

打开LevelHelper,将出现:

通过点击 工程旁边的“+”按钮。在出现的对话框的左边输入“Raycast”作为新的工程名称,选择下面的“Iphone Landscape(480*320)”,点击“CreateNEW project”

在一个XCODE的工程中你可以有多于一个层,你可以在这里设定该层属于哪个工程,这个之所以很重要是因为你为每一个XCODE工程生成的代码将包含一些你不仅在LevelHelper也在代码中引用的特征。

然后在屏幕的底部,设置这些值,

•Game World Size: 设置成 (0,0,968,320). 这是对于玩家来说可见的区域.这里把它设置成IPhone屏幕的两倍的宽度以保证 层小并且容易控制,并且容易微调!

•Can Drag Outside World: 设置成选定.这里之后需要将一个物体拖拽到世界边界之外,

•Physic Boundaries: 设置成 (0,0,968,450).

其实如你所知,BOX2D是没有边界的,你可以使用LevelHelper来创建一个边界,以此将物体限定在创建的边界之内。

对于这个游戏,阻止player从左、右、上飞出world是有用的,

注意到,我把底部的边界设定的更低一些,因为在Level上player可以掉进洞里!

•Graivty: 设置成 (0, -10). 这是个重力的估算值(-9.8 m/s^2)

另外,开始时有一件事我不是很明白,那就是怎样滚动 Game world。

要做这个,按住CONTROL并且拖拽(drag in)编辑器,你可以排布(pan)视图。

现在,导入精灵!到File\Import SpriteHelper Scene,选择事先使用SpriteHelper 制作的Sprites.pshs 文件,将看到一列sprites出现在右边(如果没有,点击顶部的第一个tab,把他选定)。

另外,要知道一旦从SpriteHelper导入了一个scene,但是回去重新再次添加另一个sprite 到spritehelper scene里面的时候,会引起重新排布。如果这时在你的levelhelper scene中使用新的sprite,这个过程将变得非常困难。

我曾尝试这样做过,结果将我现存的level给弄乱了(错误的sprites出现在游戏中)。LevelHleper很好用,但只有你将你的game art完成,并且在之后不会再做改动才行。

考虑到事实上,在你做开发时,game art 改动的非常频繁,这还是软件的缺陷,期待更新的版本能够解决这个问题。

OK——现在可以排布关卡(level)了,先在底部做简单的一列cloud——之后我们将回来完成关卡的设计。

从列表中拖拽一片cloud到屏幕的最左下角,然后选定这个cloud,点击在底部工具条上的小箭头按钮,

可以通过这种方法 在相同的一组offset下 自动 重复添加cloud 若干次(而不用重复拖拽若干次),输入15作为复制的份数,68作为 X offset,0 作为 Y offset,点击 Make Clones:

一列 cloud应该出现在关卡的底部,然后从列表中拖拽一个Monster到关卡中的右上方某处,

到这里,我们就先做这些! 到 File \Save 定位目录到到 你的 Raycast 项目的位置,保存为 TestLevel。LevelHelper 将自动生成一个plhs文件。

然后,使用LevelHelper生成我们需要读取的代码。

在主工具条中,点击Download Code,这将会从一个服务器上下载最新的代码模板,然后File\Generate Code\Cocos2D with Box2D,定位到你的Raycast 工程目录,选择目录生成文件。

会在你的工程目录下生成两个文件,LevelHelperLoader.h 和 LevelHelperLoader.mm,现在使用它们!

Integrating LevelHelper with Cocos2D

首先,我们将把SpriteHelper 和LevelHelper生成的文件导入到工程中,选择你使用SpriteHelper 保存的 Sprites.png 和 Sprite-hd.png,将它们拖拽到你的工程目录中,注意选中 “Copy items into destination group’s folder”单击Finish。

打开 ActionLayer.h 作如下的改动,

// Add to top of file#import "Box2D.h"#import "GLES-Render.h"#import "LevelHelperLoader.h"

// Add inside @interfaceb2World * _world;GLESDebugDraw * _debugDraw;LevelHelperLoader * _lhelper;

这里导入了需要的头文件,预先声明了Box2d world,debug drawing ,level helper loader class。

#import "ActionLayer.h"#import "SimpleAudioEngine.h"

@implementation ActionLayer

+ (id)scene {    CCScene *scene = [CCScene node];    

    ActionLayer *layer = [ActionLayer node];    [scene addChild:layer];    

return scene;}

- (void)setupWorld {        b2Vec2 gravity = b2Vec2(0.0f, 0.0f);bool doSleep = false;     _world = new b2World(b2Vec2(0,0), doSleep);}

- (void)setupLevelHelper {    _lhelper = [[LevelHelperLoader alloc] initWithContentOfFile:@"TestLevel"];    [_lhelper addObjectsToWorld:_world cocos2dLayer:self];    [_lhelper createWorldBoundaries:_world];    [_lhelper createGravity:_world];}

- (void)setupDebugDraw {        _debugDraw = new GLESDebugDraw([_lhelper pixelsToMeterRatio] *                                   [[CCDirector sharedDirector] contentScaleFactor]);    _world->SetDebugDraw(_debugDraw);    _debugDraw->SetFlags(b2DebugDraw::e_shapeBit |                          b2DebugDraw::e_jointBit);}

- (void)setupAudio {    [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"Raycast.m4a"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"ground.wav"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"laser.wav"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"wing.wav"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"whine.wav"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"lose.wav"];    [[SimpleAudioEngine sharedEngine] preloadEffect:@"win.wav"];}

- (id)init {if ((self = [super init])) {        [self setupWorld];        [self setupLevelHelper];        [self setupDebugDraw];        [self setupAudio];        [self scheduleUpdate];    }return self;}

- (void)updateLevelHelper:(ccTime)dt {    [_lhelper update:dt];}

- (void)updateBox2D:(ccTime)dt {    _world->Step(dt, 1, 1);    _world->ClearForces();}

- (void)updateSprites:(ccTime)dt {

for (b2Body* b = _world->GetBodyList(); b; b = b->GetNext())   {if (b->GetUserData() != NULL)         {         CCSprite *myActor = (CCSprite*)b->GetUserData();            if(myActor != 0)            {//THIS IS VERY IMPORTANT - GETTING THE POSITION FROM BOX2D TO COCOS2D                myActor.position = [_lhelper metersToPoints:b->GetPosition()];                myActor.rotation = -1 * CC_RADIANS_TO_DEGREES(b->GetAngle());                  }                    }      } }

- (void)update:(ccTime)dt {    [self updateLevelHelper:dt];    [self updateBox2D:dt];    [self updateSprites:dt];}

-(void) draw {  

    glClearColor(98.0/255.0, 183.0/255.0, 214.0/255.0, 255.0/255.0);    glClear(GL_COLOR_BUFFER_BIT);    

    glDisable(GL_TEXTURE_2D);    glDisableClientState(GL_COLOR_ARRAY);    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    _world->DrawDebugData();

    glEnable(GL_TEXTURE_2D);    glEnableClientState(GL_COLOR_ARRAY);    glEnableClientState(GL_TEXTURE_COORD_ARRAY);    }

- (void)dealloc {    [_lhelper release];    _lhelper = nil;    delete _world;    [super dealloc];}

@end

WOW! 好长的一段代码 啊! 如果你熟悉COCOS2D和Box2d这里的许多代码应该已经见到了,在这里我只是点出 使用到Levelhelper的一些特别之处。

  • setupLevelHelper包含了初始化的代码。创建了一个新的 LevelhelperLoader实例,
  • 传递进你想使用的 Level文件(不用添加.plhs扩展名),然后调用一个方法来 将你事先使用 SpriteHleper /Level Helper创建的 各种 shape 添加进Box2d的world,然后创建 World的边界,将重力设置成在 LevelHelper中定义的大小。
  • 注意到使用Box2d时,通常在某个头文件中定义PTM_RATIO,使用LevelHelper你可以从LevelHelperLoader 类中获得这个值,而不用调用 pixelsToMeterRatio Method。默认值是32.0,当然如果你需要别的值,你也可以手动设置它。
  • 在开始更新Box2d world之前,要给LevelHelperLoader时间来运行调用update方法,LevelHelperLoader用此来使物体沿着路径运动,或者控制平行滚动。虽然在这个游戏中没有什么区别,但是这样做是一个好的习惯。
  • 我们应该更新与每一个 Box2d中 与 Body相关联的sprite,使得他们的位置重合,LevelHelperLoader在 body的userData中自动存储了与之相关的sprite的引用。
  • 编译运行工程,monster掉到了clouds上!

Creating a Simple Level with LevelHelper

现在我们知道这是怎么工作的了,让我们回去添加更多的关卡。

我们已经讲了怎么使用LevelHelper在LEVEL 中添加物体,那么回去从列表中拖拽一些sprites 创建一个像这样的level(当然,你也可以做的有些不同!)。

重要提示:你也要从列表中拖拽一个lase tbeam到屏幕之外,在这个教程里你可能不需要,但是之后的教程将会用到!

完成之后,编译运行,得到如下:


这还不算令人兴奋,因为没有任何东西在动!所以让我们加一些角色的移动和滚动逻辑。

开始之前,我们要在LevelHelper中做一些改动,如你所知,我们呢需要一些方法来引用我们在LevelHelper中创建的物体,有两种方法可以做到:

1、通过为object 设置tag(一种像CSS中的class的东西)

2、通过为每一个object设一个独一无二的名字。

下面开始设置tag,在levelhelper的屏幕底部,在sprite properties部分的下面,点击“+”按钮,创建一个新的tag。

输入PLAYER作为名字,点击ADD,同理对于 MONSTER,LASER,GROUND.

然后点击每一个你添加到level中的sprite,设置他们的tag,注意这里可以多个同时选定(通过功能键+点击或者拖动选定)。

然后在 “sprites in level”下面的表中 找到你为hero 添加的sprite,双击“Unique Name”下方的入口,把它变成 “HERO”.

保存文件。另外,无论何时你修改了tag值,你都要重新生成代码,因为tag值将生成在头文件中,所以当你完成,到 File\Generate Code\Cocos2D with Box2D. 选择你的Raycast目录,重新生成代码。

你可以在LevelHelperLoader.h中修改这些值:

enum LevelHelper_TAG {     DEFAULT_TAG     = 0,    PLAYER          = 1,    MONSTER         = 2,    LASER           = 3,    GROUND          = 4,    NUMBER_OF_TAGS  = 5};

知道Cocos2d sprites怎样获得一个tag 值吗?LevelHelper根据你在Levelhelper中设定的值,设定这些tag值。

OK!现在终于可以添加 角色的移动和滚动逻辑的代码了!在ActionLayer.h中作如下的改动:

// Add inside @interfacedouble _playerVelX;CCSprite * _hero;b2Body * _heroBody;BOOL _gameOver;

在ActionLayer.mm中作如下的改动:

// Add to top of file#define MOVE_POINTS_PER_SECOND 80.0

// Add to bottom of setupLevelHelper_hero = [_lhelper spriteWithUniqueName:@"hero"];NSAssert(_hero!=nil, @"Couldn't find hero");_heroBody = [_lhelper bodyWithUniqueName:@"hero"];NSAssert(_heroBody!=nil, @"Couldn't find hero body");

// Add at bottom of initself.isTouchEnabled = YES;

// Add after init-(void)setViewpointCenter:(CGPoint) position {

    CGSize winSize = [[CCDirector sharedDirector] winSize];    CGRect worldRect = [_lhelper gameWorldSize];

int x = MAX(position.x, worldRect.origin.x + winSize.width / 2);int y = MAX(position.y, worldRect.origin.y + winSize.height / 2);    x = MIN(x, (worldRect.origin.x + worldRect.size.width) - winSize.width / 2);    y = MIN(y, (worldRect.origin.y + worldRect.size.height) - winSize.height/2);    CGPoint actualPosition = ccp(x, y);

    CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);    CGPoint viewPoint = ccpSub(centerOfView, actualPosition);

    self.position = viewPoint;

}

- (void)loseGame {    [_hero runAction:[CCSequence actions:                      [CCScaleBy actionWithDuration:0.35 scale:2.0],                      [CCDelayTime actionWithDuration:0.75],                      [CCScaleTo actionWithDuration:0.35 scale:0],                      nil]];    [_hero runAction:[CCRepeatForever actionWithAction:                      [CCRotateBy actionWithDuration:0.5 angle:360]]];     _gameOver = YES;}

- (void)winGame {    _gameOver = YES;    [[SimpleAudioEngine sharedEngine] playEffect:@"win.wav"];}

- (void)updateHero:(ccTime)dt {if (_playerVelX != 0) {        b2Vec2 b2Vel = _heroBody->GetLinearVelocity();        b2Vel.x = _playerVelX / [_lhelper pixelsToMeterRatio];        _heroBody->SetLinearVelocity(b2Vel);                        }}

- (void)updateViewpoint:(ccTime)dt {    [self setViewpointCenter:_hero.position];}

- (void)updateGameOver:(ccTime)dt {

if (_gameOver) return;

    CGRect worldRect = [_lhelper gameWorldSize];if (_hero.position.x > (worldRect.origin.x + worldRect.size.width) * 0.95) {        [self winGame];    }

if (_hero.position.y < 0.5) {        [self loseGame];    }

}

// Add in update, at beginning[self updateHero:dt];

// Add in update, at end[self updateViewpoint:dt];[self updateGameOver:dt];

// Add after draw- (void)handleTouchAtPoint:(CGPoint)touchLocation {        if (touchLocation.x < _hero.position.x) {        _playerVelX = -MOVE_POINTS_PER_SECOND;        _hero.flipX = YES;    } else {        _playerVelX = MOVE_POINTS_PER_SECOND;        _hero.flipX = NO;    }    }

- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    

if (_gameOver) return;

    UITouch *touch = [touches anyObject];    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];

    [self handleTouchAtPoint:touchLocation];    

if (touch.tapCount > 1) {        _heroBody->ApplyLinearImpulse(b2Vec2(_playerVelX/[_lhelper pixelsToMeterRatio], 1.25), _heroBody->GetWorldCenter());        [[SimpleAudioEngine sharedEngine] playEffect:@"wing.wav"];    }

}

- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

if (_gameOver) return;

    UITouch *touch = [touches anyObject];    CGPoint touchLocation = [self convertTouchToNodeSpace:touch];    [self handleTouchAtPoint:touchLocation];}

- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {    _playerVelX = 0;}

- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {    _playerVelX = 0;}

  • 这里又有许多代码,当然也有许多是重复的:这里有些提示:
  • 在 setupLevelHelper的底部,我们使用了  spriteWithUniqueName 和bodyWithUniqueName 来唯一定位"hero"的sprite和body。
  • 在LevelHelper的 set up in the Sprite in Level 部分。
  • setViewpointCenter是一个通过滚动layer以保持hero在中心的函数,更多的细节看
  • 《How To Drag and Drop Sprites with Cocos2D or our Learning Cocos2D》 一书.
  • 我们通过调用 SetLinerVelocity来根据玩家触摸了player的左边还是右边来手动设置速度。我们使用convertTouchToNodeSpace来获得layer内的触摸点。
  • 如果玩家tap了两下屏幕,让player接受一个小的力,这个过程通过ApplyLinearImpulse完成,产生一个微弱的飞翔的效果。
  • 如果player到了屏幕的最右端,,我们说player赢了,如果player掉进坑里,失败。
  • 在胜利或者失败时会播放一小段动画和音乐。
  • 编译运行,看你是否能赢了这个游戏!
  • 如果你不能,你可以使用levelhelper编辑关卡,让游戏容易一些!

LevelHelper and SpriteHelper: My Review

到这里你应该已经知道使用LevelHelper 和 SpriteHelper的基本方法,这篇教程也要结束了。

但是我还要分享一下我对这两个工具的见解,如果你还在犹豫是否要使用这两个工具,

读下面的一些关于使用这两个软件的优点:

1、使得创建和修改关卡变得简单,尤其是和手动的靠着猜测调整sprite的位置相比。

2、这里仍然还有一些我没有说明的好的特点,像创建动画,路径,平行滚动,joints,甚至可以节省更多的时间!

3、API 简明易懂。

这里也有一些缺点:

1、中途向工程中添加新的art有困难,正如先前说的,我这样做毁了我创建好的level,我不得不重做了一个。

2、由于缺乏设计的各种UI缺陷。

3.不能够定制CCSprite,然后指定给LevelHelper中定义的对象

更新: 实际上是可行的,这里有一篇教程教大家怎么做。

总而言之,优点多于缺点,特别是当你拿他们和手动放置sprite或者费事创建你自己的编辑器时。

所以,如果你在寻找做好的Cocos2d的编辑器,并且你可以忍受以上的局限,我想这两个工具是个不错的选择!节省了你宝贵的开发时间

Where To Go From Here?

  这里有本教程的完整源代码。

期待下一篇教程吧,到时候,我们会扩展这个游戏,同时为monster添加更多的功能,使得游戏更有乐趣---小小的透露一下:使用了中级的box2d物理小技巧。

如果你有任何问题,或者对于翻译教程有任何疑问,你可以在本博客留言,也可以登录泰然论坛和大家一起交流,同时还可以加入我们的qq群。

论坛交流地址,请点击传送门!



转载于:https://www.cnblogs.com/zilongshanren/archive/2012/02/16/2347966.html

(译)如何使用spritehelper和levelhelper教程:引子相关推荐

  1. 【译】ASP.NET MVC 5 教程 - 4:添加模型

    原文:[译]ASP.NET MVC 5 教程 - 4:添加模型 在本节中,我们将添加一些管理电影数据库的类,这些类在ASP.NET MVC 应用程序中扮演"Model"的角色. 我 ...

  2. 计算机专业英语被动语态举例,第七讲被动语态的译法_计算机专业英语教程

    第七讲被动语态的译法_计算机专业英语教程 第七讲 被动语态的译法 (Translation of Passive Voice) 被动语态在英语中,特别是科技英语中运用得非常广泛,而汉语的被动句则用得较 ...

  3. (译)使用cocos2d、LevelHelper和SpriteHelper实现疯狂考拉(Part 2)

    PS:关于cocos2d国外有很多不错的教程网站,http://highoncoding.com/也是其中一个,看了他们8月17发的疯狂考拉教程,其实主要是用到了LevelHelper和SpriteH ...

  4. (译)使用cocos2d、LevelHelper和SpriteHelper实现疯狂考拉(Part 1)

    PS:关于cocos2d国外有很多不错的教程网站,http://highoncoding.com/也是其中一个,看了他们8月17发的疯狂考拉教程,其实主要是用到了LevelHelper和SpriteH ...

  5. 微软python视频教程中文-【译】微软的Python入门教程(一)

    Getting started with Python(Python入门) Overview 概述 The series of videos on Channel 9 is designed to h ...

  6. [译] 用于 iOS 的 ML Kit 教程:识别图像中的文字

    原文地址:ML Kit Tutorial for iOS: Recognizing Text in Images 原文作者:By David East 译文出自:掘金翻译计划 本文永久链接:githu ...

  7. Python机器学习基础教程-第2章-监督学习之K近邻

    前言 本系列教程基本就是摘抄<Python机器学习基础教程>中的例子内容. 为了便于跟踪和学习,本系列教程在Github上提供了jupyter notebook 版本: Github仓库: ...

  8. cocos2d-x常用工具-沈大海cocos2d-x教程23

    这些工具平常也用到,不过没有像这样整理出来,这是我在网上看到的.就记录一下. 位图字体工具Bitmap Font Tools BMFont (Windows) Fonteditor Glyph Des ...

  9. Delphi Web应用开发B/S框架推荐:《Delphi Web前端开发教程——基于TMS WEB Core框架》

    使用TMS WEB Core 发现Delphi软件开发的无限潜能-- 二十多年来,编程语言Delphi 以对 Windows 应用程序的快速可视化编程而闻名于世.尤其是轻松开发桌面数据库应用程序和快捷 ...

  10. 图像处理基础教程和工具软件简介

    本文主要介绍了机器视觉图像处理的基础教程和机器视觉开发软件以及图像处理方法工具包,适合于图像处理初学者参考,以尽快利用如下资源入门,并进行简单的图像处理算法的应用和开发,本文介绍的教程.软件安装包和相 ...

最新文章

  1. 数据蒋堂 | 还原分组运算的本意
  2. 用JavaScript实现简单的excel列转sql字符串
  3. mysql内存体系结构_Innodb存储引擎的体系架构之内存
  4. linux-Centos7安装python3并与python2共存
  5. 微信公众号怎么快速导出一个月的文章数据
  6. 全国人口净流入城市排名2020_从人口净流入量来看一线城市吸引力的对比分析...
  7. [转】Python--遍历列表时删除元素的正确做法
  8. python actor_Python定义一个Actor任务
  9. socket()编程简介
  10. 电脑版微信如何实现消息批量发送
  11. Oracle基本认识
  12. python小游戏——塔防小游戏代码开源
  13. 这5个PHP编程中的不良习惯,一定要改掉!
  14. JavaScript代码优化 --- 长期更新
  15. threejs-经纬度转换成xyz坐标的方法
  16. 艾宾浩斯记忆遗忘曲线
  17. Web前端:HTML~CSS~JS
  18. 如何在 React Component 之外获取 Redux Store
  19. 树莓派3B网线连接笔记本电脑以及安装ubuntu16
  20. 2017便利蜂前端一面

热门文章

  1. c语言两位数码管动态显示,十天学会单片机和c语言编程数码管动态显示.pptx
  2. 视频教程-汇编语言程序设计III-其他
  3. 天宝DINI03/莱卡DNA03等电子水准仪原始数据处理软件使用教程
  4. 机器学习笔记(四)BP神经网络模型
  5. java的编译路径在哪_如何知道我的java编译器的路径
  6. matlab液压仿真模型,基于MATLABsimulink的液压系统动态仿真.ppt
  7. 思科模拟器Cisco Packet Tracer的汉化教程
  8. JBoss下载and安装
  9. 【Opengl】Glut下载与环境配置
  10. JavaScript的特效