转的 在这个分为上下两部分的教程中,我们将介绍如何使用Cocos2D-X和地图编辑器做一款基于地图块的游戏。在这个简单的地图块游戏里,一个精灵将在沙漠里搜寻它可口的西瓜!
- 在教程的第一部分,我们将介绍如何创建一个地图,如何将地图添加到游戏中,如何滚动地图以跟着玩家跑,以及如何使用对象层。
- 在本教程的第二部分,我们将介绍如何在地图上制作碰撞区,如何使用地图块的属性,如何制作宝藏并动态的修改地图,以及如何确保你的忍者不暴饮暴食。
如果你还没有学习“如何用cocos2d-x来开发简单的Uphone游戏:(一) 下载安装和HelloWorld ”,你不妨先学习下这个教程,因为这个教程里有很多基础知识我们下面要用到。
好吧,让我们开始探险之旅吧!
创建一个工程框架
我们将首先创建一个框架工程,以确保我们拥有下面工程中所需的所有文件。
先下载并运行helloworld(具体参见教程“如何用cocos2d-x来开发简单的Uphone游戏:(一) 下载安装和HelloWorld ”)。
接下来,下载游戏所需要的zip资源文件(TileGameResource.zip)。该zip文件包含以下内容
• 一个我们将作为游戏主角的精灵。这个和教程“如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵”很像!
• 声音特效,用cxfr工具制作(by Wenderlich)。
• 一些背景音乐,用Garage Band制作(by Wenderlich,更多信息见post)。
• 地图块 - 这实际上是要用地图编辑器做的,但现在就把它和其他东西一起包含进来会更容易一些。
• 一些其他“特殊”的地图块,我们将在下面介绍。
好了,这些资源将在后面适当的时候添加到我们的游戏中,现在就是我们制作地图和享受乐趣的时刻了!
Cocos2DX支持由开源项目地图块图编辑器(Tiled Map Editor,国外网站,需要FQ)制作并保存为TMX格式的地图。
如果你访问上面的链接,你会看到有两个版本的编辑器 – 一种是用Qt应用程序框架编写的,另一种是用Java编写的。有两个版本的原因是因为这个编辑器最先是用Java编写的,后来他们又将它移植到Qt上来。
你要用哪个版本主要是取决于你。在本教程中,我们将介绍使用Qt版本,因为从现在开始这是编辑器的发展主线,但有些人又喜欢用Java版本,因为并不是所有的旧功能都已经被移植过来了。
不管怎样 - 如果你想跟着学习,那就下载Qt版本,然后安装运行。转到文件\新建,在对话框中填写如下:
在新建地图对话框的Orientation选项,你可以选择Orthogonal(正交视图,采用的游戏Legend of Zelda)或者Isometric(等轴视图,采用的游戏Disgaea),这里,我们选择Orthogonal。
下面你得到设定地图大小。记住这是以地图块为单位,不是像素点。我们要做一张比较小的地图,因此就选择50×50。
最后设定地图块的宽度和高度。在这里你选择多大的尺寸是依赖于你的美工制作的地图块的大小的。在本教程中,我们用的是编辑器中附带的示例地图块集,每个地图块都是32×32大小的,因此选择32×32。
接下来,我们就在编辑器中加入地图块集。点击菜单栏的“Map”选项,选择“New Tileset…”,然后填写对话框:
图片可以点击Browse按钮得到,点击后导航到你的地图编辑器目录下的examples文件夹,将tmw_desert_spacing.png加到工程中.
宽度和高度32×32不用改,那个就是地图块的宽高。对于margin and spacing(边际和间距),还没有确切解释它们意思的文献,但它们应该这么理解:
• 边缘,指的是一个地图块的外面一圈,在这个边缘里面是实际的地图块内容。
• 间隔,即两个地图块之间的间距(像素单位)。
如果你看一看tmw_desert_spacing.png,你会发现每个地图块有一个1像素的黑边界,这就解释了边际和间距为什么设置作为1。
一旦你点击OK,你就将在Tilesets窗口看见地图块集。Ok,现在你也能开始画地图了! 点击在工具栏的“Stamp”图标,点击一个地图块,然后在地图上任意一个你想要的地方点击填充地图块。
继续完成你的地图吧——但一定要发挥你的想象力噢! 但请在地图上至少画一两个建筑物,因为待会我们会让我们的精灵走进你造的建筑。
这里有几个制图的小窍门,可以记住:
• 你可以在Tileset中拖动鼠标,同时选择几个地图块,然后填充到地图上。
• 你可以使用工具栏上的油漆桶按钮来填充一整片具有同样背景的地图,当然,用来填充的地图块是你在Tileset选择的。
• 你可以通过“查看\放大...”和“查看\缩小...”放大和缩小地图。
一旦你绘制完地图,在图层窗口中单击当前图层(现在这个图层就是“tile layer 1”),将名称改为“background”。然后点击“File\Save”,将文件保存到你的TileMap项目资源文件夹,并命名该文件“tilemap.tmx”。
我们后面还将做更多的东西,但现在让我们把这张地图加载到游戏中!
把地图加载到Cocos2DX场景中
首先,因为我们是利用uphone的sdk开发游戏,所以我们先将资源也就是tilemap.tmx和tmw_desert_spacing.png拷贝到sdk所在目录的子目录“/UserData/”中。
然后,将HelloWorld.cpp的initCocos2d()函数用下面的代码替换:
{
// init director
CCDirector::getSharedDirector()->setOpenGLView(m_pMainWnd);
// set to landscape mode
CCDirector::getSharedDirector()->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);
// load the tile map
CCTMXTiledMap *pDesertTileMap = CCTMXTiledMap::tiledMapWithTMXFile("tilemap.tmx");
pDesertTileMap->setPosition(CGPoint(0,0));
// create layer instance
CCLayer *pLayer = MyLayer::node();
pLayer->addChild(pDesertTileMap, 0, 1);
// add layer to scene
CCScene *pScene = CCScene::node();
pScene->addChild(pLayer);
// add scene to director
CCDirector::getSharedDirector()->runWithScene(pScene);
return true;
}
在这里,我们用到了CCTMXTiledMap类,通过它加载了我们制作的地图。
现在大概的介绍下CCTMXTiledMap。这是一个CCNode,所以你可以设置它的位置,尺寸等,该节点的子节点是地图上的层,而且也有 一个接口可以通过名字来获取这些层,比如这里的“background”层。每一个层都是CCSpriteSheet的子类,这也意味着每一层你只能有一 个tileset。
所以我们在这里做的是保存tilemap和background的关系,并且把tilemap加到HelloWorld的layer上去。
这样就很ok了!编译和运行代码,你应该看到的是你地图的左下角:
还不赖! 但我们做的是游戏,所以我们还需要做三件事: a)一个精灵, b)精灵的出生点 和c)移动我们的视图,以便我们可以看到精灵。
这是我们会感到棘手的地方,下面我们就来解决掉它!
对象层和地图位置的设置
地图模块支持两种层,一种是地图层(就是我们上面一直在接触的),另一种是对象层。
对象层允许你在地图指定一个位置,在这个位置可以响应一些事件。比如,你希望有一个地方让精灵出生或者挂掉。在我们的教程中,我们要创建一个精灵的出生点。
那么,选择菜单栏中的“Layer\Add Object Layer…”,将该层命名为“objects”,然后单击确定。现在如果你在地图上画,你会发现它不是直接画地图块,而是画一个形状怪异的灰色框框,你可以放缩和移动这个灰色框框。
我们只是想选一个位置来放置我们的精灵,所以在地图上随便选一个位置单击。框框的大小并不重要,因为我们只需要用到x,y坐标。
然后右键单击灰色框框和点击“Properties”,命名为“spawnpoint”,点击OK :
假如你在这里设置了对象的类(Cocos2DX的类,比如CCSprite),那么代码会自动创建一个对应类型的对象。(不过GeekAndDad.com的泰勒指出,这是Cocos2DX以前的版本才具有的功能,现在已经被删除了。)
不管怎样,我们将type置空,那么它将为我们创建一个NSMutableDictionary,我们可以通过它访问该对象的各个属性,包括X,Y坐标。
保存地图并返回到代码,在HelloWorld.cpp的initCocos2d()接口中,在代码:
CCTMXTiledMap *pDesertTileMap = CCTMXTiledMap::tiledMapWithTMXFile("tilemap.tmx");
pDesertTileMap->setPosition(CGPoint(0,0));
的下面,加入以下代码
CCTMXObjectGroup *objGroup = pDesertTileMap->objectGroupNamed("objects");
CCXStringToStringDictionary *spawnPoint = objGroup->objectNamed("spawnpoint");
float fSpawnPointX = spawnPoint->objectForKey("x")->toFloat();
float fSpawnPointY = spawnPoint->objectForKey("y")->toFloat();
// load player image texture
CCTexture2D *pPlayerTexture = CCTextureCache::sharedTextureCache()->addImage("Player.png");
// create sprite instance
CCSprite *pPlayer = CCSprite::spriteWithTexture(pPlayerTexture);
pPlayer->setPosition(CGPoint(fSpawnPointX, fSpawnPointY));
// add the player to the tile map
pDesertTileMap->addChild(pPlayer);
Ok,我们再解释一下对象层和对象组。在这里,我们通过CCTMXTiledMap对象的objectGroupNamed接口得到了对象层,它返回的是一个CCTMXObjectGroup对象。
然后,我们通过CCTMXObjectGroup的objectNamed方法来得到一个NSMutableDictionary,它包含了对 象层的对象的一堆有用的信息,包括X和Y坐标,宽度和高度。不过这里我们关心的是X,Y坐标,所以我们就只取得X,Y坐标,并把它作为精灵的位置。
编译运行代码我们就可以看到精灵了。
下面我们再让它动一动。在代码
CCDirector::getSharedDirector()->runWithScene(pScene);
的下面,添加代码:
CCIntervalAction *move = CCMoveBy::actionWithDuration(5, ccp(140, 120));
CCIntervalAction *back = move->reverse();
CCFiniteTimeAction *seq = CCSequence::actions(move, back,NULL);
pPlayer->runAction( CCRepeatForever::actionWithAction((CCIntervalAction*) seq) );
这里的几个action类是对动作的封装。上面代码的含义是表示精灵将向右上方移动,相对位置是(140, 120),并在五秒钟内完成移动,然后反方向移回到原来位置。
Ok,先运行你的代码,然后欣赏你的精灵开心的跑来跑去吧!