忍者无敌-实例讲解Cocos2d-x瓦片地图

忍者无敌-实例讲解Cocos2d-x瓦片地图,第1张

概述实例比较简单,如图所示,地图上有一个忍者精灵,玩家点击他周围的上、下、左、右,他能够向这个方向行走。当他遇到障碍物后是无法穿越的,障碍物是除了草地以为部分,包括了:树、山、河流等。 忍者实例地图(TODO用这个精灵替换图中的) 设计地图 我们采用David Gervais提供开源免费瓦片集,下载的文件dg_grounds32.gif,gif文件格式会有一定的问题,我们需要转换为.jpg或.png文 实例比较简单,如图所示,地图上有一个忍者精灵,玩家点击他周围的上、下、左、右,他能够向这个方向行走。当他遇到障碍物后是无法穿越的,障碍物是除了草地以为部分,包括了:树、山、河流等。




忍者实例地图(Todo用这个精灵替换图中的



设计地图
我们采用DavID Gervais提供开源免费瓦片集,下载的文件dg_grounds32.gif,gif文件格式会有一定的问题,我们需要转换为.jpg或.png文件。本实例中我是使用PhotoShop转换为dg_grounds32.jpg。

DavID Gervais提供的瓦片集中的瓦片是32 x 32像素,我们创建的地图大小是32 x 32瓦片。我们先为地图添加普通层和对象层,普通层按照上图设计,对象层中添加几个矩形区域对象,这里不再赘述。这个阶段设计完成的结果如图所示。保存文件名为MIDdleMap.tmx,保存目录Resources\map。



设计地图
程序中加载地图
地图设计完成我们就可以在程序中加载地图了。下面我们再看看具体的程序代码,首先看一下HelloWorldScene.h文件,它的代码如下:
[HTML] view plain copy #ifndef__HELLOWORLD_SCENE_H__ #define__HELLOWORLD_SCENE_H__ #include"cocos2d.h" classHelloWorld:publiccocos2d::Layer { cocos2d::TMXTiledMaP*_tileMap;① cocos2d::Sprite*_player;② public: staticcocos2d::Scene*createScene(); virtualboolinit(); CREATE_FUNC(HelloWorld); }; #endif//__HELLOWORLD_SCENE_H__
上述代码第①行代码是定义成员变量地图成员_tileMap,。第②行代码是定义精灵成员变量_player。
HelloWorldScene的实现代码HelloWorldScene.ccp文件,它的HelloWorld::init()代码如下:
copy boolHelloWorld::init() { if(!Layer::init()) returnfalse; } SizevisibleSize=Director::getInstance()->getVisibleSize(); Pointorigin=Director::getInstance()->getVisibleOrigin(); _tileMap=TMXTiledMap::create("map/MIDdleMap.tmx");① addChild(tileMap,100);② TMXObjectGrouP*group=_tileMap->getobjectGroup("objects");③ ValueMapspawnPoint=group->getobject("ninja");④ floatx=spawnPoint["x"].asfloat();⑤ floaty=spawnPoint["y"].asfloat();⑥ _player=Sprite::create("ninja.png");⑦ _player->setposition(Point(x,y));⑧ addChild(_player,2,200); returntrue; }
上述第①代码是创建TMXTiledMap对象,地图文件是MIDdleMap.tmx,map是资源目录Resources下的子目录。TMXTiledMap对象也是Node对象,需要通过第②行代码添加到当前场景中。
第③行代码是通过对象层名objects获得层中对象组集合。第④行代码是从对象组中,通过对象名获得ninja对象信息,它的返回值类型是ValueMap,ValueMap是一种“键-值”对结构。第⑤行代码float x = spawnPoint["x"].asfloat()中的spawnPoint["x"]就是从按照x键取出它的值,即x轴坐标。spawnPoint["x"]的返回值是Value类型,还需要使用asfloat()函数转换为基本的int类型。类似地,第⑥行代码是获得y轴坐标。
第⑦行代码是创建精灵_player,第⑧行代码是设置精灵位置,这个位置是从对象层中ninja对象信息获取的。

加载地图(Todo重新截取,或者换精灵)
移动精灵
移动精灵是通过触摸事件实现移动的,需要在层中进行事件处理,我们需要在层中重写如下函数:
bool ontouchBegan(touch * touch,Event* unused_event)
voID ontouchended(touch * touch,51); Font-family:Arial; Font-size:14px; line-height:26px">voID ontouchmoved(touch * touch,51); Font-family:Arial; Font-size:14px; line-height:26px">下面我们再看看具体的程序代码,首先看一下HelloWorldScene.h文件,它的代码如下:
copy cocos2d::TMXTiledMaP*_tileMap; cocos2d::Sprite*_player; virtualboolontouchBegan(cocos2d::touch*touch,cocos2d::Event*event);① virtualvoIDontouchmoved(cocos2d::touch*touch,cocos2d::Event*event); virtualvoIDontouchended(cocos2d::touch*touch,cocos2d::Event*event);② CREATE_FUNC(HelloWorld); }; #endif//__HELLOWORLD_SCENE_H__
上述代码第①~②行代码是声明触摸事件函数。HelloWorldScene的实现代码HelloWorldScene.ccp文件,它的HelloWorld::init()代码如下:
copy …… settouchEnabled(true); //设置为单点触摸 settouchMode(touch::dispatchMode::ONE_BY_ONE); returntrue; }
上述代码settouchEnabled(true)是使层开始触摸事件支持。代码settouchMode(touch::dispatchMode::ONE_BY_ONE)是设置触摸模式为单点触摸。
HelloWorldScene.ccp文件的触摸事件函数代码如下:
copy boolHelloWorld::ontouchBegan(touch*touch,Event*event) log("ontouchBegan"); } voIDHelloWorld::ontouchmoved(touch*touch,Event*event) log("ontouchmoved"); voIDHelloWorld::ontouchended(touch*touch,248)"> log("ontouchended"); PointtouchLocation=touch->getLocation();① PointplayerPos=_player->getposition();② Pointdiff=touchLocation-playerPos;③ if(abs(diff.x)>abs(diff.y)){④ if(diff.x>0){⑤ playerPos.x+=_tileMap->getTileSize().wIDth; _player->runAction(FlipX::create(false));⑥ }else{ playerPos.x-=_tileMap->getTileSize().wIDth; >runAction(FlipX::create(true));⑦ if(diff.y>0){⑧ playerPos.y+=_tileMap->getTileSize().height; }else{ playerPos.y-=_tileMap->setposition(playerPos);⑨ 上述第①代码touch->getLocation()是获得在Open GL坐标,Open GL坐标的坐标原点是左下角,touch对象封装了触摸点对象。第②行代码_player->getposition()是获得精灵的位置。
第③行代码是获得触摸点与精灵位置之差。第④行代码是比较一下触摸点与精灵位置之差,是y轴之差大还是x轴之差大,那个轴之差大就沿着那个轴移动,(abs(diff.x) > abs(diff.y))情况是x轴之差大,否则是y轴之差大。第⑤行代码,diff.x > 0情况是沿着x轴正方向移动,否则情况是沿着x轴负方向移动。第⑥行代码_player->runAction(FlipX::create(false))是把精灵翻转回原始状态。第⑦行代码_player->runAction(FlipX::create(true))是把精灵是沿着y轴水平翻转。
第⑧行代码是沿着y轴移动,diff.y > 0是沿着y轴正方向移动,否则是沿着y轴负方向移动。
第⑨行代码是重新设置精灵坐标。
检测碰撞
到目前为止我们游戏中的精灵,可以穿越任何障碍物。为了能够检测到精灵是否碰撞到障碍物,我们需要再添加一个普通层(collIDable),它的目的不是现实地图,而是检测碰撞。我们在检测碰撞层中使用瓦片覆盖background层中的障碍物之上,如图所示。
检测碰撞层 检测碰撞层中的瓦片集可以是任何的满足格式要求的图片文件。在本例中我们使用一个32 x 32像素单色jpg图片文件collIDable_tiles. jpg,它的大小与瓦片大小一样,也就是说这个瓦片集中只有一个瓦片。导入这个瓦片集到地图后,我们需要为瓦片添加一个自定义属性,瓦片本身也有一些属性,例如:坐标属性x和y。
我们要添加的属性名为“CollIDable”,属性值为“true”。添加过程如图所示,首先,选择collIDable_tiles瓦片集中的要设置属性的瓦片。然后,点击属性视图中左下角“+”按钮,添加自定义属性,这时候会d出一个对话框,我们在对话框中输入自定义属性名“CollIDable”,点击确定按钮。这时候回到属性视图,CollIDable在属性后面是可以输入内容的,这里我们输入“true”。
添加检测碰撞属性 地图修改完成后,我们还要修改代码。首选在头文件HelloWorldScene.h中添加一个成员变量和两个函数的声明。
copy #include"SimpleAudioEngine.h" classHelloWorld:publiccocos2d::Layer cocos2d::TMXTiledMaP*_tileMap; cocos2d::TMXLayer*_collIDable;① voIDsetPlayerposition(cocos2d::Pointposition);② cocos2d::PointtileCoordFromposition(cocos2d::Pointposition);③ 上述代码第①行是声明一个TMXLayer类型的成员变量,它是用来保存地图碰撞层对象。第②行代码setPlayerposition函数是重新设置精灵的位置,在这个函数中可以检测精灵是否与障碍物碰撞。第③行代码tileCoordFromposition函数是把像素坐标点转换为地图瓦片坐标点。
修改HelloWorldScene.cpp中的HelloWorld::init()代码如下:
copy _tileMap=TMXTiledMap::create("map/MIDdleMap.tmx"); addChild(_tileMap,100); >getobjectGroup("objects"); >getobject("ninja"); floatx=spawnPoint["x"].asfloat(); floaty=spawnPoint["y"].asfloat(); _player=Sprite::create("ninja.png"); _collIDable=_tileMap->getLayer("collIDable");① _collIDable->setVisible(false);② 我们需要在HelloWorld::init()函数中创建并初始化碰撞层。第①行代码是_collIDable = _tileMap->getLayer("collIDable")是通过层名字collIDable创建层,第②行代码_collIDable->setVisible(false)是设置层隐藏,我们要么在这里隐藏的,要么在地图编辑的时候,将该层透明,如图所示,在层视图中选择层,然后通过滑动上面的透明度滑块来改变层的透明度,在本例中是需要将透明度设置为0,那么_collIDable->setVisible(false)语句就不再需要了。
注意 在地图编辑器中,设置层的透明度为0与设置层隐藏,在地图上看起来一样,但是有着本质的区别,设置层隐藏是无法通过_collIDable = _tileMap->getLayer("collIDable")语句访问的。
设置层透明度
我们在前面也介绍过,collIDable层不是用来显示地图内容的,而是用来检测碰撞的。修改HelloWorldScene.cpp中的 copy HelloWorld::ontouchended代码如下: //获得在OpenGL坐标 >getLocation(); >getposition(); Pointdiff=touchLocation-playerPos; if(abs(diff.x)>abs(diff.y)){ if(diff.x>0){ playerPos.x+=_tileMap->runAction(FlipX::create(false)); playerPos.x-=_tileMap->runAction(FlipX::create(true)); if(diff.y playerPos.y+=_tileMap->getTileSize().height; playerPos.y-=_tileMap- this->setPlayerposition(playerPos);① HelloWorld::ontouchended有一些变化,第①行代码this->setPlayerposition(playerPos)替换了_player->setposition(playerPos),setPlayerposition是我们自定的函数,这个函数的作用是移动精灵和检测碰撞。
setPlayerposition代码如下:

copy voIDHelloWorld::setPlayerposition(Pointposition) //从像素点坐标转化为瓦片坐标 PointtileCoord=this->tileCoordFromposition(position);① //获得瓦片的GID inttileGID=_collIDable->getTileGIDAt(tileCoord);② if(tileGID>0){③ Valueprop=_tileMap->getPropertIEsForGID(tileGID);④ ValueMappropValueMap=prop.asValueMap();⑤ std::stringcollision=propValueMap["CollIDable"].asstring();⑥ if(collision=="true"){//碰撞检测成功⑦ CocosDenshion::SimpleAudioEngine::getInstance()->playEffect("empty.wav");⑧ return; >
setposition(position);
上述代码第①行this->tileCoordFromposition(position)是调用函数,实现从像素点坐标转化为瓦片坐标。第②行代码_collIDable->getTileGIDAt(tileCoord)是通过瓦片坐标获得GID值。
第③行代码tileGID > 0可以判断瓦片是否存在,tileGID == 0是瓦片不存在情况。第④行代码_tileMap->getPropertIEsForGID(tileGID)是通过地图对象的getPropertIEsForGID返回,它的返回值是Value类型。
由于Value类型可以代表很多类型。因此第⑤行代码prop.asValueMap()是将Value类型转换成为ValueMap,ValueMap类是“键-值”对。第⑥行代码propValueMap["CollIDable"].asstring()是将propValueMap变量中的CollIDable属性取出来,asstring()函数可以将Value类型转换成为std::string类型。第⑦行代码collision == "true"是碰撞检测成功情况。第⑧行代码是碰撞检测成功情况下处理,在本例中我们是播放一下音效。
tileCoordFromposition代码如下:
copy PointHelloWorld::tileCoordFromposition(Pointpos) intx=pos.x/_tileMap->getTileSize().wIDth;① inty=((_tileMap->getMapSize().height*_tileMap->getTileSize().height)-pos.y)/ _tileMap->getTileSize().height;② returnPoint(x,y); 在该函数中第①行代码pos.x / _tileMap->getTileSize().wIDth是获得x轴瓦片坐标(单位是瓦片数),pos.x是触摸点x轴坐标(单位是像素),_tileMap->getTileSize().wIDth是每个瓦片的宽度,单位是像素。代码第②行是获得y轴瓦片坐标(单位是瓦片数),这个计算有点麻烦,瓦片坐标的原点在左上角,而触摸点使用的坐标是Open GL坐标,坐标原点在左下角,表达式(_tileMap->getMapSize().height * _tileMap->getTileSize().height) - pos.y)是反转坐标轴,结果除以每个瓦片的高度_tileMap->getTileSize().height,就得到y轴瓦片坐标了。
滚动地图
由于地图比屏幕要大,当我们移动精灵到屏幕的边缘时候,那些处于屏幕之外的地图部分,应该滚动到屏幕之内。这些需要我们重新设置视点(屏幕的中心点),使得精灵一直处于屏幕的中心。但是精灵太靠近地图的边界时候,他有可能不在屏幕的中心。精灵与地图的边界距离的规定是,左右边界距离不小于屏幕宽度的一半,否则会出现图所示的左右黑边问题。上下边界距离不小于屏幕高度的一半,否则也会在上下黑边问题。
重新设置视点实现的方式很多,本章中采用移动地图位置实现这种效果。
我们在HelloWorldScene.cpp中再添加一个函数setVIEwpointCenter,添加后代码如下:
copy voIDHelloWorld::setVIEwpointCenter(Pointposition) intx=MAX(position.x,visibleSize.wIDth/2);① inty=MAX(position.y,visibleSize.height/2);② x=MIN(x,(_tileMap->getMapSize().wIDth*_tileMap->getTileSize().wIDth) -visibleSize.wIDth/2);③ y=MIN(y,0); Font-weight:bold; background-color:inherit">>getTileSize().height) -visibleSize.height/2);④ //屏幕中心点 PointpointA=Point(visibleSize.wIDth/2,visibleSize.height/2);⑤ //使精灵处于屏幕中心,移动地图目标位置 PointpointB=Point(x,y);⑥ log("目标位置(%f,%f)",pointB.x,pointB.y); //地图移动偏移量 Pointoffset=pointA-pointB;⑦ log("offset(%f,offset.x,offset.y); >setposition(offset);⑧ 在上述代码①~④是保障精灵移动到地图边界时候不会再移动,防止屏幕超出地图之外,这一点非常重要。其中第①行代码是防止屏幕左边超出地图之外,MAX(position.x,visibleSize.wIDth / 2)语句表示当position.x < visibleSize.wIDth / 2情况下,x轴坐标始终是visibleSize.wIDth / 2,即精灵不再向左移动。第②行代码与第①行代码类似,不再解释。第③行代码是防止屏幕右边超出地图之外,MIN(x,(_tileMap->getMapSize().wIDth * _tileMap->getTileSize().wIDth) - visibleSize.wIDth / 2)语句表示当x > (_tileMap->getMapSize().wIDth * _tileMap->getTileSize().wIDth) - visibleSize.wIDth / 2时候,x轴坐标始终是(_tileMap->getMapSize().wIDth * _tileMap->getTileSize().wIDth) - visibleSize.wIDth / 2表达式计算的结果。
提示visibleSize 是表示屏幕的宽度,_tileMap->getMapSize().wIDth * _tileMap->getTileSize().wIDth) - visibleSize.wIDth / 2表达式计算的是地图的宽度减去屏幕宽度的一半。
第④行代码与第③行代码类似,不再解释。
屏幕左边超出地图
屏幕右边超出地图 代码⑤~⑧行实现了移动地图效果,使得精灵一直处于屏幕的中心。A点是目前屏幕的中心点,也是精灵的位置。玩家触摸B点,精灵会向B点移动。为了让精灵保持在屏幕中心,地图一定要向相反的方向移动。
第⑤行代码Point pointA = Point(visibleSize.wIDth/2,visibleSize.height/2)是获取屏幕中心点(A点)。第⑥行代码是获取移动地图目标位置(B点)。第⑦行代码是计算A点与B点两者之差,这个差值就是地图要移动的距离。由于精灵的世界坐标就是地图层的模型坐标,即精灵的坐标原点是地图的左下角,因此第⑧行代码this->setposition(offset)是将地图坐标原点移动offset位置。

移动地图

总结

以上是内存溢出为你收集整理的忍者无敌-实例讲解Cocos2d-x瓦片地图全部内容,希望文章能够帮你解决忍者无敌-实例讲解Cocos2d-x瓦片地图所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1016765.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存