unity中实现A*算法的一些问题

来源:互联网 发布:淘宝售假申诉流程 编辑:程序博客网 时间:2024/05/17 22:38

http://pan.baidu.com/s/1pJGHpbH有兴趣的同学可以下载下来把玩一番。这是我在完成算法之后,应用到项目之前,制作的一个demo。该demo通过读取xml文件,在空白的立方体表面上创建六个迷宫。其中,蓝色方块代表玩家,黑色方块代表怪物,怪物每三秒寻路一次,并使用红色方块标记路径。玩家可以通过方向键控制蓝色方块,改变它的位置。

声明:这不是A*算法教程!!!这不是A*算法教程!!!
前一段时间,我参与了某游戏项目,负责AI部分。项目提出这样的要求: 游戏场景为一个巨大的立方体。立方体的每一个面都是一个迷宫,而上面都有若干怪物。如果玩家到达立方体的某一个侧面上,该侧面上的怪物会进行寻路,追踪玩家。



该项目采用unity3D游戏引擎,但是我发现unity自带NavMesh组件似乎不能实现一个场景建立多个NavMesh。于是乎,决定采用A*算法,实现不同怪物能够在立方体不同的侧面上寻路。
A*算法简介
A*算法是寻路策略经常使用的算法。
http://www.policyalmanac.org/games/aStarTutorial.htm这里是一个经典A*算法教程。当然还有其他不错的教程,请大家采用自己喜欢的搜索引擎自行查找。
在执行A*算法的过程中存在两个节点表:Open表和Closed表。Open表表示准备拓展的节点的集合。Closed表表示已经拓展完毕的节点的集合。普通搜索算法通过遍历所有解从而去寻找最优解。而A*算法是估计节点代价,选择代价最小的节点去扩展,将可行的邻居节点加入Open表中,尝试性地去寻找最优解。A*算法在估计节点代价时,使用到估价函数。估价函数的表达式为:f(n)=g(n)+h(n)。f(n)表示从开始节点经过节点n到目标节点所需的估计花费,而g(n)表示从开始节点到节点n的实际花费,h(n)表示从节点n到目标节点的估计花费。如果h(n)设计的合理比较接近真实值,算法就会高效地选择出最优解。但是如果盲目追求h(n)的合理设计将很可能会因为考率的因素太多导致算法效率降低。
下图为A*算法的算法流程图。从图中可以看出,拓展节点时需要遍历当前节点的所有临接(邻居)节点。而节点的f(n)、g(n)和h(n)值决定了搜索结果。




f(n)=g(n)+h(n)的计算:
立方体的每一个侧面上的迷宫可以抽象为一个N*N二维数组。根据迷宫的布置,将可通过区域和障碍物分别标识在数组中。
例如某个侧面迷宫想要设计为:



则对应二维矩阵为:

某节点的f(n)值由g(n)和h(n)组成。g(n)表示从起始点到该点的实际花费。为了方便计算,设横向或者竖向行走一步的花费为10,斜着行走一步的花费为14(保证两者之间大概为√2倍关系)。
h(n)的值为当前结点所在网格到终点的欧式距离*10。



图中A为起点,C为终点,B为当前考察的节点。红色折线代表g(n),紫色折线代表h(n)。

按照算法过程,实现出代码没有什么特别大的难处,但是有几个问题需要注意:
关于closed表
程序中并不需要实现closed表,只要逻辑上存在就可以了。
斜着通过的问题:
如图这种情况是不应该发生的。解决起来也很简单,只要在拓展节点D时,判断并排除类似E的临节点就可以了。


C++与C#的“对接”:
我首先在C++中实现了A*算法,生成dll,在C#中调用。为了达到这个目的,在C++中声明函数时,需要
extern "C" int _declspec(dllexport) AStar(…)
在C#中,需要导入之前生成的AStar.dll,并声明该函数;
[DllImport("AStar")]
extern int AStar(…);
这一部分,我主要参考了王宇 warensoft的一篇博客,非常感谢。
http://www.cnblogs.com/warensoft/archive/2011/12/09/warenosoft3d.html。
将A*算法返回的路径应用到unity指导怪物运动的问题:
每次运动之前,怪物需要回到最近方格的中心,这样保证不会因为移动的偏差而卡在角落动弹不得。
拓展改进:
采用最小堆,快速查找open表中F值最小的节点。




0 0
原创粉丝点击