@H_404_5@ 免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作!
@H_404_5@原文链接地址:http://www.raywenderlich.com/1163/how-to-make-a-tile-based-game-with-cocos2d
@H_404_5@教程截图:
@H_404_5@
@H_404_5@ 在这个2部分的教程中,我将会教大家如何使用cocos2d来做一个基于tile地图的游戏,当然还有Tiled地图编辑器。(我们小时候玩的小霸王小学机里面的游戏,大部分都是基于tile地图的游戏,如坦克大战、冒险岛、吞食天地等)我们将会创建一个忍者在沙漠中找西瓜吃的小游戏。-_-
@H_404_5@ 在第一部分教程中,我将教大家如何使用Tile来创建地图,怎样把地图加到游戏中,怎么让地图跟随玩家滚动,以及怎样使用对象层。
@H_404_5@ 在第二部分教程中,我将介绍如何在地图中创建可碰撞的区域,如何使用tile属性,如何制作可拾取的物体和动态修改地图,还有确保忍者不要吃撑了!
@H_404_5@ 如果你还没有准备好的话,你可能需要先从《如何使用cocos2d来制作简单的iphone游戏》系列教程开始学起,因为我们这个教程使用了大量的基本概念,而这些概念都可以从上面的教程中获取。
@H_404_5@ 好了,让我们玩一玩tile地图吧!
@H_
404_5@
@H_404_5@然后在HelloWorldScene.m文件中做如下修改:
@H_404_5@ 这里,我们调用CCTMXTiledMap类的一些方法,把我们刚刚创建的地图文件加载进去。
@H_404_5@ 一些简明的CCTMXTiledMap的背景知识。它是一个CCNode,你可以设置它的位置和比例等。这个地图的孩子是一些层,而且提供了一个帮助函数可以让你通过层的名字得到层对象--我们上面就是通过这种方面获得地图背景的。每一个层都是一个CCSpriteSheet的子类,这里考虑了性能的原因--但是这也意味着每一个层只能有一个tile集。
@H_404_5@ 因此,我们这里做的所有这些,就是指向一个tile地图,然后保存背景层的引用,并且把tile地图加到HelloWorld层中。
@H_404_5@ 好了,就这么多!编译并运行工程,你将会看到地图的左下角出现在模拟器中。
@H_404_5@
@H_404_5@ 还不错!但是,这还不是一个游戏!我们还需要三个东西:a)游戏主角,b)主角初使位置和c)能够移动视图,这样就好像是第一视角了。
@H_404_5@ 好了,接下来让我们来解决这些问题。
创建工程骨架
@H_404_5@ 让我们首先创建整个工程的骨架,这样可以确保今后我们需要的文件都包含进来了,并且能够跑起来。 @H_404_5@ 因此,启动XCode,点击“File\New Project...”,选择cocos2d Application template,并且把工程命名为TileGame。 @H_404_5@ 接下来,下载游戏资源文件。这个资源文件包里包含了以下内容:- 玩家sprite。这个图片和《如何使用cocos2d来制作简单的iphone游戏》差不多。
- 我使用cxfr这个工具制作的一些音效。
- 我使用Garage Band制作的一些背景音乐。(查看这篇博文获得更多的信息)
- 我们将会使用的tile集合--它实际上会和tile地图编辑器一块儿使用,但是,我想把它放在这里,余下的事情会变得更容易。
- 一些额外的“特殊”的tile,我将会在后面加以说明。
使用Tile来制作地图
@H_404_5@ cocos2d支持使用开源的Tile地图编辑器创建的TMX格式的地图。 @H_404_5@ (作者给出的网址现在打不开了,这是我在另一个地方找到的。我把它放到我的网盘里了,并且做了一个链接。如果有人下载不了,请留言。这个tile地图编辑器是java版的,其实还有一个at版的,但是java版的功能强大一些。但是,大家请注意,作者使用的是qt版本的,所以界面会有一些不一致,但这并不影响程序的使用。) @H_404_5@ 下载完之后,直接双击运行。点击File\New,然后会出现以下对话框: @H_404_5@ @H_404_5@ 在 orientation部分,你可以选择Orthogonal(参考: Legend of Zelda)或者Isometric(参考: Disgaea)。我们这里将选择Orthogonal。 @H_404_5@ 接下来,设置地图的大小。记住,这个大小是以tile为单位的,而不是以像素为单位。我们将创建一个尽量小的地图,因此选择50×50. @H_404_5@ 最后,你指定每个tile的宽度和高度。你这里选择的宽度和高度要根据你的实际的tile图片的尺寸来做。这个教程使用的样例tile的尺寸是32×32,所以在上面的选项中选择32×32. @H_404_5@ 接下来,我们把制作地图所需要的tile集合导入进来。点击菜单栏上面的“TileSets”菜单,“New Tileset...”,然后会出现下面的窗口: @H_404_5@ @H_404_5@ 为了获得图片,点击Browse按钮,然后定位到你的TestGame文件夹,选择 tmw_desert_spacing.png文件,然后加到工程中去。它会基于文件名自动填充Name。然后把TileSet name命名为“tmw_desert_spacing.png”.同时,设置下面的Tile spacing和Margin都为1. @H_404_5@ 你可以保留宽度和高度为32×32,因为tile的实际大小也是这么多。至于margin和spacing,我还没找到任何好的文档解释如何设置这两个值,下面是我的个人看法:- Margin就是当前的tile计算自身的像素的时候,它需要减去多少个像素(宽度和高度都包含在内)。(类比word、css的margin)
- Spacing就是相邻两个tile之间的间隔(同时考虑宽度和高度)(类比word、css的spacing)
- 你可以在Tileset拾取器中拖出一个方框,一次选取多个tile。
- 你可以使用工具栏上的paint按钮来基于一个基准tile绘制整个地图。
- 你可以使用“View\Zoom In...”和“View\Zoom out...”来放大和缩小地图。
把tile地图添加到cocos2d的场景中
@H_404_5@ 首先,第一件事情,右键点击Resources,选择“ Add\Existing Files…”,然后添加TileMap.tmx文件。 @H_404_5@ 打开HelloWorldScene.h,然后添加一些成员变量,并且申明一声属性:
//
Inside the HelloWorld class declaration
CCTMXTiledMap * _tileMap;
CCTMXLayer * _background;
After the class declaration
@property (nonatomic,retain) CCTMXTiledMap * tileMap;
@property (nonatomic,retain) CCTMXLayer * background;
CCTMXTiledMap * _tileMap;
CCTMXLayer * _background;
After the class declaration
@property (nonatomic,retain) CCTMXTiledMap * tileMap;
@property (nonatomic,retain) CCTMXLayer * background;
Right after the implementation section
@synthesize tileMap = _tileMap;
@synthesize background = _background;
In dealloc
self.tileMap = nil;
self.background = nil;
Replace the init method with the following
- (id) init
{
if ( (self = [super init] )) {
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile: @" TileMap.tmx " ];
self.background = [_tileMap layerNamed: Background " ];
[self addChild:_tileMap z: - 1 ];
}
return self;
}
@synthesize tileMap = _tileMap;
@synthesize background = _background;
In dealloc
self.tileMap = nil;
self.background = nil;
Replace the init method with the following
- (id) init
{
if ( (self = [super init] )) {
self.tileMap = [CCTMXTiledMap tiledMapWithTMXFile: @" TileMap.tmx " ];
self.background = [_tileMap layerNamed: Background " ];
[self addChild:_tileMap z: - 1 ];
}
return self;
}
tiled对象层和设置tile地图位置
@H_404_5@ tiled支持两类层--tile层(就是我们目前使用的层),还有对象层。 @H_404_5@ 对象层允许你在地图上圈出一些区域,来指定一些事件的发生。比如,你可能想制作一个区域,在那里怪物将会跳出来,或者是一个区域,只要进入就会死掉。这我们这个例子中,我们将创建一个区域来显示我们的游戏主角。 @H_404_5@ 因此,找到Tiled的菜单,点击” Layer\Add Object Group…”,命名为“Objects”,然后选择Ok。如果你绘制了地图,你将会注意到,它并没有绘制一个tile,而是画了一个很难看的灰色矩形,这个矩形我们之后可以扩展,使之能够包含多个tiles或者移动它。 @H_404_5@ 我们只想要选择一个tile来让主角显示。因此,在你的地图上选择一个tile。这个区域的大小实际上并没有关系,因为我们仅仅使用x、y坐标。 @H_404_5@ @H_404_5@ 然后,上面的黄色对象上面点右键, 取名为“SpawnPoint",然后选择Ok: @H_404_5@(添加对象方法很简单,看到左边的+号和-号了吗?一个是增加对象,一个是删除对象。点中加号以后,用鼠标可以拖出一个矩阵区域。) @H_404_5@ @H_404_5@(下面给出一些技巧。如何把一个对象准确放置到Background的空白区域,只需要调整背景的opacity就可以了) @H_404_5@ @H_404_5@ 假设,你可以做一些奇思妙想。你把对象的类型命名为cocos2d里面的class的名字,然后它会为你创建那个类型(比如CCSprite),但是,我在cocos2d自带的源代码里面代不到这样的例子。更新:来自GeekAndDad.com的Tyler提供了之前版本的cocos2d里面使用这种“妙想”的方法,但是,由于背景有白色,所以被移除了。 @H_404_5@ 不管怎么说--我们仅仅把这个类型设置为空就行了,最后cocos2d会为我们创建NSMutableDictionary,我们可以从中获得对象的各种属性,包含x,y坐标。 @H_404_5@ 保存地图,然后返回XCode。在HelloWorldScene.h中做如下修改:
//
Inside the HelloWorld class declaration
CCSprite * _player;
After the class declaration
@property (nonatomic,retain) CCSprite * player;
@H_404_5@
@H_404_5@同样,修改HelloWorldScene.m,代码如下:
CCSprite * _player;
After the class declaration
@property (nonatomic,retain) CCSprite * player;
@synthesize player = _player;
self.player = nil;
Inside the init method,after setting self.background
CCTMXObjectGroup * objects = [_tileMap objectGroupNamed: Objects " ];
NSAssert(objects != nil, 'Objects' object group not found " );
NSMutableDictionary * spawnPoint = [objects objectNamed: SpawnPoint " ];
NSAssert(spawnPoint != nil,0); line-height:1.5!important">SpawnPoint object not found " );
int x = [[spawnPoint valueForKey: x " ] intValue];
int y = [[spawnPoint valueForKey: y " ] intValue];
self.player = [CCSprite spriteWithFile: Player.png " ];
_player.position = ccp(x,y);
[self addChild:_player];
[self setViewpointCenter:_player.position];
-
(
void
)setViewpointCenter:(CGPoint) position {
CGSize winSize = [[CCDirector sharedDirector] winSize];
int x = MAX(position.x,winSize.width / 2 );
int y = MAX(position.y,winSize.height / 2 );
x = MIN(x,(_tileMap.mapSize.width * _tileMap.tileSize.width)
- winSize.width / 2 );
y = MIN(y,(_tileMap.mapSize.height * _tileMap.tileSize.height)
- winSize.height / 2 );
CGPoint actualPosition = ccp(x,y);
CGPoint centerOfView = ccp(winSize.width / 2 ,128); line-height:1.5!important">2 );
CGPoint viewPoint = ccpSub(centerOfView,actualPosition);
self.position = viewPoint;
}
CGSize winSize = [[CCDirector sharedDirector] winSize];
int x = MAX(position.x,winSize.width / 2 );
int y = MAX(position.y,winSize.height / 2 );
x = MIN(x,(_tileMap.mapSize.width * _tileMap.tileSize.width)
- winSize.width / 2 );
y = MIN(y,(_tileMap.mapSize.height * _tileMap.tileSize.height)
- winSize.height / 2 );
CGPoint actualPosition = ccp(x,y);
CGPoint centerOfView = ccp(winSize.width / 2 ,128); line-height:1.5!important">2 );
CGPoint viewPoint = ccpSub(centerOfView,actualPosition);
self.position = viewPoint;
}