上一篇:正六边形:平铺
下一篇:正六边形:寻路优化
寻路使用的A*寻路,相关代码如下:
处理正六边形网格数据:
const double sqrt3 = 1.732051;
const int sideLen = 50;
const int rowGridNum = 14;
const int colGridNum = 8;
void HelloWorld::addSixSideGrid()
{
char buff[32] = { 0 };
std::vector<AStar::MapData> childVec;
AStar::MapData tmpData;
for (int i = 0; i <= rowGridNum; i++)
{
childVec.clear();
for (int j = 0; j <= colGridNum; ++j)
{
tmpData.grid.x = i;
tmpData.grid.y = j;
auto sp = Sprite::create("six_side.png");
auto pos = this->getPosByGrid(i, j);
sp->setPosition(pos);
this->addChild(sp);
auto txt = ui::Text::create();
if (i == rowGridNum / 2 && j == colGridNum / 2)
{
txt->setString("start");
}
else
{
sprintf(buff, "%d,%d", tmpData.grid.x, tmpData.grid.y);
txt->setString(buff);
}
txt->setTextColor(Color4B::BLACK);
txt->setFontSize(24);
sp->addChild(txt);
txt->setPosition(sp->getContentSize() * 0.5);
tmpData.grid.posX = pos.x;
tmpData.grid.posY = pos.y;
childVec.push_back(tmpData);
}
mapVec.push_back(childVec);
}
}
寻路算法:
AStar.h
#ifndef __AStar__
#define __AStar__
#include <algorithm>
#include <vector>
struct Grid
{
Grid() {}
Grid(int x, int y) {
this->x = x;
this->y = y;
}
int x = 0;
int y = 0;
double posX = 0;
double posY = 0;
double distance(const Grid& grid) {
double dis = pow(grid.x - x,2) + pow(grid.y - y,2);
return dis;
}
bool operator==(const Grid& grid) const {
return this->x == grid.x && this->y == grid.y;
}
};
class AStar
{
public:
AStar();
~AStar();
struct MapData
{
Grid grid;
bool visited = false;
bool isSpace = true;
void reset() {
visited = false;
}
};
MapData* getMapDataByGrid(const Grid& grid, std::vector<std::vector<MapData>>& arr);
void findPath(const Grid & startPos,const Grid &endPos, std::vector<std::vector<MapData>> &arr,std::vector<Grid> &pathVec);
};
#endif /* defined(__AStar__) */
AStar.cpp
#include "AStar.h"
#include <iostream>
#include <map>
struct Data
{
Data() {}
Data(Grid pos, double h, double g, Data* parent) {
this->grid = pos;
this->h = h;
this->g = g;
this->parent = parent;
}
Grid grid = Grid(0, 0);
double h = 0;
double g = 0;
Data* parent = nullptr;
double f() {
return this->g + this->h;
}
};
struct MinHeap
{
std::vector<Data*> m_vec;
std::map<std::string, Data*> m_map;
int index = 0;
Data* getMinAndRemove() {
if (isEmpty()) {
return nullptr;
}
sort();
auto first = m_vec.at(0);
auto last = m_vec.at(index - 1);
m_vec[0] = last;
--index;
return first;
}
bool isEmpty() {
return index <= 0;
}
std::string getKey(const Grid& grid) {
char buff[32] = { 0 };
sprintf(buff, "%d-%d", grid.x, grid.y);
return buff;
}
Data* find(const Grid& grid) {
auto it = m_map.find(getKey(grid));
if (it != m_map.end())
{
return it->second;
}
return nullptr;
}
void add(Data* data) {
if (index < m_vec.size())
{
m_vec[index] = data;
}
else
{
m_vec.push_back(data);
}
index = m_vec.size();
m_map[getKey(data->grid)] = data;
}
void sort() {
std::sort(m_vec.begin(), m_vec.end(), [](Data* a, Data* b) {return a->f() < b->f(); });
}
void release() {
for (auto it = m_map.begin(); it != m_map.end();)
{
Data* tmp = it->second;
it = m_map.erase(it);
delete tmp;
}
}
};
AStar::AStar()
{}
AStar::~AStar()
{
}
AStar::MapData* AStar::getMapDataByGrid(const Grid& grid, std::vector<std::vector<MapData>>& arr)
{
auto& vec = arr[grid.x];
for (auto &a:vec)
{
if (a.grid.y==grid.y)
{
return &a;
}
}
return nullptr;
}
void AStar::findPath(const Grid& startPos, const Grid& endPos, std::vector<std::vector<MapData>>& arr, std::vector<Grid>& pathVec)
{
//------可以拓展的六个方向
const int directs[6][2] = {
{ 1,-1},
{-1, 1},
{ 1, 0},
{-1, 0},
{ 0, 1},
{ 0,-1}
};
MinHeap heap;
heap.add(new Data(startPos, 0, 0, nullptr));
bool finish = false;
Data* lastData = nullptr;// 记录最后一个点的数据,用来反推路径 若为空则无路径可到达
int max_l = 0;
while (!finish && !heap.isEmpty())
{
Data* data = heap.getMinAndRemove();// 取出f值最小的点
auto mapData = getMapDataByGrid(data->grid, arr);
if (mapData->isSpace)
{
mapData->visited = true;// 将取出的点标识为已访问点
}
max_l = arr[data->grid.x].size();
for (auto dir : directs)// 遍历六个方向的点
{
Grid pos = Grid(data->grid.x + dir[0], data->grid.y + dir[1]);
if (pos.x >= 0 && pos.x < arr.size()
&& pos.y<max_l)
{
auto fData = getMapDataByGrid(pos,arr);
if (!fData)
{
continue;
}
if (endPos==pos)// 如果是终点,则跳出循环,不用再找
{
finish = true;
lastData = data;
break;
}
if (!fData->isSpace|| fData->visited)// 如果不是空地,就不需要再扩展
{
continue;
}
auto nn = heap.find(pos);
if (nn)
{
if (nn->g > data->g + 1)
{
nn->g = data->g + 1;
nn->parent = data;
}
}
else
{
heap.add(new Data(pos, pos.distance(endPos), data->g + 1, data));
}
}
}
}
if (lastData)
{
// 反向找出路径
pathVec.clear();
auto mapData = getMapDataByGrid(lastData->grid, arr);
pathVec.push_back(mapData->grid);
while (lastData->parent)
{
lastData = lastData->parent;
mapData = getMapDataByGrid(lastData->grid, arr);
pathVec.push_back(mapData->grid);
}
}
else
{
std::cout << "no path" << std::endl;
}
heap.release();
}
根据目标点寻路:
bool HelloWorld::onTouchBegan(Touch* touch, Event* unused_event)
{
auto pos = touch->getLocation();
auto grid = this->getGridByPos(pos);
CCLOG("goal->%d,%d", (int)grid.x, (int)grid.y);
findPath(Vec2(rowGridNum/2,colGridNum/2), grid);
return true;
}
void HelloWorld::findPath(const Vec2& startPos, const Vec2& endPos)
{
for (auto &a: mapVec)
{
for (auto &b:a)
{
b.reset();
}
}
std::vector<Grid> pathVec;
AStar star;
Grid endGrid = Grid(endPos.x, endPos.y);
auto p = star.getMapDataByGrid(endGrid,mapVec);
endGrid.posX = p->grid.posX;
endGrid.posY = p->grid.posY;
star.findPath(Grid(startPos.x, startPos.y), endGrid, mapVec, pathVec);
m_node->removeAllChildren();
std::vector<Vec2> posVec;
posVec.push_back(Vec2(endGrid.posX, endGrid.posY));
for (auto aa: pathVec)
{
posVec.push_back(Vec2(aa.posX, aa.posY));
}
int num = posVec.size();
for (int i = 0; i < num-1; ++i)
{
auto pos = posVec[i];
DrawNode* drawNode = DrawNode::create();
//drawNode->setPosition(pos);
m_node->addChild(drawNode);
drawNode->drawLine(pos, posVec[i+1], Color4F::BLUE);
}
}
实现的效果:
不过有些寻路效果没有达到预期,比如第二个效果,下图标注的黄线或红线路径才更合适:
什么原因导致的呢?
检查代码之后发现【六个方向的点】这个数据有问题,奇数行和偶数行相邻网格的纵坐标偏移量不一样,可是调整逻辑之后寻路效果依然不理想。
细细探究,根本原因是视觉上看起来距离一样的相邻网格,根据坐标计算距离不一样,比如(6,2)到(5,2)和(6,2)到(5,3)。
那么如何实现视觉上看起来距离一样的相邻网格,根据坐标计算距离也一样呢?
解决方案:正六边形:寻路优化
备注:【根据网格信息计算出X,Y坐标,然后使用此坐标计算寻路中的f值,这样调整寻路效果依然不佳,感兴趣的伙伴可以试试这种效果】