談到地圖不少人都說要做地圖編輯器了,但是我暫時繞過這一步,如果不用尋路地圖就不能移動?尋路就是會繞過障礙物的算法。
我做了一個簡單的地圖的思想,就是地圖分層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
我建了一個QQ群:和大家一起分享cocos2dx開發經驗【41131516】