一、A星搜索
他就是一种启发性的算法,根据现在到达这个位置的步数及之后的“估计步数”,即f=g+h,f是整个从起点到终点的代价,g是从起点到我们目前位置的步数,h是从目前位置到终点的估计值,注意这里是估计值,所以我们得到解并不一定是最好的解,具体解“好”到什么程度呢?就是要根据h的估计的好坏,因此只是一个较优解。(以上的部分只能说是我对A星算法较为浅的理解,只能算是初探吧,有不足之处欢迎指正,这里只是为了更好的解释我的程序。)
我的程序的流程可以分为如下几步,
当然我们还要根据我们的游戏做一些处理即是处理地图中的碰撞等,我们要让我们的人物绕过碰撞。
二、代码
Astaritem.h
#pragma once #include "cocos2d.h" using namespace cocos2d; class AstarItem:public CCNode { public: AstarItem(void); ~AstarItem(void); void setpos(int col,int row); int getcol(){return id_col;}; int getrow(){return id_row;}; void setg(int g); int getg(){return id_g;}; void seth(int h); int geth(){return id_h;}; void setfid(int fid); int getfid(){return id_fid;}; void setf(int f); int getf(){return id_f;}; private: int id_col;//列 int id_row;//行 int id_g;// 实际代价 int id_h;// 估计代价 int id_fid;// 父节点id int id_f;// 估价函数f = g + h };
#include "Astaritem.h" AstarItem::AstarItem(void) { } AstarItem::~AstarItem(void) { } void AstarItem::setpos(int col,int row) { id_col = col; id_row = row; } void AstarItem::setg(int g) { id_g = g; } void AstarItem::seth(int h) { id_h = h; } void AstarItem::setfid(int fid) { id_fid = fid; } void AstarItem::setf(int f) { id_f = f; }
#pragma once #include "cocos2d.h" #include "AstarItem.h" using namespace cocos2d; class Astar { private: int curCol, curRow, aimCol, aimRow; int AimX, AimY, AimW, AimH; CCTMXTiledMap* map; CCArray *open; CCArray *close; CCArray *path; public: Astar(void); ~Astar(void); int getG(int col,int row,int id); int getH(int col,int row); void fromopentoclose(); void removefromopen(); void getpath(); void starseach(int fid); void resetSort(int last); bool checkclose(int col,int row); void addtoopen(int col,int row,int id); bool checkmap(int col,int row); bool checkOpen(int col,int row,int id); CCArray *findPath(int curX, int curY, int aimX, int aimY, CCTMXTiledMap* passmap); };
#include "Astar.h" #include "Astaritem.h" Astar::Astar(void) { } Astar::~Astar(void) { open->removeAllObjects(); close->removeAllObjects(); path->removeAllObjects(); } CCArray *Astar::findPath(int curX, int curY, int aimX, int aimY, CCTMXTiledMap* passmap) { //参数以及记录路径数组初始化 curCol = curX; curRow = curY; aimCol = aimX; aimRow = aimY; map = passmap; path = new CCArray(); open = new CCArray(); AstarItem * temp = new AstarItem(); open->addObject(temp); AstarItem * temp1 = new AstarItem(); temp1->setpos(curCol,curRow); temp1->setg(0); int ag = getH(curCol,curRow); temp1->seth(ag); temp1->setfid(0); temp1->setf(ag); open->addObject(temp1); close = new CCArray(); //遍历寻找路径 while(open->count() > 1){ fromopentoclose();//open和close列表管理 int fatherid = close->count() - 1; if(abs(aimCol - ((AstarItem *)close->objectAtIndex(fatherid))->getcol()) <= 1 && abs(aimRow - ((AstarItem *)close->objectAtIndex(fatherid))->getrow()) <= 1){ getpath(); break; }else{ //搜索 starseach(fatherid); } } open->removeAllObjects(); close->removeAllObjects(); //获得路径 if(path->count() == 0){ return NULL; }else{ if(((AstarItem *)path->lastObject())->getcol() != aimCol || ((AstarItem *)path->lastObject())->getrow() != aimRow){ AstarItem * temp = new AstarItem(); temp->setpos(aimCol,aimRow); path->addObject(temp); } return path; } } void Astar::starseach(int fid) { int col = ((AstarItem *)close->objectAtIndex(fid))->getcol(); int row = ((AstarItem *)close->objectAtIndex(fid))->getrow(); //搜索目前点的上下左右四个方向 int mycol = col; int myrow = row - 1; if(myrow >= 0 && checkmap(mycol,myrow)){ if(checkOpen(mycol,myrow,fid) && checkclose(mycol,myrow)){ addtoopen(mycol,myrow,fid); } } mycol = col - 1; myrow = row; if(mycol >= 0 && checkmap(mycol,myrow)){ if(checkOpen(mycol,myrow,fid) && checkclose(mycol,myrow)){ addtoopen(mycol,myrow,fid); } } mycol = col; myrow = row + 1; if(myrow < map->getMapSize().width && checkmap(mycol,myrow)){ if(checkOpen(mycol,myrow,fid) && checkclose(mycol,myrow)){ addtoopen(mycol,myrow,fid); } } mycol = col + 1; myrow = row; if(mycol < map->getMapSize().height && checkmap(mycol,myrow)){ if(checkOpen(mycol,myrow,fid) && checkclose(mycol,myrow)){ addtoopen(mycol,myrow,fid); } } } void Astar::addtoopen(int col,int row,int id) { //向open列表中加入点 AstarItem * temp = new AstarItem(); temp->setpos(col,row); temp->setfid(id); int g = getG(col, row, id); int h = getH(col, row); temp->setg(g); temp->seth(h); temp->setf(g + h); open->addObject(temp); resetSort(open->count() - 1); } bool Astar::checkclose(int col,int row) { //检查close列表 for(int i = close->count() - 1;i > 0;i --){ if(((AstarItem *)close->objectAtIndex(i))->getcol() == col && ((AstarItem *)close->objectAtIndex(i))->getrow() == row){ return false; } } return true; } bool Astar::checkOpen(int col,int row,int id) { //检查open列表中是否有更小的步长,并排序 for(int i = open->count() - 1;i > 0;i --){ if(((AstarItem *)open->objectAtIndex(i))->getcol() == col && ((AstarItem *)open->objectAtIndex(i))->getrow() == row){ int tempG = getG(col,row,id); if(tempG < ((AstarItem *)open->objectAtIndex(i))->getg()){ ((AstarItem *)open->objectAtIndex(i))->setg(tempG); ((AstarItem *)open->objectAtIndex(i))->setfid(id); ((AstarItem *)open->objectAtIndex(i))->setf(tempG + ((AstarItem *)open->objectAtIndex(i))->geth()); resetSort(i); } return false; } } return true; } bool Astar::checkmap(int col,int row) { //检查地图中是否有碰撞 if(abs(aimCol - col) > 1 || abs(aimRow - row) > 1){ CCTMXLayer* layer = map->layerNamed("trees2"); int tilegid = layer->tileGIDAt(ccp(col,row)); CCDictionary *tiledic = map->propertiesForGID(tilegid); if (tiledic) { CCDictionary *properties = map->propertiesForGID(tilegid); if (properties) { const CCString *collision = properties->valueForKey("conflict"); if (collision && collision->compare("true") == 0) { return false; } } } // CCString *mvalue = (CCString *)tiledic->objectForKey("conflict"); // int mv = mvalue->intValue(); // if(mv == 1){ // return false; // } } return true; } void Astar::getpath() { //从整个close数组中找出路径 if(path->count() == 0){ path->addObject(close->objectAtIndex(close->count() - 1)); }else{ path->insertObject(close->objectAtIndex(close->count() - 1),path->count() - 1); } while(true){ if(((AstarItem *)path->objectAtIndex(0))->getg() == 0){ break; } path->insertObject(close->objectAtIndex(((AstarItem *)path->objectAtIndex(0))->getfid()),0); } curCol = aimCol; curRow = aimRow; } void Astar::fromopentoclose() { //把open列表中的点放到close列表中 AstarItem * temp = (AstarItem *)open->objectAtIndex(1); close->addObject(temp); removefromopen(); } void Astar::removefromopen() { int last = open->count() - 1; open->replaceObjectAtIndex(1,open->lastObject(),true); open->removeLastObject(); last = open->count() - 1; //堆排序 int head = 1; while((head * 2 + 1) <= last){ int child1 = head * 2; int child2 = head * 2 + 1; int childmin = (((AstarItem *)open->objectAtIndex(child1))->getf() < ((AstarItem *)open->objectAtIndex(child2))->getf() ? child1:child2); if(((AstarItem *)open->objectAtIndex(head))->getf() <= ((AstarItem *)open->objectAtIndex(childmin))->getf()){ break; } //AstarItem * temp = (AstarItem *)open->objectAtIndex(childmin); open->replaceObjectAtIndex(childmin,open->objectAtIndex(head),false); //temp->release(); } } void Astar::resetSort(int last) { //根据步长排序,堆排序 while(last > 1){ int half = last/2; if(((AstarItem *)open->objectAtIndex(half))->getf() <= ((AstarItem *)open->objectAtIndex(last))->getf()) break; AstarItem * temp = (AstarItem *)open->objectAtIndex(last); open->replaceObjectAtIndex(last,open->objectAtIndex(half),false); open->replaceObjectAtIndex(half,temp,true); temp->release(); } } int Astar::getG(int col,int row,int id) { //获得该点的g函数值 int fx = ((AstarItem *)close->objectAtIndex(id))->getcol(); int fy = ((AstarItem *)close->objectAtIndex(id))->getrow(); int fg = ((AstarItem *)close->objectAtIndex(id))->getg(); if(fx - col != 0 && fy - row != 0){ return fg + 14; }else{ return fg + 10; } } int Astar::getH(int col,int row) { //获得该点的h值 return abs(aimCol - col) * 10 + abs(aimRow - row) * 10; }
A星算法的逻辑部分基本完成。任何一个算法要在游戏开发的过程中使用,需要有特定的场景。这里设定一个简单的场景:45度角地图中,主角需要绕过树到达你点击的目标点。
MapScene.h
#ifndef __MAPSCENE_H__ #define __MAPSCENE_H__ #include "cocos2d.h" #include "SimpleAudioEngine.h" #include "Astar.h" #include "AstarItem.h" using namespace cocos2d; class MapScene :public CCLayer { private: CCArray * path; int stepindex; int smallstepindex; public: int vmove; int hmove; Astar * myastar; CCSprite* m_tamara; virtual bool init(); void update(float dt); //void MapScene::update1(ccTime dt); static cocos2d::CCScene* scene(); CCPoint convertto2d(float x,float y); virtual void menuCloseCallback(CCObject* pSender); virtual void ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent); CREATE_FUNC(MapScene); }; #endif
#include "MapScene.h" #include "Astar.h" #include "AstarItem.h" #include <math.h> using namespace cocos2d; enum { kTagTileMap = 1, }; CCScene* MapScene::scene() { CCScene *scene = CCScene::create(); MapScene *layer = MapScene::create(); scene->addChild(layer); return scene; } // on "init" you need to initialize your instance bool MapScene::init() { if ( !CCLayer::init() ) { return false; } //初始化地图 CCTMXTiledMap *map = CCTMXTiledMap::create("iso-test-zorder.tmx"); addChild(map, 0, kTagTileMap); CCSize s = map->getContentSize(); CCSize winSize = CCDirector::sharedDirector()->getWinSize(); ////----UXLOG("ContentSize: %f, %f", s.width,s.height); map->setPosition(ccp(-s.width/2 + winSize.width/2,0)); //初始化人物 m_tamara = CCSprite::create("Icon.png"); map->addChild(m_tamara, map->getChildren()->count() ); m_tamara->retain(); int mapWidth = map->getMapSize().width * map->getTileSize().width; int mapHeight = map->getMapSize().height * map->getTileSize().height; m_tamara->setPosition(ccp( mapWidth/2,112)); m_tamara->setAnchorPoint(ccp(0.5f,0)); setTouchEnabled(true); scheduleUpdate(); vmove = -1; hmove = -1; stepindex = -1; myastar = new Astar(); return true; } //坐标与地图位置的转换 CCPoint MapScene::convertto2d(float x,float y){ CCTMXTiledMap* map = (CCTMXTiledMap*) getChildByTag(kTagTileMap); int mapWidth = map->getMapSize().width * map->getTileSize().width; int mapHeight = map->getMapSize().height * map->getTileSize().height; double distanse; double sin1; double sin11; double sin22; double cos11; double cos1; int d2x; int d2y; double mystatic5 = sqrt(5.0);//«Û∏˘∫≈5 double mystatic = 16 * mystatic5;//–°ÕºøÈ¿‚≥§ //char mch[256]; if(x > mapWidth/2){ distanse = sqrt((x - mapWidth/2) * (x - mapWidth/2) + (mapHeight - y) * (mapHeight - y)); sin1 = (mapHeight - y)/distanse; cos1 = (x - mapWidth/2)/distanse; sin11 = (sin1 * 2 - cos1) / mystatic5; cos11 = (sin1 + cos1 * 2) / mystatic5; d2y = distanse * 5 / 4 * sin11 / mystatic; sin22 = (2 * sin1 + cos1) / mystatic5; d2x = distanse * 5 / 4 * sin22 / mystatic; return ccp(d2x,d2y); }else{ distanse = sqrt((mapWidth/2 - x) * (mapWidth/2 - x) + (mapHeight - y) * (mapHeight - y)); sin1 = (mapHeight - y)/distanse; cos1 = (mapWidth/2 - x)/distanse; sin11 = (sin1 * 2 - cos1) / mystatic5; cos11 = (sin1 + cos1 * 2) / mystatic5; d2x = distanse * 5 / 4 * sin11 / mystatic; //sin22 = 4.0 * cos11 / 5 + 3.0 * sin11 / 5; sin22 = (2 * sin1 + cos1) / mystatic5; d2y = distanse * 5 / 4 * sin22 / mystatic; return ccp(d2x,d2y); } } void MapScene::update(float dt) { CCPoint herop = m_tamara->getPosition(); CCPoint mapindex = convertto2d(herop.x,herop.y); //根据路径移动 if(stepindex >= 1){ if(smallstepindex == 0){ int ncol = ((AstarItem *)path->objectAtIndex(stepindex))->getcol(); int nrow = ((AstarItem *)path->objectAtIndex(stepindex))->getrow(); int pcol = ((AstarItem *)path->objectAtIndex(stepindex - 1))->getcol(); int prow = ((AstarItem *)path->objectAtIndex(stepindex - 1))->getrow(); if(pcol == ncol){ if(prow > nrow){ vmove = 2; }else if(prow < nrow){ vmove = 3; }else{ vmove = -1; } }else if(prow == nrow){ if(pcol > ncol){ vmove = 1; }else if(pcol < ncol){ vmove = 0; }else{ vmove = -1; } }else{ vmove = -1; } } if(vmove == 0){ herop.x += 1; herop.y -= 0.5; }else if(vmove == 1){ herop.x -= 1; herop.y += 0.5; }else if(vmove == 2){ herop.x += 1; herop.y += 0.5; }else if(vmove == 3){ herop.x -= 1; herop.y -= 0.5; }else if(vmove == 4){ herop.x += 1; }else if(vmove == 5){ herop.x -= 1; }else if(vmove == 6){ herop.y += 0.5; }else if(vmove == 7){ herop.y -= 0.5; } smallstepindex ++; if(smallstepindex >= 32){ smallstepindex = 0; if(stepindex >= path->count() - 1){ stepindex = -1; vmove = -1; }else{ stepindex ++; vmove = -1; } } } m_tamara->setPosition(herop); //地图随主角移动 CCTMXTiledMap* map = (CCTMXTiledMap*) getChildByTag(kTagTileMap); CCSize s = map->getContentSize(); int mapWidth = map->getMapSize().width * map->getTileSize().width; int mapHeight = map->getMapSize().height * map->getTileSize().height; float deltax = herop.x - mapWidth/2; float deltay = herop.y - 112; CCSize winSize = CCDirector::sharedDirector()->getWinSize(); map->setPosition(ccp(-s.width/2 + winSize.width/2 - deltax,-deltay)); } void MapScene::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) { CCSetIterator it = pTouches->begin(); CCTouch* touch = (CCTouch*)(*it); CCPoint m_tBeginPos = touch->getLocationInView(); m_tBeginPos = CCDirector::sharedDirector()->convertToGL( m_tBeginPos ); char mch[256]; CCTMXTiledMap* map = (CCTMXTiledMap*) getChildByTag(kTagTileMap); CCPoint mapp = map->getPosition(); //获得触摸点位置在地图上的索引(行列) CCPoint aimmapindex = convertto2d(m_tBeginPos.x - mapp.x,m_tBeginPos.y - mapp.y); if(aimmapindex.x < 0 || aimmapindex.y < 0 || aimmapindex.x >= map->getMapSize().width || aimmapindex.y >= map->getMapSize().height) { return; } CCPoint herop = m_tamara->getPosition(); CCPoint mapindex = convertto2d(herop.x,herop.y); CCTMXLayer* layer = map->layerNamed("trees2"); int tilegid = layer->tileGIDAt(ccp(aimmapindex.x,aimmapindex.y)); if (tilegid) { CCDictionary *properties = map->propertiesForGID(tilegid); if (properties) { const CCString *collision = properties->valueForKey("conflict"); if (collision && collision->compare("true") == 0) { return; } } } //A星搜索 path = myastar->findPath(mapindex.x,mapindex.y,aimmapindex.x,aimmapindex.y,map); stepindex = 1; smallstepindex = 0; } void MapScene::menuCloseCallback(CCObject* pSender) { // "close" menu item clicked CCDirector::sharedDirector()->end(); }
资源下载:http://download.csdn.net/detail/my183100521/6721821
参考书藉:《Cocos2D-X权威指南》