首页 > 代码库 > cocos2d-x 地图活起来了 (转)
cocos2d-x 地图活起来了 (转)
谈到地图不少人都说要做地图编辑器了,但是我暂时绕过这一步,如果不用寻路地图就不能移动?寻路就是会绕过障碍物的算法。
我做了一个简单的地图的思想,就是地图分层3层:背景层、可行区域层、遮罩层,但是地图就不寻路了,通过设置可行区域层来
实现地图障碍物的方法。下面看一个视图,我把地图详细的分层了:
?
OK,有了这个思路,大家应该也知道我要怎么做了?代码实现上怎么处理呢?
重点:可行区域层原理是根据点击屏幕上的坐标点来取得这个点是否透明!如果不透明那就不让他进行移动,透明则为不可行区域;
首先感谢一下为我提供取色源码的哥们(firedragonpzy),帮助我实现了这个另类的地图设计;下面我贴一下他的源码,
新建了FDPixelSprite.cpp,FDPixelSprite.h代码如下:
FDPixelSprite.h
// //? FDPixelSprite.h //? PixelDemo // //? Created by firedragonpzy on 13-2-19. // // #ifndef __PixelDemo__FDPixelSprite__ #define __PixelDemo__FDPixelSprite__ #include "cocos2d.h" USING_NS_CC; class FDPixelSprite : public CCSprite, public CCTargetedTouchDelegate { public: ??? FDPixelSprite(); ??? virtual ~FDPixelSprite(); ??? ??? void onEnter(); ??? void onExit(); ??? void setimg(CCString Url); ??? ??? FDPixelSprite* create(CCString Url); ??? CCImage* img ; ??? ??? CCRect atlasRect(); ??? bool isContainTouchLocation(CCTouch *pTouch); ??? ??? bool ccTouchBegan(CCString thismapurl,CCTouch *pTouch, CCEvent *pEvent); ??? void ccTouchMoved(CCTouch *pTouch, CCEvent *pEvent); ??? void ccTouchEnded(CCTouch *pTouch, CCEvent *pEvent); ??? CC_SYNTHESIZE(const char*, m_pName,Name); }; #endif /* defined(__PixelDemo__FDPixelSprite__) */
FDPixelSprite.cpp
// //? FDPixelSprite.cpp //? PixelDemo // //? Created by firedragonpzy on 13-2-19. // // #include "FDPixelSprite.h" #include "FontChina.h" FDPixelSprite::FDPixelSprite() {} FDPixelSprite::~FDPixelSprite() {} FDPixelSprite* FDPixelSprite::create(CCString Url) { ??? FDPixelSprite *sprite = new FDPixelSprite(); ??? if (sprite && sprite->initWithFile(Url.getCString())) { ??????? sprite->setName(Url.getCString()); ??????? sprite->autorelease(); ??????? return sprite; ??? } ??? CC_SAFE_DELETE(sprite); ??? sprite = NULL; ??? ??? ??? return NULL; } void FDPixelSprite::setimg(CCString Url){ ??? img= new CCImage(); ??? img->initWithImageFileThreadSafe(CCFileUtils::sharedFileUtils()->fullPathForFilename(Url.getCString()).c_str()); } void FDPixelSprite::onEnter() { ??? CCSprite::onEnter(); ??? CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true); } void FDPixelSprite::onExit() { ??? CCSprite::onExit(); ??? CCDirector::sharedDirector()->getTouchDispatcher()->removeDelegate(this); } bool FDPixelSprite::ccTouchBegan(CCString thismapurl,cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { ??? if (this->isContainTouchLocation(pTouch) ) { ??????? ccColor4B c = {0, 0, 0, 0}; ??????? ??????? CCSize winSize = CCDirector::sharedDirector()->getWinSize(); ??????? ??????? CCPoint touchPoint = pTouch->getLocationInView(); ??????? ??????? CCSize cSize = this->getContentSize(); ??????? CCPoint point =this->getAnchorPointInPoints(); ??????? point = ccp(cSize.width - point.x,cSize.height- point.y); ??????? CCPoint pos(this->getPositionX() - point.x,winSize.height-this->getPositionY()- point.y); ??????? ??????? CCPoint localPoint = ccp(touchPoint.x - pos.x, ???????????????????????????????? touchPoint.y -pos.y); ??????? ??????? float scaleFactor = CCDirector::sharedDirector()->getContentScaleFactor(); ??????? unsigned int x = localPoint.x? * scaleFactor, y = localPoint.y * scaleFactor; ??????? ??????? float _width = this->getContentSize().width*scaleFactor; ??????? //This method is currently only supports symmetric image ??????? //unsigned char *data_ = this->getTexture()->getFDImageData(); ??????? ??????? //Efficiency of this method is relatively low ??????? //CCImage * img = new CCImage(); ??????? //img->initWithImageFileThreadSafe(CCFileUtils::sharedFileUtils()->fullPathForFilename(thismapurl.getCString()).c_str()); ??????? unsigned char *data_ = img->getData(); ??????? ??????? ??????? unsigned int *pixel = (unsigned int *)data_; ??????? pixel = pixel + (y * (int)_width)* 1 + x * 1; ??????? c.r = *pixel & 0xff; ??????? c.g = (*pixel >> 8) & 0xff; ??????? c.b = (*pixel >> 16) & 0xff; ??????? c.a = (*pixel >> 24) & 0xff; ??????? if (c.a == 0) { ??????????? CCLog(FontChina::G2U("不可点击!")); ??????????? return false; ??????? }else ??????? { ??????????? CCLog(FontChina::G2U("可点击!")); ??????????? return true; ??????? } ??? } ??? return false; } void FDPixelSprite::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { ??? //CCPoint pos = this->getPosition(); ??? //CCPoint sub = pTouch->getDelta(); ??? //this->setPosition(ccpAdd(pos, sub)); } void FDPixelSprite::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) { ??? //CCLog("firedragonpzy:ccTouchEnded"); } CCRect FDPixelSprite::atlasRect() { ??? CCSize cSize = this->getContentSize(); ??? CCPoint point = this->getAnchorPointInPoints(); ??? return CCRectMake( -point.x, -point.y, cSize.width,cSize.height); } bool FDPixelSprite::isContainTouchLocation(cocos2d::CCTouch *pTouch) { ?? return this->atlasRect().containsPoint(convertTouchToNodeSpaceAR(pTouch)); }
有了他们我们就能判断地图上是否可行了。OK废话不多,继续走向精彩,详细解决一下背景层我们应该做些什么东西,有什么内容?
背景层肯定要装载精灵,把我们之前第二章说的【cocos2d-x 大型ARPG手游研发----精灵的八面玲珑】精灵加载出来,就可以当主角了。
这里有人说,那其他不带主角功能的怎么办?继承你写的精灵类拓展成怪物类(智能AI攻击操作),NPC(任务功能模块),可拓展行是
杠杠滴,继承下来NPC都能移动,和你打起来,我的思路是把地图做成一个大容器,每一块新地图继承一个MapsBase的同时他自己也有
有自己的特殊逻辑和特殊业务;
继续贴代码,地图是这么实现的:
Maps_Diyu.h
#include "cocos2d.h" #include "../Commen/FDPixelSprite.h" #include "../Spirits/SpiritsPlayer.h" USING_NS_CC; class Maps_Diyu :public cocos2d::CCSprite { public: ??? Maps_Diyu(CCLayer* layer,CCString mapsurl,CCString mapsurl_1,int zOrder,CCPoint cp); ??? ~Maps_Diyu(void);
??? CCSprite* nowmap; ??? CCSprite* nowmap_zhezhao; ??? //基本数据 ??? float lastmove_x,lastmove_y; ??? bool moveMapto(CCPoint cp,FDPixelSprite* mainmap_Touch); ??? CCActionInterval* act_moveto_maps; ??? CCActionInterval* act_moveto_maps_touch; ??? CCActionInterval* act_moveto_maps_zhezhao; ??? private: ??? ??? SpiritsPlayer* role_main; ??? CCAnimate* playdonghua; };
Maps_Diyu.cpp
#include "Maps_Diyu.h" #include "../GameData/GetNPCData.h" #include "../Commen/FontChina.h" #include "../Spirits/SpiritsMonster.h" #include "../Effects/SkillEffects.h" Maps_Diyu::Maps_Diyu(CCLayer* layer,CCString mapsurl,CCString mapsurl_1,int zOrder,CCPoint cp) { ??? act_moveto_maps=NULL; ??? act_moveto_maps_zhezhao=NULL; ??? lastmove_x=0; ??? lastmove_y=0; ??? float map_x , map_y; ??? float center_x,center_y; ??? CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); ??? CCSize size = CCDirector::sharedDirector()->getWinSize(); ??? nowmap = Maps_Diyu::create(mapsurl.getCString()); ??? nowmap_zhezhao = Maps_Diyu::create(mapsurl_1.getCString()); ??? center_x = size.width/2; ??? center_y = size.height/2; ??? map_y = nowmap->getAnchorPointInPoints().y+origin.y; ??? map_x = nowmap->getAnchorPointInPoints().x; ??? if(cp.getLength()>0) ??? { ??????? nowmap->setPosition(cp); ??????? nowmap_zhezhao->setPosition(cp); ??? } ??? else ??? { ??????? nowmap->setPosition(ccp(map_x,map_y)); ??????? nowmap_zhezhao->setPosition(ccp(map_x,map_y)); ??? } ??? //计算地图上绝对位置的原点坐标 ??? float map_fornpc_x,map_fornpc_y; ??? map_fornpc_x= nowmap->getContentSize().width/2; ??? map_fornpc_y=nowmap->getContentSize().height/2; ??? //主角加载 ??? GetNPCData* basedatas = new GetNPCData(); ??? basedatas->GetNPCchapter1(); ??? basedatas->role_player.nowpoint= CCPointMake(map_fornpc_x+428,map_fornpc_y+480); ??? role_main = new SpiritsPlayer(basedatas->role_player,1,false); ??? role_main->npc->retain(); ??? //加载NPC ??? basedatas->role_mengpo.nowpoint= CCPointMake(map_fornpc_x+158,map_fornpc_y+510); ??? SpiritsPlayer* role_mengpo= new SpiritsPlayer(basedatas->role_mengpo,1,true); ??? nowmap->addChild(role_mengpo->npc, 2); //------------------------------------------------------- ??? nowmap->addChild(role_main->npc, 2,999); ??? layer->addChild(nowmap_zhezhao, zOrder+1); ??? layer->addChild(nowmap, zOrder); }
OK,地图初始化就加载了这些基础的数据,这些应该大家都能看懂,下面贴最核心的代码,如何把他们都关联起来
并且做移动操作呢??????
/************************* 参数说明: CCPoint cp? 可行区域的坐标 mainmap_Touch 可行区域,需要去随时改变他移动 **************************/ bool Maps_Diyu::moveMapto(CCPoint cp,FDPixelSprite* mainmap_Touch) { ??? float center_x,center_y,move_x,move_y, map_x , map_y ,to_c_x,to_c_y; ??? CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); ??? CCSize size = CCDirector::sharedDirector()->getWinSize(); ??? center_x = size.width/2; ??? center_y = size.height/2; ??? move_x = center_x-cp.x; ??? move_y = center_y-cp.y; ??? map_x = nowmap->getPositionX(); ??? map_y = nowmap->getPositionY(); ??? to_c_x = nowmap->getContentSize().width/2; ??? to_c_y = nowmap->getContentSize().height/2+origin.y; ??? map_x = map_x + move_x; ??? map_y? = map_y + move_y-origin.y; ??? //计算移动时间,这边大家可以帮我优化一下 ??? //现在就这块移动时间有一些问题 ??? float a1 , b1 ; ??? a1 = fabs(move_x)/size.width; ??? b1 = fabs(move_y)/size.height; ??? float movetime = ((a1+b1)*8); ??? if(movetime<=1) ??? { ??????? movetime=1; ??? } ??? //这里是精华,主要是处理任意地图放进来之后, ??? //防止显示区域超出地图的长宽,移动到边界就不能移动了! ??? if(map_x>=to_c_x) ??? { ??????? map_x = to_c_x; ??? } ??? else if(map_x<=-((nowmap->getContentSize().width/2)-size.width)) ??? { ??????? map_x =-((nowmap->getContentSize().width/2)-size.width); ??? } ??? if(map_y>=to_c_y) ??? { ??????? map_y = to_c_y; ??? } ??? else if(map_y <= -((nowmap->getContentSize().height/2)-size.height)) ??? { ??????? map_y = -((nowmap->getContentSize().height/2)-size.height); ??? } ??? //经典中的经典// ??? //主角移动 ??? CCPoint role_move_pc = nowmap->convertToNodeSpace(ccp(cp.x,cp.y));//此处需要通过地图的视角把人物移动的坐标转化一下。 ??? role_main->moveTomap_dir(role_move_pc);??? ??? role_main->moveTomap_move(movetime,role_move_pc,false); ??? //地图移动 ??? if(map_x!=lastmove_x&&map_y!=lastmove_y) ??? { ??????? nowmap->stopAction(act_moveto_maps); ??????? nowmap_zhezhao->stopAction(act_moveto_maps_zhezhao); ??????? mainmap_Touch->stopAllActions(); ??????? act_moveto_maps = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); ??????? act_moveto_maps_touch = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); ??????? act_moveto_maps_zhezhao = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); ??????? nowmap->runAction(act_moveto_maps); ??????? nowmap_zhezhao->runAction(act_moveto_maps_zhezhao); ??????? mainmap_Touch->runAction(act_moveto_maps_touch); ??????? return true; ??? } ??? else????????????????? ??? { ??????? return false; ??? } }
核心的地方有三处,帮大家分析一下:
第一,就是计算移动时间,我是根据屏幕长宽来计算,这个地方一直是我心结,这个方法效果现在很不好,跑起来
长距离用时长,短距离就很快,所以请大家也帮我优化一下,可以往下贴代码,
第二,就是计算出地图移动的区域,你不可能随便丢一张图进去,地图超过边界会显示黑色,不能让黑色显示出来(除非丢进来的图小过屏幕地图);
第三,就是通过地图移动的标识来进行人物和地图的移动,在人物移动的时候需要转化一下成地图的坐标!
map_x!=lastmove_x&&map_y!=lastmove_y 可移动的标识 act_moveto_maps = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); act_moveto_maps_touch = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); act_moveto_maps_zhezhao = CCMoveTo::create(movetime,ccp((int)map_x,(int)map_y)); 大家也看到,我移动的时候,移动的是3个层,这下就保证了可行区域也是不停在变动的
然后就是如何传数据了,几句话就可以搞定Scene加载的地图上所有层;
Scene_Diyu.h
#include "cocos2d.h" #include "ToScene.h" #include "../MapSpirits/Maps_Diyu.h" USING_NS_CC; class Scene_Diyu : public CCLayer { public: ??? Scene_Diyu(void); ??? ~Scene_Diyu(void); ??? Maps_Diyu* mainmap; ??? FDPixelSprite* mainmap_Touch; ??? void nextCallback(CCObject* pSender); ??? virtual void registerWithTouchDispatcher(void); ??? virtual bool ccTouchBegan(CCTouch *pTouch,CCEvent *pEvent); ??? virtual void ccTouchMoved(CCTouch *pTouch,CCEvent *pEvent); ??? virtual void ccTouchEnded(CCTouch *pTouch,CCEvent *pEvent); ??? virtual void ccTouchCancelled(CCTouch *pTouch,CCEvent *pEvent); };
Scene_Diyu.cpp
#include "Scene_Diyu.h" #include "../ImagePaths.h" #include "../PublicUI/BaseUI.h" Scene_Diyu::Scene_Diyu(void) { ??? float x,y; ??? CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin(); ??? CCSize size = CCDirector::sharedDirector()->getWinSize(); ??? x = size.width; ??? y = size.height; ??? //地图 ??? mainmap = new Maps_Diyu(this,"map_diyu_naihe.jpg","map_diyu_naihe1.png",0,ccp(x/2-308,y/2-486)); ??? mainmap_Touch =? mainmap_Touch->create("map_diyu_naihe0.png"); ??? mainmap_Touch->setimg("map_diyu_naihe0.png"); ??? mainmap_Touch->setPosition(ccp(x/2-308,y/2-486)); ??? mainmap_Touch->setVisible(false);//是否显示点击层 ??? BaseUI* baseui = new BaseUI(this); ??? this->addChild(mainmap_Touch, 0); ??? setTouchEnabled(true); } void Scene_Diyu::registerWithTouchDispatcher()? {? ??? CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this,0,true);? }? bool Scene_Diyu::ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)? {? ??? if(mainmap_Touch->ccTouchBegan("map_diyu_naihe0.png",pTouch,pEvent)==true) ??? { ??????? mainmap->moveMapto(pTouch->getLocation(),mainmap_Touch); ??? } ??? return true;? }? ? void Scene_Diyu::ccTouchMoved(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)? {? }? ? void Scene_Diyu::ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)? {? }? ? void Scene_Diyu::ccTouchCancelled(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent)? {? }? Scene_Diyu::~Scene_Diyu(void) { }
?
?大家也看到了再Scene里面控制的点击事件主要就是处理可行区域的:
if(mainmap_Touch->ccTouchBegan("map_diyu_naihe0.png",pTouch,pEvent)==true) ??? { ??????? mainmap->moveMapto(pTouch->getLocation(),mainmap_Touch); ??? }
好了,大家如果理解,可以自己研究一下自己的方式去实现一下这样一套,人物移动,地图移动的原理,当然,我在这里声明一下
这套实现思路其实是很歪门,另类的,应为他并没有采用寻路,但是你也不能完全说不采用寻路算法的地图系统就不行。
跑起来看一下效果截图:
跑起来后的效果图!!!
人物被遮罩层遮挡的效果图!!
这篇就讲这么多了,下一篇直接讲一下【怪物智能AI的制作】怪物实现追踪主角,怪物随机生成怪圈,怪物随机移动巡逻。
?
?
游戏demo及素材下载地址(demo里面包含了所有的素材资料);
http://pan.baidu.com/share/link?shareid=4012433582&uk=4097703620&third=15
?
http://www.cnblogs.com/zisou/p/cocos2d-xARPG3.html
?
cocos2d-x 地图活起来了 (转)