寻路算法源码
❶ 如何基于cocos2dx3.x实现A星寻路算法
在学习本篇教程之前,如果你有cocos2d-x的开发经验,将会有所帮助。如果没有也没关系,因为你可以将这里讲解的例子迁移到其他的语言或者框架中。
找到到达你键盘的最短路径,开始吧!
Maze猫
首先介绍下我们将要在本篇教程中开发的简单游戏。
前往下载本篇教程的 工程代码 。编译运行工程,你将看到以下画面。
在这款游戏中,你扮演着一只小偷猫,在一个由危险的狗守护着的地牢里小心穿行。如果你试图穿过一只狗,他会把你吃掉 – 除非你可以用骨头去贿赂它!
所以在这款游戏中,你的任务是尝试以正确的顺序捡起骨头,然后 寻找路线 穿过狗逃离。
注意到猫只能水平或者垂直的移动(例如不能斜线移动),并且会从一个方块的中心点移动到另一个中心点。每个方块既可以是可通行的也可以是不可通行的。
尝试下这款游戏,看看你能否找到出路!建议你阅读代码以熟悉它的原理。这是一款相当普通的方块-地图式游戏,我们会在接下来的教程中修改它并使用上A星寻路算法。
Maze猫和A星概览
正如你所看到的,当你点击地图某处时,猫会沿着你点击的方向跳到相邻的方块上。
我们想对程序做修改,让猫持续的往你点击的方块方向前进,就像许多RPGs或者point-and-click冒险类游戏。
让我们看下控制触摸事件代码的工作原理。如果你打开HelloWorldScene.cpp文件,你将看到像下面这样去实现触摸操作:
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches( true );
listener->onTouchBegan = [ this ](Touch *touch, Event *event){
if (_gameOver)
{
return false ;
}
Point touchLocation = _tileMap->convertTouchToNodeSpace(touch);
_cat->moveToward(touchLocation);
return true ;
};
_eventDispatcher->(listener, this );
你可以看到这里只是对猫精灵调用了一个方法,让猫在方块地图上往你点击的地方移动。
我们现在要做的是修改在CatSprite.m文件中的以下方法,寻找到达该点的最短路径,并且开始前进:
void CatSprite::moveToward( const Point &target)
{
}
创建ShortestPathStep类
我们开始创建一个内部类,代表路径上的一步操作。在这种情况下,它是一个方块和由A星算法计算出来的的F,G和H scores。
class ShortestPathStep : public cocos2d::Object
{
public :
ShortestPathStep();
~ShortestPathStep();
static ShortestPathStep *createWithPosition( const cocos2d::Point &pos);
bool initWithPosition( const cocos2d::Point &pos);
int getFScore() const ;
bool isEqual( const ShortestPathStep *other) const ;
std::string getDescription() const ;
CC_SYNTHESIZE(cocos2d::Point, _position, Position);
CC_SYNTHESIZE( int , _gScore, GScore);
CC_SYNTHESIZE( int , _hScore, HScore);
CC_SYNTHESIZE(ShortestPathStep*, _parent, Parent);
};
现在添加以下代码到CatSprite.cpp文件的顶部。
CatSprite::ShortestPathStep::ShortestPathStep() :
_position(Point::ZERO),
_gScore(0),
_hScore(0),
_parent(nullptr)
{
}
CatSprite::ShortestPathStep::~ShortestPathStep()
{
}
CatSprite::ShortestPathStep *CatSprite::ShortestPathStep::createWithPosition( const Point &pos)
{
ShortestPathStep *pRet = new ShortestPathStep();
if (pRet && pRet->initWithPosition(pos))
{
pRet->autorelease();
return pRet;
}
else
{
CC_SAFE_DELETE(pRet);
return nullptr;
}
}
bool CatSprite::ShortestPathStep::initWithPosition( const Point &pos)
{
bool bRet = false ;
do
{
this ->setPosition(pos);
bRet = true ;
} while (0);
return bRet;
}
int CatSprite::ShortestPathStep::getFScore() const
{
return this ->getGScore() + this ->getHScore();
}
bool CatSprite::ShortestPathStep::isEqual( const CatSprite::ShortestPathStep *other) const
{
return this ->getPosition() == other->getPosition();
}
std::string CatSprite::ShortestPathStep::getDescription() const
{
return StringUtils::format( "pos=[%.0f;%.0f] g=%d h=%d f=%d" ,
this ->getPosition().x, this ->getPosition().y,
this ->getGScore(), this ->getHScore(), this ->getFScore());
}
正如所见,这是一个很简单的类,记录了以下内容:
- 方块的坐标
- G值(记住,这是开始点到当前点的方块数量)
- H值(记住,这是当前点到目标点的方块估算数量)
- Parent是它的上一步操作
- F值,这是方块的和值(它是G+H的值)
这里定义了getDescription方法,以方便调试。创建了isEquals方法,当且仅当两个ShortestPathSteps的方块坐标相同时,它们相等(例如它们代表着相同的方块)。
创建Open和Closed列表
打开CatSprite.h文件,添加如下代码:
cocos2d::Vector _spOpenSteps;
cocos2d::Vector _spClosedSteps;
检查开始和结束点
重新实现moveToward方法,获取当前方块坐标和目标方块坐标,然后检查是否需要计算一条路径,最后测试目标方块坐标是否可行走的(在这里只有墙壁是不可行走的)。打开CatSprite.cpp文件,修改moveToward方法,为如下:
void CatSprite::moveToward( const Point &target)
{
Point fromTileCoord = _layer->tileCoordForPosition( this ->getPosition());
Point toTileCoord = _layer->tileCoordForPosition(target);
if (fromTileCoord == toTileCoord)
{
CCLOG( "You're already there! :P" );
return ;
}
if (!_layer->isValidTileCoord(toTileCoord) || _layer->isWallAtTileCoord(toTileCoord))
{
SimpleAudioEngine::getInstance()->playEffect( "hitWall.wav" );
return ;
}
CCLOG( "From: %f, %f" , fromTileCoord.x, fromTileCoord.y);
CCLOG( "To: %f, %f" , toTileCoord.x, toTileCoord.y);
}
编译运行,在地图上进行点击,如果不是点击到墙壁的话,可以在控制台看到如下信息:
From: 24.000000, 0.000000
To: 20.000000, 0.000000
其中 **From** 就是猫的方块坐标,**To**就是所点击的方块坐标。
实现A星算法
根据算法,第一步是添加当前坐标到open列表。还需要三个辅助方法:
- 一个方法用来插入一个ShortestPathStep对象到适当的位置(有序的F值)
- 一个方法用来计算从一个方块到相邻方块的移动数值
- 一个方法是根据"曼哈顿距离"算法,计算方块的H值
打开CatSprite.cpp文件,添加如下方法:
void CatSprite::insertInOpenSteps(CatSprite::ShortestPathStep *step)
{
int stepFScore = step->getFScore();
ssize_t count = _spOpenSteps.size();
ssize_t i = 0;
for (; i < count; ++i)
{
if (stepFScore <= _spOpenSteps.at(i)->getFScore())
{
break ;
}
}
_spOpenSteps.insert(i, step);
}
int CatSprite::computeHScoreFromCoordToCoord( const Point &fromCoord, const Point &toCoord)
{
// 忽略了可能在路上的各种障碍
return abs(toCoord.x - fromCoord.x) + abs(toCoord.y - fromCoord.y);
}
int CatSprite::( const ShortestPathStep *fromStep, const ShortestPathStep *toStep)
{
// 因为不能斜着走,而且由于地形就是可行走和不可行走的成本都是一样的
// 如果能够对角移动,或者有沼泽、山丘等等,那么它必须是不同的
return 1;
}
接下来,需要一个方法去获取给定方块的所有相邻可行走方块。因为在这个游戏中,HelloWorld管理着地图,所以在那里添加方法。打开HelloWorldScene.cpp文件,添加如下方法:
PointArray *HelloWorld::( const Point &tileCoord) const
{
PointArray *tmp = PointArray::create(4);
// 上
Point p(tileCoord.x, tileCoord.y - 1);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 左
p.setPoint(tileCoord.x - 1, tileCoord.y);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 下
p.setPoint(tileCoord.x, tileCoord.y + 1);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
// 右
p.setPoint(tileCoord.x + 1, tileCoord.y);
if ( this ->isValidTileCoord(p) && ! this ->isWallAtTileCoord(p))
{
tmp->addControlPoint(p);
}
return tmp;
}
可以继续CatSprite.cpp中的moveToward方法了,在moveToward方法的后面,添加如下代码:
bool pathFound = false ;
_spOpenSteps.clear();
_spClosedSteps.clear();
// 首先,添加猫的方块坐标到open列表
this ->insertInOpenSteps(ShortestPathStep::createWithPosition(fromTileCoord));
do
{
// 得到最小的F值步骤
// 因为是有序列表,第一个步骤总是最小的F值
ShortestPathStep *currentStep = _spOpenSteps.at(0);
// 添加当前步骤到closed列表
_spClosedSteps.pushBack(currentStep);
// 将它从open列表里面移除
// 需要注意的是,如果想要先从open列表里面移除,应小心对象的内存
_spOpenSteps.erase(0);
// 如果当前步骤是目标方块坐标,那么就完成了
if (currentStep->getPosition() == toTileCoord)
{
pathFound = true ;
ShortestPathStep *tmpStep = currentStep;
CCLOG( "PATH FOUND :" );
do
{
CCLOG( "%s" , tmpStep->getDescription().c_str());
tmpStep = tmpStep->getParent(); // 倒退
} while (tmpStep); // 直到没有上一步
_spOpenSteps.clear();
_spClosedSteps.clear();
break ;
}
// 得到当前步骤的相邻方块坐标
PointArray *adjSteps = _layer->(currentStep->getPosition());
for (ssize_t i = 0; i < adjSteps->count(); ++i)
{
ShortestPathStep *step = ShortestPathStep::createWithPosition(adjSteps->getControlPointAtIndex(i));
// 检查步骤是不是已经在closed列表
if ( this ->getStepIndex(_spClosedSteps, step) != -1)
{
continue ;
}
// 计算从当前步骤到此步骤的成本
int moveCost = this ->(currentStep, step);
// 检查此步骤是否已经在open列表
ssize_t index = this ->getStepIndex(_spOpenSteps, step);
// 不在open列表,添加它
if (index == -1)
{
// 设置当前步骤作为上一步操作
step->setParent(currentStep);
// G值等同于上一步的G值 + 从上一步到这里的成本
step->setGScore(currentStep->getGScore() + moveCost);
// H值即是从此步骤到目标方块坐标的移动量估算值
step->setHScore( this ->computeHScoreFromCoordToCoord(step->getPosition(), toTileCoord));
// 按序添加到open列表
this ->insertInOpenSteps(step);
}
else
{
// 获取旧的步骤,其值已经计算过
step = _spOpenSteps.at(index);
// 检查G值是否低于当前步骤到此步骤的值
if ((currentStep->getGScore() + moveCost) < step->getGScore())
{
// G值等同于上一步的G值 + 从上一步到这里的成本
step->setGScore(currentStep->getGScore() + moveCost);
// 因为G值改变了,F值也会跟着改变
// 所以为了保持open列表有序,需要将此步骤移除,再重新按序插入
// 在移除之前,需要先保持引用
step->retain();
// 现在可以放心移除,不用担心被释放
_spOpenSteps.erase(index);
// 重新按序插入
this ->insertInOpenSteps(step);
// 现在可以释放它了,因为open列表应该持有它
step->release();
}
}
}
} while (_spOpenSteps.size() > 0);
if (!pathFound)
{
SimpleAudioEngine::getInstance()->playEffect( "hitWall.wav" );
}
添加以下方法:
ssize_t CatSprite::getStepIndex( const cocos2d::Vector &steps, const CatSprite::ShortestPathStep *step)
{
for (ssize_t i = 0; i < steps.size(); ++i)
{
if (steps.at(i)->isEqual(step))
{
return i;
}
}
return -1;
}
编译运行,在地图上进行点击,如下图所示:
From: 24.000000, 0.000000
To: 23.000000, 3.000000
PATH FOUND :
pos=[23;3] g=10 h=0 f=10
pos=[22;3] g=9 h=1 f=10
pos=[21;3] g=8 h=2 f=10
pos=[20;3] g=7 h=3 f=10
pos=[20;2] g=6 h=4 f=10
pos=[20;1] g=5 h=5 f=10
pos=[21;1] g=4 h=4 f=8
pos=[22;1] g=3 h=3 f=6
pos=[23;1] g=2 h=2 f=4
pos=[24;1] g=1 h=3 f=4
pos=[24;0] g=0 h=0 f=0
注意该路径是从后面建立的,所以必须从下往上看猫选择了哪条路径。
跟随路径前进
现在已经找到了路径,只需让猫跟随前进即可。需要创建一个数组去存储路径,打开CatSprite.h文件,添加如下代码:
cocos2d::Vector _shortestPath;
打开CatSprite.cpp文件,更改moveToward方法,注释掉语句**bool pathFound = false**;,如下:
//bool pathFound = false;
替换语句**pathFound = true;**为如下:
//pathFound = true;
this ->(currentStep);
并且注释掉下方的调试语句:
//ShortestPathStep *tmpStep = currentStep;
//CCLOG("PATH FOUND :");
//do
//{
// CCLOG("%s", tmpStep->getDescription().c_str());
// tmpStep = tmpStep->getParent(); // 倒退
/
❷ 手机游戏开发中怎么实现怪物自动寻路(希望给段代码)
我有,但是有谁会无偿把这些资源给你。
我觉得我办不到。
❸ vc环境 最短路径算法
单源最短路径算法---Dijkstra算法
转自:http://space.flash8.net/space/html/07/14107_itemid_400760.html
算法介绍
Dijkstra算法是由荷兰计算机科学家艾兹格·迪科斯彻发现的。算法解决的是有向图中最短路径问题。
举例来说,如果图中的顶点表示城市,而边上的权重表示着城市间开车行经的距离。 Dijkstra算法可以用来找到两个城市之间的最短路径。
Dijkstra 算法的输入包含了一个有权重的有向图G,以及G中的一个来源顶点S。我们以V表示G中所有顶点的集合。每一个图中的边,都是两个顶点所形成的有序元素对。 (u,v)表示从顶点u到v有路径相连。我们以E所有边的集合,而边的权重则由权重函数w: E → [0, ∞]定义。因此,w(u,v)就是从顶点u到顶点v的非负花费值(cost)。边的花费可以想象成两个顶点之间的距离。任两点间路径的花费值,就是该路径 上所有边的花费值总和。已知有V中有顶点s及t,Dijkstra算法可以找到s到t的最低花费路径(i.e. 最短路径)。这个算法也可以在一个图中,找到从一个顶点s到任何其他顶点的最短路径。
算法描述
这个算法是通过为每个顶点v保留目前为止所找到的从s到v的最短路径来工作的。 初始时,源点s的路径长度值被赋为0(d[s]=0),同时把所有其他顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于V中所有 顶点v除s外d[v]= ∞)。当算法结束时,d[v]中储存的便是从s到v的最短路径,或者如果路径不存在的话是无穷大。 Dijstra算法的基础操作是边的拓展:如果存在一条从u到v的边,那么从s到u的最短路径可以通过将边(u,v)添加到尾部来拓展一条从s到v的路 径。这条路径的长度是d[u]+w(u,v)。如果这个值比目前已知的d[v]的值要小,我们可以用新值来替代当前d[v]中的值。拓展边的操作一直执行 到所有的d[v]都代表从s到v最短路径的花费。这个算法经过组织因而当d[u]达到它最终的值的时候没条边(u,v)都只被拓展一次。
算法维护两个顶点集S和Q。集合S保留了我们已知的所有d[v]的值已经是最短路径的值顶点,而集合Q则保留其他所有顶点。集合S初始状态为空,而后每一步 都有一个顶点从Q移动到S。这个被选择的顶点是Q中拥有最小的d[u]值的顶点。当一个顶点u从Q中转移到了S中,算法对每条外接边(u,v)进行拓展。
伪码
在下面的算法中,u:=Extract_Min(Q)在在顶点集Q中搜索有最小的d[u]值的顶点u。这个顶点被从集合Q中删除并返回给用户。
function Dijkstra(G, w, s)
// 初始化
for each vertex v in V[G] {
d[v] := infinity
previous[v] := undefined
d[s] := 0
}
S := empty set
Q := set of all vertices
while Q is not an empty set { // Dijstra算法主体
u := Extract_Min(Q)
S := S union {u}
for each edge (u,v) outgoing from u
if d[v] > d[u] + w(u,v) // 拓展边(u,v)
d[v] := d[u] + w(u,v)
previous[v] := u
}
如果我们只对在s和t之间寻找一条最短路径的话,我们可以在第9行添加条件如果满足u=t的话终止程序。
现在我们可以通过迭代来回溯出s到t的最短路径
1 S := empty sequence
2 u := t
3 while defined u
4 insert u to the beginning of S
5 u := previous[u]
现在序列S就是从s到t的最短路径的顶点集.
时间复杂度
我们可以用大O符号将Dijkstra算法的运行时间表示为边数m和顶点数n的函数。
Dijkstra算法最简单的实现方法是用一个链表或者数组来存储所有顶点的集合Q,所以搜索Q中最小元素的运算(Extract-Min(Q))只需要线性搜索Q中的所有元素。这样的话算法的运行时间是O(n2)。
对 于边数少于n2稀疏图来说,我们可以用邻接表来更有效的实现Dijkstra算法。同时需要将一个二叉堆或者斐波纳契堆用作优先队列来寻找最小的顶点 (Extract-Min)。当用到二叉堆的时候,算法所需的时间为O((m+n)log n),斐波纳契堆能稍微提高一些性能,让算法运行时间达到O(m + n log n)。
相关问题和算法
在Dijkstra 算法的基础上作一些改动,可以扩展其功能。例如,有时希望在求得最短路径的基础上再列出一些次短的路径。为此,可先在原图上计算出最短路径,然后从图中删 去该路径中的某一条边,在余下的子图中重新计算最短路径。对于原最短路径中的每一条边,均可求得一条删去该边后子图的最短路径,这些路径经排序后即为原图 的一系列次短路径。
OSPF(open shortest path first, 开放最短路径优先)算法是Dijkstra算法在网络路由中的一个具体实现。
与Dijkstra算法不同,Bellman-Ford算法可用于具有负花费边的图,只要图中不存在总花费为负值且从源点 s 可达的环路(如果有这样的环路,则最短路径不存在,因为沿环路循环多次即可无限制的降低总花费)。
与最短路径问题有关的一个问题是旅行商问题(traveling salesman problem),它要求找出通过所有顶点恰好一次且最终回到源点的最短路径。该问题是NP难的;换言之,与最短路径问题不同,旅行商问题不太可能具有多项式时间算法。
如果有已知信息可用来估计某一点到目标点的距离,则可改用A*算法 ,以减小最短路径的搜索范围。
另外,用于解决最短路径问题的算法被称做“最短路径算法”, 有时被简称作“路径算法”。 最常用的路径算法有:
Dijkstra算法
A*算法
SPFA算法
Bellman-Ford算法
Floyd-Warshall算法
Johnson算法
所谓单源最短路径问题是指:已知图G=(V,E),我们希望找出从某给定的源结点S∈V到V中的每个结点的最短路径。
首先,我们可以发现有这样一个事实:如果P是G中从vs到vj的最短路,vi是P中的一个点,那么,从vs沿P到vi的路是从vs到vi的最短路。
❹ 游戏中的常用的寻路算法有哪些
f(n)=g(n)+h(n) 从起始点到目的点的最佳评估值
– 每次都选择f(n)值最小的结点作为下一个结点,
直到最终达到目的结点
– A*算法的成功很大程度依赖于h(n)函数的构建
?;) = g(n? 在各种游戏中广泛应用 Open列表和Closed列表
– Open列表
A*算法
? h(n) = 从结点n到目的结点的耗费评估值,启发函数
?,程序返回n
else 生成结点n的每一个后继结点n;
foreach 结点n的后继结点n;{
将n’的父结点设置为n
计算启发式评估函数h(n‘)值,评估从n‘到node_goal的费用
计算g(n‘) = g(n) + 从n’到n的开销
计算f(n?? 在算法启动时,Closed列表为空 A* 算法伪代码初始化OPEN列表
初始化CLOSED列表
创建目的结点;称为node_goal
创建起始结点;称为node_start
将node_start添加到OPEN列表
while OPEN列表非空{
从OPEN列表中取出f(n)值最低的结点n
将结点n添加到CLOSED列表中
if 结点n与node_goal相等then 我们找到了路径;)
if n‘位于OPEN或者CLOSED列表and 现有f(n)较优then丢弃n’ ;) + h(n?? 包含我们还没有处理到的结点
? g(n) = 从初始结点到结点n的耗费
?? 包含我们已经处理过的结点
,处理后继n’
将结点n‘从OPEN和CLOSED中删除
添加结点n‘到OPEN列表
}
}
return failure (我们已经搜索了所有的结点?? 启发式搜索
– 在搜索中涉及到三个函数
??? 我们最开始将起始结点放入到Open列表中
– Closed列表
?
❺ RMXP寻路算法
#==============================================================================
# ■ Find_Path
#------------------------------------------------------------------------------
# 寻路算法--完整鼠标系统(四方向)专用版
# By whbm
#==============================================================================
class Find_Path
#--------------------------------------------------------------------------
def initialize #初始化
@open_list = []
@close_lise = []
@path = []
end #结束初始化
#--------------------------------------------------------------------------
def fp_passable?(x, y, d, tr_x = -2, tr_y = -2) #开始判定通行
return false if (tr_x == @unable_xa or
tr_x == @unable_xb or
tr_y == @unable_ya or
tr_y == @unable_yb)
if $game_player.passable?(x, y, d)
return true
else
return false
end
end #结束判定通行
#--------------------------------------------------------------------------
def get_g(now_point) #开始计算G值
d = now_point[2]
return 0 if d == 5
father_point = get_father_point(now_point)
g = father_point[3] + 10
return g
end #结束计算G值
#--------------------------------------------------------------------------
def get_h(now_point) #开始计算H值
now_x = now_point[0]
now_y = now_point[1]
#print @trg_x,now_x,@trg_y,now_y
h = (@trg_x - now_x).abs + (@trg_y - now_y).abs
return h * 10
end #结束计算H值
#--------------------------------------------------------------------------
def get_f(now_point) #开始计算F值
f = now_point[3] + now_point[4]
return f
end #结束计算F值
#--------------------------------------------------------------------------
def get_point(x, y) #取已知坐标点
if @open_list.size != 0
@open_list.each do |point|
if point[0] == x and point[1] == y
return point
break
end
end
end
if @close_list.size != 0
@close_list.each do |point|
if point[0] == x and point[1] == y
return point
break
end
end
end
end #结束取已知坐标点
#--------------------------------------------------------------------------
def get_father_point(now_point) #取已知点的父节点
d = now_point[2]
return now_point if d == 5
x = now_point[0] + (d == 6 ? 1 : (d == 4 ? -1 : 0))
y = now_point[1] + (d == 2 ? 1 : (d == 8 ? -1 : 0))
return get_point(x, y)
end #结束取已知点的父节点
#--------------------------------------------------------------------------
def new_point(x, y, d) #开始建立新节点
#print x,y,d
point = [x, y, d]
point.push get_g(point)
point.push get_h(point)
point.push get_f(point)
return point
end #结束建立新节点
#--------------------------------------------------------------------------
def get_direction(self_x, self_y, trg_x, trg_y)
if trg_x > self_x
if trg_y - self_y > - ( trg_x - self_x ) and
trg_y - self_y < ( trg_x - self_x )
return 6
end
if trg_y - self_y > ( trg_x - self_x )
return 2
end
if trg_y - self_y < - ( trg_x - self_x )
return 8
end
end
if trg_x < self_x
if trg_y - self_y > - ( self_x - trg_x ) and
trg_y - self_y < ( self_x - trg_x )
return 4
end
if trg_y - self_y > ( self_x - trg_x )
return 2
end
if trg_y - self_y < - ( self_x - trg_x )
return 8
end
end
end
#--------------------------------------------------------------------------
def get_d_x_y(x, y, d)
d_x = x + (d == 6 ? 1 : (d == 4 ? -1 : 0))
d_y = y + (d == 2 ? 1 : (d == 8 ? -1 : 0))
return d_x, d_y
end
#--------------------------------------------------------------------------
def find_short_path_other(self_x, self_y, trg_x, trg_y,
real_self_x, real_self_y, real_trg_x, real_trg_y)
@self_x = self_x
@self_y = self_y
@now_x = self_x
@now_y = self_y
@trg_x = trg_x
@trg_y = trg_y
@path = []
direction = get_direction(real_self_x, real_self_y, real_trg_x, real_trg_y)
@now_trg_x, @now_trg_y = get_d_x_y(@self_x, @self_y, direction)
while fp_passable?(@now_x, @now_y, direction)
@path.push direction
@now_x = @now_trg_x
@now_y = @now_trg_y
@now_trg_x, @now_trg_y = get_d_x_y(@now_x, @now_y, direction)
end
return @path
end
#--------------------------------------------------------------------------
def find_short_path(self_x, self_y, trg_x, trg_y,
real_self_x, real_self_y, real_trg_x, real_trg_y) #开始搜索路径
return find_short_path_other(self_x, self_y, trg_x, trg_y,
real_self_x, real_self_y, real_trg_x, real_trg_y) if not
(fp_passable?(trg_x, trg_y + 1, 8) or
fp_passable?(trg_x + 1, trg_y, 4) or
fp_passable?(trg_x - 1, trg_y, 6) or
fp_passable?(trg_x, trg_y - 1, 2)) and @goal_type != 1
#根据屏幕限定搜索面积..加速
@unable_xa = $game_map.display_x / 128 - 1
@unable_ya = $game_map.display_y / 128 - 1
@unable_xb = $game_map.display_x / 128 + 20
@unable_yb = $game_map.display_y / 128 + 20
@self_x = self_x
@self_y = self_y
@now_x = self_x
@now_y = self_y
@trg_x = trg_x
@trg_y = trg_y
@open_list = []
@close_list = []
#准备搜索
#print @self_x,@self_y
@now_point = new_point(@self_x, @self_y, 5) #令起始点为当前点
@open_list.push @now_point #将当前点加入关闭列表
#开始搜索
begin
loop do
check_trg = check_around_point(@now_point)
if check_trg == true
@path = get_path
break
end
@now_point = get_lowest_f_point
if @now_point == [] or @now_point == nil
@path = []
break
end
end
rescue Hangup
retry
end
return @path
end #结束搜索路径
#--------------------------------------------------------------------------
def find_player_short_path(trg_x, trg_y,
real_trg_x, real_trg_y) #寻找角色的最短路径
self_x = $game_player.x
self_y = $game_player.y
real_self_x = $game_player.screen_x
real_self_y = $game_player.screen_y
@goal_type, event = $game_map.check_event_custom_exist(real_trg_x, real_trg_y)
if @goal_type == 1
trg_x = event.x
trg_y = event.y
end
return find_short_path(self_x, self_y, trg_x, trg_y,
real_self_x, real_self_y, real_trg_x, real_trg_y)
end #结束角色的寻找路径
#--------------------------------------------------------------------------
def get_path #取得最终的路径
path = []
now_point = @open_list[@open_list.size - 1]
path.push(10 - now_point[2])
last_point = now_point
loop do
now_point = get_father_point(now_point)
break if now_point[2] == 5
path.push(10 - now_point[2])
end
return path.reverse
end #结束取得最终的路径
#--------------------------------------------------------------------------
def get_lowest_f_point #开始取得最低F值的点
if @open_list == []
return []
end
last_lowest_f_point = @open_list[0]
@open_list.each do |point|
last_lowest_f_point = point if point[5] < last_lowest_f_point[5]
end
return last_lowest_f_point
end #结束取得最低F值点
#--------------------------------------------------------------------------
def check_around_point(point) #开始检查已知点的八方节点
for d in [2, 4, 6, 8]
x = point[0] + (d == 6 ? 1 : (d == 4 ? -1 : 0))
y = point[1] + (d == 2 ? 1 : (d == 8 ? -1 : 0))
if in_close_list?(x, y) #在关闭列表中
next
elsif in_open_list?(x, y) #在开启列表中
get_new_g_point = new_point(x, y, 10 - d)
get_last_g_point = get_point(x, y)
if get_new_g_point[3] >= get_last_g_point[3]
next
else
#如果改变父节点是新G值更小则确定改变
@open_list[@open_list.index(get_last_g_point)] = get_new_g_point
end
else
if fp_passable?(point[0], point[1], d, x, y)
# 如果不在开启列表中、且不在关闭列表中、且通行则添加它到新八周节点
@open_list.push new_point(x, y, 10 - d)
#如果将目标点添加到了开启列表中就返回true
return true if x == @trg_x and y == @trg_y
return true if @goal_type == 1 and ([1, -1].include?(x - @trg_x) and y - @trg_y == 0) or ([1, -1].include?(y - @trg_y) and x - @trg_x == 0)
end
end
end
#此刻没有找到目标点并将当前点加入关闭列表并在开启列表中删除
@close_list.push point
@open_list.delete(point)
#此刻没找到目标点并返回false
return false
end #结束计算已知点的八方节点
#--------------------------------------------------------------------------
def in_open_list?(x, y) #开始检查谋点是否在开启列表中
@open_list.each do |point|
return true if point[0] == x and point[1] == y
end
return false
end #结束检查谋点是否在开启列表中
#--------------------------------------------------------------------------
def in_close_list?(x, y) #开始检查谋点是否在关闭列表中
@close_list.each do |point|
return true if point[0] == x and point[1] == y
end
return false
end #结束检查谋点是否在关闭列表中
#--------------------------------------------------------------------------
end
❻ 星际争霸2的寻路算法思路是怎样的
首先地图整体开始前,会用多层可达矩阵算法,算出路径关键点
2,创建关键节点可达矩阵
3,再每个兵当前位置对关键节点进行路径计算
这样可以最小化资源占用就可以完成路径计算了,高数的离散数学,挺容易解的
❼ 制作一个A*算法的寻路插件,或者用其他算法也可以。 但是效率必须高。
1000*1000可以直接广搜啊。效率应该可以的
❽ 从原点出发,遍历50个点,再回到原点的最短路径,求matlab程序
据 Drew 所知最短路经算法现在重要的应用有计算机网络路由算法,机器人探路,交通路线导航,人工智能,游戏设计等等。美国火星探测器核心的寻路算法就是采用的D*(D Star)算法。
最短路经计算分静态最短路计算和动态最短路计算。
静态路径最短路径算法是外界环境不变,计算最短路径。主要有Dijkstra算法,A*(A Star)算法。
动态路径最短路是外界环境不断发生变化,即不能计算预测的情况下计算最短路。如在游戏中敌人或障碍物不断移动的情况下。典型的有D*算法。这是Drew程序实现的10000个节点的随机路网三条互不相交最短路真实路网计算K条路径示例:节点5696到节点3006,三条最快速路,可以看出路径基本上走环线或主干路。黑线为第一条,兰线为第二条,红线为第三条。约束条件系数为1.2。共享部分路段。 显示计算部分完全由Drew自己开发的程序完成。 参见 K条路算法测试程序
Dijkstra算法求最短路径:
Dijkstra算法是典型最短路算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。
Dijkstra算法是很有代表性的最短路算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。
Dijkstra一般的表述通常有两种方式,一种用永久和临时标号方式,一种是用OPEN, CLOSE表方式,Drew为了和下面要介绍的 A* 算法和 D* 算法表述一致,这里均采用OPEN,CLOSE表的方式。
大概过程:
创建两个表,OPEN, CLOSE。
OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
1. 访问路网中里起始点最近且没有被检查过的点,把这个点放入OPEN组中等待检查。
2. 从OPEN表中找出距起始点最近的点,找出这个点的所有子节点,把这个点放到CLOSE表中。
3. 遍历考察这个点的子节点。求出这些子节点距起始点的距离值,放子节点到OPEN表中。
4. 重复2,3,步。直到OPEN表为空,或找到目标点。
这是在drew 程序中4000个节点的随机路网上Dijkstra算法搜索最短路的演示,黑色圆圈表示经过遍历计算过的点由图中可以看到Dijkstra算法从起始点开始向周围层层计算扩展,在计算大量节点后,到达目标点。所以速度慢效率低。
提高Dijkstra搜索速度的方法很多,据Drew所知,常用的有数据结构采用Binary heap的方法,和用Dijkstra从起始点和终点同时搜索的方法。
推荐网页:http://www.cs.ecnu.e.cn/assist/js04/ZJS045/ZJS04505/zjs045050a.htm
简明扼要介绍Dijkstra算法,有图解显示和源码下载。
A*(A Star)算法:启发式(heuristic)算法
A*(A-Star)算法是一种静态路网中求解最短路最有效的方法。
公式表示为: f(n)=g(n)+h(n),
其中f(n) 是节点n从初始点到目标点的估价函数,
g(n) 是在状态空间中从初始节点到n节点的实际代价,
h(n)是从n到目标节点最佳路径的估计代价。
保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:
估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。
如果 估价值>实际值, 搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
估价值与实际值越接近,估价函数取得就越好。
例如对于几何路网来说,可以取两节点间欧几理德距离(直线距离)做为估价值,即f=g(n)+sqrt((dx-nx)*(dx-nx)+(dy-ny)*(dy-ny));这样估价函数f在g值一定的情况下,会或多或少的受估价值h的制约,节点距目标点近,h值小,f值相对就小,能保证最短路的搜索向终点的方向进行。明显优于Dijstra算法的毫无无方向的向四周搜索。
conditions of heuristic
Optimistic (must be less than or equal to the real cost)
As close to the real cost as possible
主要搜索过程:
创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
遍历当前节点的各个节点,将n节点放入CLOSE中,取n节点的子节点X,->算X的估价值->
While(OPEN!=NULL)
{
从OPEN表中取估价值f最小的节点n;
if(n节点==目标节点) break;
else
{
if(X in OPEN) 比较两个X的估价值f //注意是同一个节点的两个不同路径的估价值
if( X的估价值小于OPEN表的估价值 )
更新OPEN表中的估价值; //取最小路径的估价值
if(X in CLOSE) 比较两个X的估价值 //注意是同一个节点的两个不同路径的估价值
if( X的估价值小于CLOSE表的估价值 )
更新CLOSE表中的估价值; 把X节点放入OPEN //取最小路径的估价值
if(X not in both)
求X的估价值;
并将X插入OPEN表中;//还没有排序
}
将n节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序; //实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。
}
❾ 求一个战棋游戏的六边形地图寻路算法(AS3实现)
楼上的都讲完了,我也没啥好说的,拿走两分好了
❿ 如何基于cocos2dx3.x实现A星寻路算法
在学习本篇教程之前,如果你有cocos2d-x的开发经验,将会有所帮助。如果没有也没关系,因为你可以将这里讲解的例子迁移到其他的语言或者框架中。找到到达你键盘的最短路径,开始吧!Maze猫首先介绍下我们将要在本篇教程中开发的简单游戏。前往下载本篇教程的工程代码。编译运行工程,你将看到以下画面。在这款游戏中,你扮演着一只小偷猫,在一个由危险的狗守护着的地牢里小心穿行。如果你试图穿过一只狗,他会把你吃掉–除非你可以用骨头去贿赂它!所以在这款游戏中,你的任务是尝试以正确的顺序捡起骨头,然后寻找路线穿过狗逃离。注意到猫只能水平或者垂直的移动(例如不能斜线移动),并且会从一个方块的中心点移动到另一个中心点。每个方块既可以是可通行的也可以是不可通行的。尝试下这款游戏,看看你能否找到出路!建议你阅读代码以熟悉它的原理。这是一款相当普通的方块-地图式游戏,我们会在接下来的教程中修改它并使用上A星寻路算法。Maze猫和A星概览正如你所看到的,当你点击地图某处时,猫会沿着你点击的方向跳到相邻的方块上。我们想对程序做修改,让猫持续的往你点击的方块方向前进,就像许多RPGs或者point-and-click冒险类游戏。让我们看下控制触摸事件代码的工作原理。如果你打开HelloWorldScene.cpp文件,你将看到像下面这样去实现触摸操作:autolistener=EventListenerTouchOneByOne::create();listener->setSwallowTouches(true);listener->onTouchBegan=[this](Touch*touch,Event*event){if(_gameOver){returnfalse;}PointtouchLocation=_tileMap->convertTouchToNodeSpace(touch);_cat->moveToward(touchLocation);returntrue;};_eventDispatcher->(listener,this);你可以看到这里只是对猫精灵调用了一个方法,让猫在方块地图上往你点击的地方移动。我们现在要做的是修改在CatSprite.m文件中的以下方法,寻找到达该点的最短路径,并且开始前进:voidCatSprite::moveToward(constPoint&target){}创建ShortestPathStep类我们开始创建一个内部类,代表路径上的一步操作。在这种情况下,它是一个方块和由A星算法计算出来的的F,G和Hscores。classShortestPathStep:publiccocos2d::Object{public:ShortestPathStep();~ShortestPathStep();staticShortestPathStep*createWithPosition(constcocos2d::Point&pos);boolinitWithPosition(constcocos2d::Point&pos);intgetFScore()const;boolisEqual(constShortestPathStep*other)const;std::stringgetDescription()const;CC_SYNTHESIZE(cocos2d::Point,_position,Position);CC_SYNTHESIZE(int,_gScore,GScore);CC_SYNTHESIZE(int,_hScore,HScore);CC_SYNTHESIZE(ShortestPathStep*,_parent,Parent);};现在添加以下代码到CatSprite.cpp文件的顶部。CatSprite::ShortestPathStep::ShortestPathStep():_position(Point::ZERO),_gScore(0),_hScore(0),_parent(nullptr){}CatSprite::ShortestPathStep::~ShortestPathStep(){}CatSprite::ShortestPathStep*CatSprite::ShortestPathStep::createWithPosition(constPoint&pos){ShortestPathStep*pRet=newShortestPathStep();if(pRet&&pRet->initWithPosition(pos)){pRet->autorelease();returnpRet;}else{CC_SAFE_DELETE(pRet);returnnullptr;}}boolCatSprite::ShortestPathStep::initWithPosition(constPoint&pos){boolbRet=false;do{this->setPosition(pos);bRet=true;}while(0);returnbRet;}intCatSprite::ShortestPathStep::getFScore()const{returnthis->getGScore()+this->getHScore();}boolCatSprite::ShortestPathStep::isEqual(constCatSprite::ShortestPathStep*other)const{returnthis->getPosition()==other->getPosition();}std::stringCatSprite::ShortestPathStep::getDescription()const{returnStringUtils::format("pos=[%.0f;%.0f]g=%dh=%df=%d",this->getPosition().x,this->getPosition().y,this->getGScore(),this->getHScore(),this->getFScore());}正如所见,这是一个很简单的类,记录了以下内容:-方块的坐标-G值(记住,这是开始点到当前点的方块数量)-H值(记住,这是当前点到目标点的方块估算数量)-Parent是它的上一步操作-F值,这是方块的和值(它是G+H的值)这里定义了getDescription方法,以方便调试。创建了isEquals方法,当且仅当两个ShortestPathSteps的方块坐标相同时,它们相等(例如它们代表着相同的方块)。创建Open和Closed列表打开CatSprite.h文件,添加如下代码:cocos2d::Vector_spOpenSteps;cocos2d::Vector_spClosedSteps;检查开始和结束点重新实现moveToward方法,获取当前方块坐标和目标方块坐标,然后检查是否需要计算一条路径,最后测试目标方块坐标是否可行走的(在这里只有墙壁是不可行走的)。打开CatSprite.cpp文件,修改moveToward方法,为如下:voidCatSprite::moveToward(constPoint&target){PointfromTileCoord=_layer->tileCoordForPosition(this->getPosition());PointtoTileCoord=_layer->tileCoordForPosition(target);if(fromTileCoord==toTileCoord){CCLOG("You'realreadythere!:P");return;}if(!_layer->isValidTileCoord(toTileCoord)||_layer->isWallAtTileCoord(toTileCoord)){SimpleAudioEngine::getInstance()->playEffect("hitWall.wav");return;}CCLOG("From:%f,%f",fromTileCoord.x,fromTileCoord.y);CCLOG("To:%f,%f",toTileCoord.x,toTileCoord.y);}编译运行,在地图上进行点击,如果不是点击到墙壁的话,可以在控制台看到如下信息:From:24.000000,0.000000To:20.000000,0.000000其中**From**就是猫的方块坐标,**To**就是所点击的方块坐标。实现A星算法根据算法,第一步是添加当前坐标到open列表。还需要三个辅助方法:-一个方法用来插入一个ShortestPathStep对象到适当的位置(有序的F值)-一个方法用来计算从一个方块到相邻方块的移动数值-一个方法是根据"曼哈顿距离"算法,计算方块的H值打开CatSprite.cpp文件,添加如下方法:voidCatSprite::insertInOpenSteps(CatSprite::ShortestPathStep*step){intstepFScore=step->getFScore();ssize_tcount=_spOpenSteps.size();ssize_ti=0;for(;igetFScore()){break;}}_spOpenSteps.insert(i,step);}intCatSprite::computeHScoreFromCoordToCoord(constPoint&fromCoord,constPoint&toCoord){//忽略了可能在路上的各种障碍returnabs(toCoord.x-fromCoord.x)+abs(toCoord.y-fromCoord.y);}intCatSprite::(constShortestPathStep*fromStep,constShortestPathStep*toStep){//因为不能斜着走,而且由于地形就是可行走和不可行走的成本都是一样的//如果能够对角移动,或者有沼泽、山丘等等,那么它必须是不同的return1;}接下来,需要一个方法去获取给定方块的所有相邻可行走方块。因为在这个游戏中,HelloWorld管理着地图,所以在那里添加方法。打开HelloWorldScene.cpp文件,添加如下方法:PointArray*HelloWorld::(constPoint&tileCoord)const{PointArray*tmp=PointArray::create(4);//上Pointp(tileCoord.x,tileCoord.y-1);if(this->isValidTileCoord(p)&&!this->isWallAtTileCoord(p)){tmp->addControlPoint(p);}//左p.setPoint(tileCoord.x-1,tileCoord.y);if(this->isValidTileCoord(p)&&!this->isWallAtTileCoord(p)){tmp->addControlPoint(p);}//下p.setPoint(tileCoord.x,tileCoord.y+1);if(this->isValidTileCoord(p)&&!this->isWallAtTileCoord(p)){tmp->addControlPoint(p);}//右p.setPoint(tileCoord.x+1,tileCoord.y);if(this->isValidTileCoord(p)&&!this->isWallAtTileCoord(p)){tmp->addControlPoint(p);}returntmp;}可以继续CatSprite.cpp中的moveToward方法了,在moveToward方法的后面,添加如下代码:boolpathFound=false;_spOpenSteps.clear();_spClosedSteps.clear();//首先,添加猫的方块坐标到open列表this->insertInOpenSteps(ShortestPathStep::createWithPosition(fromTileCoord));do{//得到最小的F值步骤//因为是有序列表,第一个步骤总是最小的F值ShortestPathStep*currentStep=_spOpenSteps.at(0);//添加当前步骤到closed列表_spClosedSteps.pushBack(currentStep);//将它从open列表里面移除//需要注意的是,如果想要先从open列表里面移除,应小心对象的内存_spOpenSteps.erase(0);//如果当前步骤是目标方块坐标,那么就完成了if(currentStep->getPosition()==toTileCoord){pathFound=true;ShortestPathStep*tmpStep=currentStep;CCLOG("PATHFOUND:");do{CCLOG("%s",tmpStep->getDescription().c_str());tmpStep=tmpStep->getParent();//倒退}while(tmpStep);//直到没有上一步_spOpenSteps.clear();_spClosedSteps.clear();break;}//得到当前步骤的相邻方块坐标PointArray*adjSteps=_layer->(currentStep->getPosition());for(ssize_ti=0;icount();++i){ShortestPathStep*step=ShortestPathStep::createWithPosition(adjSteps->getControlPointAtIndex(i));//检查步骤是不是已经在closed列表if(this->getStepIndex(_spClosedSteps,step)!=-1){continue;}//计算从当前步骤到此步骤的成本intmoveCost=this->(currentStep,step);//检查此步骤是否已经在open列表ssize_tindex=this->getStepIndex(_spOpenSteps,step);//不在open列表,添加它if(index==-1){//设置当前步骤作为上一步操作step->setParent(currentStep);//G值等同于上一步的G值+从上一步到这里的成本step->setGScore(currentStep->getGScore()+moveCost);//H值即是从此步骤到目标方块坐标的移动量估算值step->setHScore(this->computeHScoreFromCoordToCoord(step->getPosition(),toTileCoord));//按序添加到open列表this->insertInOpenSteps(step);}else{//获取旧的步骤,其值已经计算过step=_spOpenSteps.at(index);//检查G值是否低于当前步骤到此步骤的值if((currentStep->getGScore()+moveCost)getGScore()){//G值等同于上一步的G值+从上一步到这里的成本step->setGScore(currentStep->getGScore()+moveCost);//因为G值改变了,F值也会跟着改变//所以为了保持open列表有序,需要将此步骤移除,再重新按序插入//在移除之前,需要先保持引用step->retain();//现在可以放心移除,不用担心被释放_spOpenSteps.erase(index);//重新按序插入this->insertInOpenSteps(step);//现在可以释放它了,因为open列表应该持有它step->release();}}}}while(_spOpenSteps.size()>0);if(!pathFound){SimpleAudioEngine::getInstance()->playEffect("hitWall.wav");}添加以下方法:ssize_tCatSprite::getStepIndex(constcocos2d::Vector&steps,constCatSprite::ShortestPathStep*step){for(ssize_ti=0;iisEqual(step)){returni;}}return-1;}编译运行,在地图上进行点击,如下图所示:From:24.000000,0.000000To:23.000000,3.000000PATHFOUND:pos=[23;3]g=10h=0f=10pos=[22;3]g=9h=1f=10pos=[21;3]g=8h=2f=10pos=[20;3]g=7h=3f=10pos=[20;2]g=6h=4f=10pos=[20;1]g=5h=5f=10pos=[21;1]g=4h=4f=8pos=[22;1]g=3h=3f=6pos=[23;1]g=2h=2f=4pos=[24;1]g=1h=3f=4pos=[24;0]g=0h=0f=0注意该路径是从后面建立的,所以必须从下往上看猫选择了哪条路径。跟随路径前进现在已经找到了路径,只需让猫跟随前进即可。需要创建一个数组去存储路径,打开CatSprite.h文件,添加如下代码:cocos2d::Vector_shortestPath;打开CatSprite.cpp文件,更改moveToward方法,注释掉语句**boolpathFound=false**;,如下://boolpathFound=false;替换语句**pathFound=true;**为如下://pathFound=true;this->(currentStep);并且注释掉下方的调试语句://ShortestPathStep*tmpStep=currentStep;//CCLOG("PATHFOUND:");//do//{//CCLOG("%s",tmpStep->getDescription().c_str());//tmpStep=tmpStep->getParent();//倒退/