正六边形:寻路

上一篇:正六边形:平铺

下一篇:正六边形:寻路优化

寻路使用的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值,这样调整寻路效果依然不佳,感兴趣的伙伴可以试试这种效果】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值