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

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

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

程序截图: node

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

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

  在这部分教程中,咱们将会介绍如何在地图中制做能够碰撞的区域,如何使用tile属性,若是收集游戏物品而且动态地修改地图、如何确保你的忍者不会吃得太饱! 函数

  所以,让咱们继续咱们上篇教程所学而且让它更像一个真实的游戏吧! 工具

tiled地图和碰撞检测

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

  所以,咱们须要找到一种方法,经过把一些tile标记成“可碰撞的”,这样的话,咱们就能够防止玩家穿过那些点的位置。有不少方法能够作获得(包括使用对象层),可是,我想向大家展现一种新的技术。我认为它更高效,而且也是一次好的学习锻炼--使用一个元层(meta layer)和层属性。 测试

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

  所以,如今咱们须要增长咱们的特殊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的地图:第三部分》。

  若是你看了这个教程,有什么好的意见或建议,能够自由发言,谢谢!

相关文章
相关标签/搜索