参考资料:https://www.redblobgames.com/grids/hexagons/
处理正六边形网格数据【纵坐标优化,引入Z坐标】:
const double sqrt3 = 1.732051;
const int sideLen = 50;
const int rowGridNum = 14;
const int colGridNum = 16;
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 - i;
tmpData.grid.z = -1 * (tmpData.grid.x + tmpData.grid.y);
auto sp = Sprite::create("six_side.png");
auto pos = this->getPosByGrid(i, j);
sp->setPosition(pos);
this->addChild(sp);
auto txt = ui::Text::create();
sprintf(buff, "%d,%d,%d", tmpData.grid.x, tmpData.grid.y, tmpData.grid.z);
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);
}
}
//根据网格信息计算坐标
Vec2 HelloWorld::getPosByGrid(const int& gridX, const int& gridY)
{
int y = gridY - gridX / 2;
Vec2 pos = Vec2::ZERO;
pos.x = gridX * sideLen * 1.5 + sideLen;
if (gridX % 2 == 0)
{
pos.y = sideLen * sqrt3 * y;
}
else
{
pos.y = sideLen * sqrt3 * (y - 0.5);
}
return pos;
}
//根据坐标计算网格信息
Vec2 HelloWorld::getGridByPos(const Vec2& pos)
{
int girdX = (pos.x- sideLen) / (1.5 * sideLen) + 0.5;
int girdY = 0;
if (girdX % 2 == 0)
{
girdY = pos.y / (sideLen * sqrt3) + 0.5;
}
else
{
girdY = pos.y / (sideLen * sqrt3)+1;
}
girdY -= (girdX+1)/2;
return Vec2(girdX, girdY);
}
AStar.h
#ifndef __AStar__
#define __AStar__
#include <algorithm>
#include <vector>
#include <string>
struct Grid
{
Grid() {}
Grid(int x, int y,int z) {
this->x = x;
this->y = y;
this->z = z;
}
int x = 0;
int y = 0;
int z = 0;
double posX = 0;
double posY = 0;
double distance(const Grid& grid) {
double dis = abs(grid.x - x) + abs(grid.y - y)+abs(grid.z-z);
//需要除以2
return dis/2;
}
bool operator==(const Grid& grid) const {
return this->x == grid.x && this->y == grid.y;
}
void operator+=(const Grid& grid) {
this->x += grid.x;
this->y += grid.y;
this->z += grid.z;
}
};
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, 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)
{
//------可以拓展的六个方向
std::vector<Grid> directs = {
Grid( 1,-1, 0),
Grid(-1, 1, 0),
Grid( 1, 0,-1),
Grid(-1, 0, 1),
Grid( 0, 1,-1),
Grid( 0,-1, 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 = mapData->grid;
pos += dir;
if (pos.x >= 0 && pos.x < arr.size()
&& pos.y < max_l)
{
auto c = getMapDataByGrid(pos,arr);
if (!c)
{
continue;
}
if (endPos==pos)// 如果是终点,则跳出循环,不用再找
{
finish = true;
lastData = data;
break;
}
if (!c->isSpace||c->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);
if (mapData)
{
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();
}
实现效果: