一、A星搜索
他就是一种启发性的算法,根据现在到达这个位置的步数及之后的“估计步数”,即f=g+h,f是整个从起点到终点的代价,g是从起点到我们目前位置的步数,h是从目前位置到终点的估计值,注意这里是估计值,所以我们得到解并不一定是最好的解,具体解“好”到什么程度呢?就是要根据h的估计的好坏,因此只是一个较优解。(以上的部分只能说是我对A星算法较为浅的理解,只能算是初探吧,有不足之处欢迎指正,这里只是为了更好的解释我的程序。)
我的程序的流程可以分为如下几步,
- 有一个open表,有一个close表,open表首先存储我们的起点,然后由此出发。
- 首先把起点放入close列表,并检测这点周围点的f值(g+h,h我们通过本点到终点的横纵索引差估计而来)。
- 把剩下的点放入open列表中,并根据f值进行堆排序。
- 把f值最小的点放入close列表中,
- 继续上面的循环,继续处理,直到找到终点为止。
当然我们还要根据我们的游戏做一些处理即是处理地图中的碰撞等,我们要让我们的人物绕过碰撞。
二、代码
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
};
Astaritem.cpp
#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;
}
Astar.h
#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);
};
Astar.cpp
#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;
}
注:checkmap(int col,int row) 方法中,获得该位置地图上的 trees2层的" conflict"属性值,如果是碰撞的不能通过的位置,则返回false,否则返回true.
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
MapScene.cpp
#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权威指南》