`

(译)碰撞检测和收集物品:如何使用cocos2d制作基于tiled地图的游戏:第二部分

    博客分类:
  • ios
 
阅读更多

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

原文链接地址:http://www.raywenderlich.com/1186/collisions-and-collectables-how-to-make-a-tile-based-game-with-cocos2d-part-2

程序截图:

  这篇教程是《如何使用cocos2d制作基于tiled地图的游戏》的第二部分。在上一个教程中,我们创建了一个简单的基于tiled地图的游戏,里面有一个忍者在沙漠里寻找可口的西瓜!

  在第一部分教程中,我们介绍了如何基于tiled创建地图,怎样把地图增加到游戏中去,以及如何滚动地图来跟随主角移动、还有如何使用对象层。

  在这部分教程中,我们将会介绍如何在地图中制作可以碰撞的区域,如何使用tile属性,如果收集游戏物品并且动态地修改地图、如何确保你的忍者不会吃得太饱!

  因此,让我们继续我们上篇教程所学并且让它更像一个真实的游戏吧!

tiled地图和碰撞检测

  你可能已经注意到了,目前我们的忍者可以毫无阻拦地穿过墙壁和障碍物。他是一个忍者,但是即使是真正的忍者,他也没这么厉害啊!

  因此,我们需要找到一种方法,通过把一些tile标记成“可碰撞的”,这样的话,我们就可以防止玩家穿过那些点的位置。有很多方法可以做得到(包括使用对象层),但是,我想向你们展示一种新的技术。我认为它更高效,并且也是一次好的学习锻炼--使用一个元层(meta layer)和层属性。

  让我们开始动手吧!再一次启动Tiled软件,点击“Layer\Add tile Lyaer...”,并且命名为“Meta”,然后选择OK。我们将在这个层里面加入一些假的tile代表一些“特殊tile”。

  因此,现在我们需要增加我们的特殊tile。点击“Map\New tileset...”,在你的Resources文件夹下面找到mate_tiles.png,然后选择打开。设置Margin和Spacing都为1并点击OK。

  这时,你可以在Tilesets区域看到一个新的标签。打开它,而且你会看到2个tile:一个红色的和一个绿色的。

  这些tile并没有什么特殊的东西--我只是制作了一个简单的图片,里面包含了一个红色的和一个绿色的半透明tile。接下来,我们把红色的tile当作是“可碰撞的”(后面我们会用到绿色的),然后,合适地绘制我们的场景。

  因此,确保Meta层被选中,选择stamp工具,选择红色的tile,然后把任何你不想让忍者通过的地图都涂一遍。当你做完的时候,应该看起来像下面的图示一样:

  接下来,我们可以设置tile的属性,这样的话,我们在代码中就可以识别这个tile是“可以碰撞的(穿不过去的)”。在Tileset里面的红色tile上在,右击,选择“Properties...“。增加一个新的属性,叫做”Collidable“,并且设置成”Ture“:

  (由于版本的关系,我这里补充我上传的Tiled编辑器(java版本)如何设置属性!!!)

       首先,选择TileSets-->TileSetManager,并选中meta_tiles,出现如下所示图:

    然后点击右下角的“Edit”按钮(就是垃圾回收站下面那个图标),接下来会出现下图所示:(接着就选中红色tile和绿色tile,然后添加Collidable属性并设置为True就ok啦)

 


  保存map,并返回Xcode。在HelloWorldScene.h中做如下改动:

// Inside the HelloWorld class declaration
CCTMXLayer *_meta;

// After the class declaration
@property (nonatomic, retain) CCTMXLayer *meta;

 同时修改HelloWorldScene.m文件如下:

// Right after the implementation section
@synthesize meta = _meta;

// In dealloc
self.meta = nil;

// In init, right after loading background
self.meta = [_tileMap layerNamed:@"Meta"];
_meta.visible = NO;

// Add new method
- (CGPoint)tileCoordForPosition:(CGPoint)position {
int x = position.x / _tileMap.tileSize.width;
int y = ((_tileMap.mapSize.height * _tileMap.tileSize.height) - position.y) / _tileMap.tileSize.height;
return ccp(x, y);
}
 好了,让我们先停一会儿。像之前一样,我会meta层声明了一个成员变量,而且从tile map中加载了一个引用。注意,我们把这个字当作是不可见的,因为我们并不想看见这些对象,它们的存在只是为了说明,那个区域是可以碰撞的。

  接下来,我们增加一个新的帮助方法,这个方法可以帮助我们把x,y坐标转换成”tile坐标“。每一个tile都有一个坐标,从左上角的(0,0)开始,到右下角的(49,49)。(本例中,地图的大小是49×49)

  

  上面的截屏是java版本的tiled界面。能否显示tile的坐标,我不确定这个功能在QT版本的tiled中是否存在。不管怎么说,我们将要使用的一些功能会使用tile坐标,而不是x,y坐标。因此,我们需要一种方式,将x,y坐标转换成tile坐标。这正是那个函数所需要做的。

   获得x坐标非常容易--我们只需要让它除以一个tile的宽度就可以了。为了得到y坐标,我们不得不翻转一些东西,因为,在cocos2d里面(0,0)是在左下角的,而不是在左上角。

  接下来,把setPlayerPosition替换成以下内容:

CGPoint tileCoord = [self tileCoordForPosition:position];
int tileGid = [_meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [_tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collision = [properties valueForKey:@"Collidable"];
if (collision && [collision compare:@"True"] == NSOrderedSame) {
return;
}
}
}
_player.position = position;
 在这里,我们把玩家的x,y坐标转换成tile坐标。然后,我们使用meta层中的tileGIDAt方法来获取指定位置点的GID号。

  对了,什么是GID呢?GID代表”全球唯一标志符“(我个人意见)。但是,在这个例子中,我认为它只是我们使用的tile的一种标识,它可以是我们想要移动的红色区域。

  当我们使用GID来查找指定tile的属性的时候。它返回一个属性字典,因此,我们可以遍历一下,看是否有”可碰撞的“物体被设置成”true“,或者是gij仅仅就是那样。编译并运行工程,因此还没有设置玩家的位置。

  就这么多!编译并运行程序,它将会向你展示,现在你不能够通过那些红色的tile组成的地方了吧:

动态修改Tiled Map

  目前为此,我们的忍者已经有一个比较有意思的冒险啦,但是,这个世界有一点点无趣。而且简单无任务事可做!加上,我们的忍者看起来比较贪吃,而且背景将会随着玩家移动而移动。因此,让我们创建一些东西让忍者来玩吧!

  为了使之可行,我将不得不创建一个前景层,这样做可以让用户收集东西。那样的话,我们仅仅从前景层中删除不用的tile(当tile被玩角拾取的时候),这个过程中,背景将会随之移动。

  因此,打开Tiled,选择”Layer\Add Tile Layer...“,把这个层命名为”Foreground“,然后选择OK。确保前景层被选择,而且增加一对可以拾取的物品在游戏中。我喜欢放置一些向西瓜或者别的什么东西。

  

  现在,我们需要把这些tile标记成可以拾取的,类似的,参照我们是如何把tile标志成可以碰撞的。选择Meta层,转换到Meta_tiles。现在,我们需要使这些tile可以拾取,点击”Layer\Move Layer Up“来确保你的meta层是在最顶层,并且保持绿色可见的。

  接下来,我们需要为tile增加属性,这样把它标记成可拾取的。点键点击Tilesets选项卡里的绿色的tile,然后点“Properties...”,再增加一个新的属性,命名为“Collectable”,值设置为“True”。

  保存地图,然后返回到Xcode。在HelloWorldScene.h中做如下修改:

// Inside the HelloWorld class declaration
CCTMXLayer *_foreground;

// After the class declaration
@property (nonatomic, retain) CCTMXLayer *foreground;

 同时,相应地修改HelloWorldScene.m:

// Right after the implementation section
@synthesize foreground = _foreground;

// In dealloc
self.foreground = nil;

// In init, right after loading background
self.foreground = [_tileMap layerNamed:@"Foreground"];

// Add to setPlayerPosition, right after the if clause with the return in it
NSString *collectable = [properties valueForKey:@"Collectable"];
if (collectable && [collectable compare:@"True"] == NSOrderedSame) {
[_meta removeTileAt:tileCoord];
[_foreground removeTileAt:tileCoord];
}
 这里是一个常用的方法,用来保存前景层的句柄。不同之处在于,我们测试玩家正朝之移动的tile是否含有“Collectable”属性。如果有,我们就使用removeTileAt方法来把tile从mata层和前景层中移除掉。编译并运行工程,现在你的忍者可以尝尝西瓜的滋味啦!

  

创建一个计分器

  我们忍者非常高兴地吃西瓜啦,但是,作为一个游戏玩家,我们想知道自己到底吃了多少个西瓜。你懂的,我们并不想让他吃得太胖。

  通常的做法是,我们在层上面添加一个label。但是,等一下:我们在不停地移动这个层,那样的话,label就会看不到了,怎么办?

  这是一个非常好的机会,如果在一个场景中使用多个层--这正是我们现在面临的难题。我们将保留HelloWorld层,但是,我们会再增加一个HelloWorldHud层来显示我们的label。(Hud意味着Heads up display,大家可以google一下,游戏中常用的技术)

  当然,这两个层之间需要一种方式联系起来--Hud层应该知道什么时候忍者吃了一个西瓜。有许许多多的方式可以使2个不同的层相互通信,但是,我只介绍最简单的。我们在HelloWorld层里面保存一个HelloWorldHud层的句柄,这样的话,当忍者吃了一个西瓜就可以调用Hud层的一个方法来进行通知。

  因此,在HelloWorldScene.h里面增加下面的代码:

// Before HelloWorld class declaration
@interface HelloWorldHud : CCLayer
{ 
CCLabel *label;
}

- (void)numCollectedChanged:(int)numCollected;
@end

// Inside HelloWorld class declaration
int _numCollected;
HelloWorldHud *_hud;

// After the class declaration
@property (nonatomic, assign) int numCollected;
@property (nonatomic, retain) HelloWorldHud *hud;

 同样的,修改HelloWorldScene.m文件:

// At top of file
@implementation HelloWorldHud

-(id) init
{
if ((self = [super init])) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
label = [CCLabel labelWithString:@"0" dimensions:CGSizeMake(50, 20)
alignment:UITextAlignmentRight fontName:@"Verdana-Bold" 
fontSize:18.0];
label.color = ccc3(0,0,0);
int margin =10;
label.position = ccp(winSize.width - (label.contentSize.width/2) 
- margin, label.contentSize.height/2+ margin);
[self addChild:label];
}
return self;
}

- (void)numCollectedChanged:(int)numCollected {
[label setString:[NSString stringWithFormat:@"%d", numCollected]];
}

@end

// Right after the HelloWorld implementation section
@synthesize numCollected = _numCollected;
@synthesize hud = _hud;

// In dealloc
self.hud = nil;

// Add to the +(id) scene method, right before the return
HelloWorldHud *hud = [HelloWorldHud node]; 
[scene addChild: hud];

layer.hud = hud;

// Add inside setPlayerPosition, in the case where a tile is collectable
self.numCollected++;
[_hud numCollectedChanged:_numCollected];
 一切很明了。我们的第二个层从CCLayer派生,只是在它的右下角加了一个label。我们修改scene把第二个层也添加进去,然后传递一个Hud类的引用给HelloWorld层。然后修改HelloWorldLayer层,当计数器改变的时候,就调用Hud类的方法,这样就可以相应地更新Hud类了。

  编译并运行,如果一切ok,你将会在屏幕右下角看到统计忍者吃西瓜的Label。

来点音效和音乐

  如果没有很cool的音效和背景音乐的话,这就不能算作是一个完整的游戏教程了。

  增加音效和音乐非常简单,只需在HelloWolrdScene.m作如下修改:

// At top of file
#import "SimpleAudioEngine.h"

// At top of init for HelloWorld layer
[[SimpleAudioEngine sharedEngine] preloadEffect:@"pickup.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"hit.caf"];
[[SimpleAudioEngine sharedEngine] preloadEffect:@"move.caf"];
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"TileMap.caf"];

// In case for collidable tile
[[SimpleAudioEngine sharedEngine] playEffect:@"hit.caf"];

// In case of collectable tile
[[SimpleAudioEngine sharedEngine] playEffect:@"pickup.caf"];

// Right before setting player position
[[SimpleAudioEngine sharedEngine] playEffect:@"move.caf"];
 现在,我们的忍者可以开怀大吃了!

何去何从?

  这个系列的教程,就此完结了。距离上次翻译时间长了点。通过这个教程的学习,你对cocos2d里面的tiled map的使用,应该有一个非常好的理解了。这里有这个教程的完整源代码。

  接下来,我会接着翻译下一篇,是原作者的一个朋友写的,这个系列教程的终结版:《加入敌人和战斗:如果使用cocos2d制作基于tiled的地图:第三部分》。

  如果你看了这个教程,有什么好的意见或建议,可以自由发言,谢谢!

 

著作权声明:本文由http://www.cnblogs.com/andyque翻译,欢迎转载分享。请尊重作者劳动,转载时保留该声明和作者博客链接,谢谢!

分享到:
评论

相关推荐

    Cocos2D-iPhone游戏开发教程打包整理-(泰然论坛整理)

    碰撞检测和收集物品:如何使用cocos2d制作基于tiled地图的游戏:第二部分.pdf (译)cocos2d精灵教程:第一部分.pdf (译)cocos2d精灵教程:第三部分.pdf (译)cocos2d精灵教程:第二部分.pdf (译)cocos2d...

    Cocos2D游戏开发教程打包整理-(泰然论坛整理)

    碰撞检测和收集物品:如何使用cocos2d制作基于tiled地图的游戏:第二部分.pdf (译)cocos2d精灵教程:第一部分.pdf (译)cocos2d精灵教程:第三部分.pdf (译)cocos2d精灵教程:第二部分.pdf (译)cocos2d菜单...

    子龙山人翻译的所有ios文章

    碰撞检测和收集物品:如何使用cocos2d制作基于tiled地图的游戏:第二部分.pdf (译)cocos2d精灵教程:第一部分.pdf (译)cocos2d精灵教程:第三部分.pdf (译)cocos2d精灵教程:第二部分.pdf (译)cocos2d菜单...

    cocos2d-iphone游戏开发pdf

    第二章《开始学习》深入探讨了如何使用cocos2d创建游戏界面,包括添加图像、文本和按钮等元素。此外,还会介绍如何处理用户输入,如触摸事件,这是游戏交互性的重要组成部分。 第四章《你的第一个游戏(修正错误)...

    How to make a cocos2d-x simple game(第二部分)

    在“如何制作cocos2d-x简单游戏”的第二部分,我们将继续上一部分的工作,构建游戏的基本结构,并实现一些基本的游戏功能。 首先,让我们来看看"Classes"目录。这个目录通常包含了项目中的所有源代码文件,这些文件...

    Tiled瓦片地图编辑器1.3.5-1.10.2

    Tiled的核心功能在于创建基于瓦片的2D地图,这种地图由一系列小图片(即“瓦片”)拼接而成,广泛应用于像素艺术风格的游戏。它支持多种地图层类型,包括图块层、对象层和图像层,满足不同游戏场景的需求。此外,...

    Cocos2d开发资源大集合

    - **Cocos2d-Android**:提供两个基于Java语言的Android版本,分别是Cocos2d-Android和Cocos2d-Android-1,满足不同开发者的需求。 - **Cocos2d-JavaScript**:使用JavaScript语言编写,适合Web开发领域。 - **Cocos...

    Cocos2D权威指南的VerticalShootingGame小游戏用Cocos2D-X实现的Android项目代码

    《Cocos2D权威指南》是一本深入介绍Cocos2D系列引擎的书籍,而"VerticalShootingGame"是该书第二章中的一个经典示例,旨在帮助读者理解和掌握Cocos2D-X在游戏开发中的实际应用。这个小游戏是垂直射击类游戏,玩家...

    Cocos2d-X权威指南完全扫描版.part2

    《Cocos2d-X权威指南完全扫描版》是关于Cocos2d-X游戏开发的一本重要参考资料,该书深入浅出地介绍了Cocos2d-X框架的各个方面,旨在帮助开发者们掌握这一强大的游戏引擎。本部分是该书的第二部分,由于文件大小的...

    ios5 cocos2d游戏开发实战(第二版)源码 第(2-5)章

    《iOS5 cocos2d游戏开发实战(第二版)》是一本深入探讨使用cocos2d框架在iOS平台上构建游戏的权威指南。这本书的第二版针对iOS5进行了全面更新,提供了丰富的实战案例,帮助开发者掌握cocos2d的核心技术和游戏开发...

    cocos2d-x实战C++卷

    - 通常使用Tiled Map Editor这样的第三方工具来创建地图。 11. **物理引擎(Physics Engine)**: - 物理引擎用于模拟游戏中的物理现象,如碰撞检测、重力效果等。 - cocos2d-x内置了Box2D物理引擎,可以轻松地...

    IOS5 COCOS2D游戏开发实战源代码 第2章~第5章

    COCOS2D是一个广泛用于iOS平台上的2D游戏开发框架,它基于Objective-C编写,提供了丰富的图形、动画以及物理引擎功能,使得开发者能够轻松构建具有高质量视觉效果和流畅性能的游戏应用。 第二章通常会涵盖COCOS2D的...

    Packt.Python.Game.Programming.By.Example.2015

    - **地图编辑**:使用Tiled Map Editor创建游戏地图。 - **游戏场景**:定义了游戏场景类。 - **游戏过场动画**:设计了游戏结束时的过场动画。 - **塔防角色**:定义了炮台、敌人等游戏中的角色。 ##### 第4章:...

Global site tag (gtag.js) - Google Analytics