(4) cocos2d-x Tile Map教程(二)

原文来自:http://www.raywenderlich.com/40544/cocos2d-x-tile-map-tutorial-part-2

欢迎来到Cocos2d-x tile map教程系列的第二部分!在这里你将学会怎样用cocos2d-x制作一个基于tile的游戏,cocos2d-x是cocos2d-iphone的C++跨平台移植版。

第一部分的教程里,你学会了怎样创建一个地图用Tiled工具,怎样添加地图到游戏中,怎样让地图跟随玩家滚动还有怎样使用对象层。

这一部分,包含了怎样在地图中检测碰撞区域,怎样使用块属性,怎样收集物品、动态改变地图,还有怎样确保你的忍者不能吃太多等等。

Tiled Maps and Collisions

你也许注意到了当前的忍者可以轻松的穿过城墙和障碍物,它是一个忍者,但即使是忍者这样也不见得好。

所以,你需要找出一种方法标记一些图块作为“可碰撞‘,这样就可以阻止玩家移动到这些位置。这里有很多可行的办法来解决(包括使用对象层),但是,我给你的是我认为很有效的一种新技术,也是一个很好的练习——使用Meta图层和图层属性

让我们开始吧!再次打开Tiled工具,点击图层\添加图层,然后命名图层为Meta,你将要放一些假的图块来表示它是”特殊的图块“。

所以现在你需要添加一些特殊图块,点击地图\新图块...,浏览到MyTileMap\Resources\TileGameResources文件夹中的Meta_tiles.png,然后点击打开,设置边距和间距都为1,再点OK

确保图层窗口选中,点击Meta_tiles,你将看到两个图块:红色和绿色的。


它们没什么特殊的——我只是做了两个部分透明的红色和绿色的图块。此后,红色代表"可碰撞",它会用来适当的绘制场景。

接着确保Meta图层选中,选择图章刷工具,点击红色图块,绘制你想要这个忍者碰撞的任何物体。完成后,可能看起来像下面这样会:

接下来,你需要在这个图块上设置一个属性标记它,以至于你可以在代码中识别它是"可碰撞的"。在图块集区右键点击红色图块,然后点击图块属性...添加一个新的属性Collidable,设置为True,像下面这样:

点击OK,保存地图返回到Xcode添加一个新的private属性HelloWorldScene.h

CCTMXLayer*_Meta;

这是你Meta图层的引用,添加一个新的public方法声明:

CCPointtileCoordForPosition(CCPointposition);

接着,打开HelloWorldScene.cpp添加下面行到init方法中,在加载背景的后面: _Meta=_tileMap->layerNamed("Meta");

_Meta->setVisible(false);

这里得到了Meta图层的引用,并把它设为不可见,你不希望问玩家可以看到这些红色图块吧! 下面,添加这个新方法

CCPointHelloWorld::tileCoordForPosition(cocos2d::CCPointposition)

{

intx = position.x/_tileMap->getTileSize().width;

inty = ((_tileMap->getMapSize().height*_tileMap->getTileSize().height) - position.y)/_tileMap->getTileSize().height;

returnccp(x,y);

}

在这里停一下,像平常一样你为Meta图层声明了一个成员变量,然后从地图中加载一个引用。注意,你标记了这个图层为不可见的,因为你不想看它们,它们只是表示可碰撞。 然后添加了一个新的辅助方法来帮助你把x,y坐标转换为"tile坐标"。每一个图块都有自己的坐标,左上角(0,0),右下角(49,49).(在这个程序中是50x50)

顺便说下,上面的截图是以前java版本的Tiled,它们已经移植到新版的(Qt)中,有一个显示图块坐标的功能

不管怎样,你要用到的功能是需要tile坐标,而不是坐标转化为tile坐标,这正是tileCoordForPosition功能

获取x坐标很容易——只需要用它除以图块的宽。为了得到y坐标,你必须反过来,因为Cocos2d-x的(0,0)是左下角,而不是左上角。

用下面的代码替换掉setPlayerPosition中的内容

voidHelloWorld::setPlayerPosition(cocos2d::CCPointposition)

CCPointtileCoord =this->tileCoordForPosition(position);

inttileGid =_Meta->tileGIDAt(tileCoord);//得到全局唯一标识

if(tileGid) {

CCDictionary*properties =_tileMap->propertiesForGID(tileGid);

if(properties) {

CCString*collision =newCCString();

*collision = *properties->valueForKey("Collidable");

if(collision && (collision->compare("True")==0)) {

return;

}

}

}

_player->setPosition(position);

在这里,你将玩家tile坐标,然后你在Meta图层用tileGID方法,通过指定的tile坐标来获取GID 噢,什么是GID?GID代表”globally unique identifier“(全局唯一标识)。但是在这种情况下我认为它就是被使用的图块的id,也就是你将要移动到得红色的图块。

然后,使用GID来查找图块的属性,它返回一个字典,所以,你可以通过看Collidable是否为true,来立即返回,这样就不设置玩家的位置,使这次移动无效。;

编译-》运行,你应该会看到玩家将不能够穿过你涂过红色的城墙了。

Modifying the Tiled Map Dynamically

到目前为止,你的忍者能够很好的探索,但是这个世界有点枯燥,原因很简单,没什么事可做。

加上你的忍者看起来有点饿,所以让我们来弄点好吃的东西给你的忍者。

为了做到这点,我们要为你想要玩家收集的东西创建一个前景图层,这样当忍者捡起东西的时候,你可以轻松的在前景图层上删除这个图块,然后背景图层就会显示出来。

打开Tiled工具,选择图层\添加图层,并命名为Foreground。为了不影响我们后面的游戏,把以前添加的西瓜图块都要删除掉,先将背景层前置到最上层(右键->前置图层)

使用橡皮工具将以前绘制的西瓜图块都擦除掉。将Foreground层设为最上层,然后确保点中Foreground层,添加一些可收集的物品到地图中,我们这里就使用西瓜图块。

现在,你需要标记这些图块为可收集的,就像标记可碰撞的那些图块一样。先将Meta图层设为最上层,选中Meta图层,在点击Meta_tiles视图然后给这些

西瓜图块涂上绿色的图块。

下一步,你需要添加图块属性标记其可收集,在图块集窗口右键点击绿色的图块,点击图块属性...然后添加一个新属性名为Collectable,值为True,再点击 保存地图(cmd+s),回到xcode,添加一个新的HelloWorldScene.h:

CCTMXLayer*_foreground;

然后打开HelloWorldScene.cpp,并添加下面行到init方法中(在加载完背景以后): _foreground=_tileMap->layerNamed("Foreground");

这里得到一个前景图层的引用,呆会会用到。 接着,添加下面代码setPlayerPosition,在造成它return之后。

CCString*collectible =newCCString();

*collectible = *properties->valueForKey("Collectable");

if(collectible && (collectible->compare("True")==0)) {

_Meta->removeTileAt(tileCoord);

_foreground->removeTileAt(tileCoord);

}

这里是标准的,对于跟踪Foreground图层的引用。新的东西是你需要检查玩家是否移动到有可收集的属性 如果是,你用removeTileAt方法删除Meta图层和Foreground图层。

编译-》运行工程,现在你的忍者就可以吃到美味的西瓜了。

creating a score Counter

现在你的忍者又饱又开心,但是作为玩家你想要知道到底吃了多少西瓜,你知道的,不希望它吃得太肥。

通常你只需要添加一个标签到你的图层上就可以解决。但是等一下,你一直在移动整个图层,这会搞砸的,oh noes!

这是一个在场景中展示怎样使用多图层的好机会。之前你一直都是在HelloWorld层做,但是你将要使用另外一个叫HudLayer图层来显示你的标签。(Hud全称是heads up display).

当然,你的这些图层需要一些方法来交互——HudLayer图层需要知道忍者什么时候吃了西瓜。有很多方法都可以让两个图层间交互,但是你也许将用最简单的方法——你将拿到HelloWorld图层的

指针到HudLayer图层,然后当忍者吃东西的时候就调用一个方法通知它。

所以,在xcode中点击File\New\File...在IOS\C and C++选择C++ Class模板,点击Next,命名为HudLayer然后点击Create

HudLayer.h,用下面内容代替:

#ifndef __MyTileMap__HudLayer__

#define __MyTileMap__HudLayer__


#include"cocos2d.h"

USING_NS_CC;

classHudLayer:publicCCLayer

{

private:

CCLabelTTF*_label;

public:

virtualboolinit();

CREATE_FUNC(HudLayer);

voidnumCollectedChanged(intnumCollected);

};


#endif/* defined(__MyTileMap__HudLayer__) */

这是一个继承至CCLayer的类,代表一个图层。它创建了一个private的成员变量来跟踪标签显示,有一个辅助的方法来更新数值的显示 用下面代码代替HudLayer.cpp:

#include"HudLayer.h"

boolHudLayer::init()

{

if(!CCLayer::init()) {

returnfalse;

}

CCSizewinSize =CCDirector::sharedDirector()->getWinSize();

_label=newCCLabelTTF();

_label->initWithString("0","Arial",35.0);

_label->setColor(ccc3(255,0,0));

intmargin =20;

_label->setPosition(ccp(winSize.width- (_label->getContentSize().width/2) - margin,_label->getContentSize().height/2+ margin));

this->addChild(_label);

returntrue;

}

voidHudLayer::numCollectedChanged(intnumCollected)

CCString*labelCollected =newCCString();

labelCollected->initWithFormat("%d",numCollected);

_label->setString(labelCollected->getCString());

}

init方法中,你创建一个标签,并作为子图层添加到这个图层中,在numCollectedChanged,你更新的标签的文本。

现在,让我们来使用这个图层,在HelloWorldScene.h添加文件

#include"HudLayer.h"

然后声明两个private属性,一个是新建的图层,一个是收集的西瓜数目。

HudLayer*_hud;

int_numCollected;

HelloWorldScene.cpp中,添加下面代码CCScene*scene()方法中,在return之前。 HudLayer*hud =newHudLayer();

hud->init();

scene->addChild(hud);

layer->_hud= hud;

这创建了你的图层并添加到这个场景中。还把主图层中得成员变量hud设置到新创建的图层,以至于你有办法与它交互。 最后,添加下面代码到setPlayerPosition,在收集图块的时候。

_numCollected++;

_hud->numCollectedChanged(_numCollected);

为了能够调用HudLayer中的方法你已经修改HelloWorldScene当计数改变的时候,因此你可以相应的更新标签 编译-》运行工程,如果没错的话在右下角你就能看到西瓜的计数了。


Gratuituous Sound Effects and Music

简单的改变一下HelloWorldScene.cpp

//在文件头部:

#include"SimpleAudioEngine.h"


//在init方法顶部

CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("pickup.caf");

CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("hit.caf");

CocosDenshion::SimpleAudioEngine::sharedEngine()->preloadEffect("move.caf");

CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic("TileMap.caf");

//在setPlayerPosition里面,发生碰撞的时候 CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("hit.caf");


setPlayerPosition里面,收集到西瓜的时候

CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("pickup.caf");

setPlayerPosition里面,在设置player位置之前:
CocosDenshion::SimpleAudioEngine::sharedEngine()->playEffect("move.caf");

通过这个系列教程,至少你已经掌握了在cocos2d-x中使用tile map的相关重要概念。

这里是完整的游戏代码

相关文章

操作步骤 1、创建cocos2d-x工程 2、新建 Scene1.cpp Scene1.h Scene1.h代码 #ifndef __SCENE1_H__#defi...
开发环境:OS(WINDOWS 8.1 X64 企业版) cocos2d-x 2.2.1 vs2010 想给vs安装上cocos的模版,执行Install...
把创建项目做成一个批处理,当创建项目时可以省时省力很多。 操作步骤 1、在 E:cocos2d-x-2.2.1toolspr...
https://www.cnblogs.com/JiaoQing/p/3906780.html 四个响应函数 1 EventListenerPhysicsContact* evC...
转载于 http://www.cnblogs.com/kenkofox/p/3926797.html 熟悉js的dom事件或者flash事件的,基本都能立...
ScrollView(滚动容器)加载大量item时会导致游戏界面的卡顿,严重时整个界面会出现卡死的情况。最近项...