cocos2dx 利用遮罩層來實現地圖的簡單尋路
談到地圖不少人都說要做地圖編輯器了,但是我暫時繞過這一步,如果不用尋路地圖就不能移動?尋路就是會繞過障礙物的演算法。
我做了一個簡單的地圖的思想,就是地圖分層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廢話不多,繼續走向精彩,詳細解決一下背景層我們應該做些什麼東西,有什麼內容?
這裡有人說,那其他不帶主角功能的怎麼辦?繼承你寫的精靈類拓展成怪物類(智慧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);
}
好了,大家如果理解,可以自己研究一下自己的方式去實現一下這樣一套,人物移動,地圖移動的原理,當然,我在這裡宣告一下
這套實現思路其實是很歪門,另類的,應為他並沒有採用尋路,但是你也不能完全說不採用尋路演算法的地圖系統就不行。