A星路径寻找算法的应用.docx

上传人:b****8 文档编号:10953253 上传时间:2023-02-23 格式:DOCX 页数:58 大小:687.14KB
下载 相关 举报
A星路径寻找算法的应用.docx_第1页
第1页 / 共58页
A星路径寻找算法的应用.docx_第2页
第2页 / 共58页
A星路径寻找算法的应用.docx_第3页
第3页 / 共58页
A星路径寻找算法的应用.docx_第4页
第4页 / 共58页
A星路径寻找算法的应用.docx_第5页
第5页 / 共58页
点击查看更多>>
下载资源
资源描述

A星路径寻找算法的应用.docx

《A星路径寻找算法的应用.docx》由会员分享,可在线阅读,更多相关《A星路径寻找算法的应用.docx(58页珍藏版)》请在冰豆网上搜索。

A星路径寻找算法的应用.docx

A星路径寻找算法的应用

摘要

A*算法是一种启发式的搜索算法。

使用A*算法能够快速地在游戏地图中得到一条最优路径。

本文通过对A*算法的分析,结合面向对象的思想,用C#语言实现可重用的A*类库,并应用于一个小游戏中。

关键词

A*算法;代价;二叉堆;启发函数

Abstract

A*algorithmisaheuristicsearchalgorithm.UsingtheA*algorithmcanquicklygettheoptimalpathinthegamemap.BasedontheA*algorithmanalysis,combinedwithobject-orientedthinking,anA*reusableclasslibrariywiththeC#languageisimplementedandappliedittoasmallgame.

Keywords

A*Algorithm;Cost;BinaryHeap;HeuristicFunction

目录

1引言1

2算法原理1

2.1算法基本概念1

2.1.1地图(搜索空间)1

2.1.2A*结点2

2.1.3代价2

2.2A*算法2

2.2.1启发函数与估价函数2

2.2.2Open表与Closed表2

2.2.3伪代码3

3算法设计4

3.1二叉堆(BinaryHeap)4

3.1.1数据存储和方法4

3.1.2添加元素4

3.1.3获取堆顶元素5

3.1.4删除堆顶元素5

3.1.5查找指定元素7

3.1.6获取和替换元素7

3.2AStarNode抽象类10

3.2.1属性10

3.2.2方法10

3.3AStar类11

3.3.1属性11

3.3.2方法11

3.4AStarNode2D类13

3.4.1属性14

3.4.2方法14

3.5IAStarMap接口14

3.6UML类图17

3.7类的使用18

4算法测试程序20

4.1程序描述20

4.2Model20

4.3Graphics22

4.4Controller22

4.5界面24

4.6测试25

5实现以A*算法为基础的追赶小游戏27

5.1程序描述27

5.2Model27

5.2.1Tile27

5.2.2Board29

5.2.3Unit32

5.2.4Enemy35

5.2.5Player38

5.3View40

5.4Controller43

5.5运行游戏47

6总结与展望50

参考文献51

谢 辞52

A*路径寻找算法在游戏开发中的应用

TheApplicationofA*PathfindingAlgorithminGameDevelopment

数学与信息工程学院计算机科学与技术专业

蒋文龙

指导教师:

陈荣钦

1引言

寻路是游戏中AI的基本问题之一,其问题是要让游戏角色找到从起点到目标的通路。

A*算法是游戏开发中常用的路径寻找算法,相比经典的Dijkstra算法[11],A*具有更快的搜索速度,因为A*采用了启发式的搜索方法[1]。

2算法原理

2.1算法基本概念

2.1.1地图(搜索空间)

A*算法所要搜索的空间,可以是一个二维网格,或者三维区域,也可以是传统的图。

地图中的各个地图结点联系必须被很好的描述,每个结点都能得到相应的子结点,如二维网格中,在某网格上可以与周围上下左右4个方向的网格相连通,也可以与周围8个方向上的网格相连通[2]。

A*能够在地图中的两点间找到一条最好的路径,路径的好坏由代价判断。

以二维网格地图为例,如图1所示。

在此例中地图中的每个网格于周围的8个网格相连接,代价为距离[17]。

图1二维网格地图

2.1.2A*结点

A*结点描述了A*算法所需要的关键信息:

1.结点在地图中的位置信息;

2.到达该结点的父结点,只有起始结点没有父结点;

3.结点的估计值,即估价函数计算的结果,估价函数在2.2节中介绍;

结点通过与父结点的联系,可以递归地得到地图的路径。

A*结点是独立于地图数据结构的[3],例如两个A*结点,他们的地图信息一样,但是他们的父结点不同,即他们是从不同的路径到达的此地图位置的[7,8]。

2.1.3代价

代价可以是时间、距离、金钱等因素。

路径的好坏由所通过地图结点的代价之和来判断,A*的目的是求得从起点到目标所花费代价最小的路径[7]。

2.2A*算法

2.2.1启发函数与估价函数

A*是一种启发式的搜索算法。

g(n)代表从起点到结点n到所花费得代价;启发函数h(n)代表从结点n到目标的启发式的估计值;估价函数f(n)=g(n)+h(n)代表经过该结点的路径的估计值,在A*算法运行中,f(n)值越小那么此结点的路径越好[20]。

启发函数在A*算法中非常重要,启发函数的设计决定了A*算法的达到目的的速度以及准确性。

假设h’(n)是结点n到目标的真值,只有当h(n)<=h’(n)时才能确保找到一条最好的路径。

在二维地图当中,代价为路径长度,每个地图结点由坐标(X,Y)表示,那么结点A到目标结点B的启发函数可以用两点间最短路径的方法来设计,即h(A)=

[4,5]。

2.2.2Open表与Closed表

A*算法,始终保持着两个表:

Open表与Closed表。

Open表存放着已计算出估计值,并且还没有访问过的A*结点。

Closed表存放已访问过的结点[6]。

Closed表用来判断该结点是否已访问,用哈希表可以很好的完成这项任务[13,19]。

Open表是A*算法访问最频繁的表,因为A*算法会从Open表中取出最小的估计值,作为本次访问节点。

Open表的数据结构设计决定A*算法的运行速度。

常用的数据结构:

有序表,有序链表,哈希表,二叉堆(BinaryHeap)等[18,19]。

本文用二叉堆来实现Open表,二叉堆是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。

二叉堆满足堆特性:

父结点的键值总是大于或等于任何一个子节点的键值。

二叉堆用数组来表示,数组的第1个元素的键值总是最大或最小得,由于这个特性,可以在O

(1)的时间内得到Open表中估计值最小的结点。

而且在二叉堆中插入元素和删除元素的操作都是O(logN)[16]。

2.2.3伪代码

参考[8]中的算法:

创建起始结点S和目标结点G;

初始化Open表和Close表;

将起始节点S存入Open表;

While(Open表非空)

{

从Open表取出估计值最小的结点curNode;

If(curNode的地图信息和G相等)

{

通过curNode的父结点获取路径;

Break;

}

获取curNode的子结点集合children;

Foreach(childinchildren)

{

If(child的地图信息在Close表中存在)

Continue;

If(child地图信息在Open表中存在)

{

If(child的估计值小于Open表中的估计值)

用当前的child替换Open表中原来的结点;

Continue;

}

将child存入Open表;

}

将curNode存入Close表;

}

3算法设计

3.1二叉堆(BinaryHeap)

3.1.1数据存储和方法

二叉堆[18]采用数组存储元素,二叉堆的主要方法:

(1).添加元素;

(2).获取堆顶元素;

(3).删除堆顶元素;

(4).查找指定元素;

(5).获取指定索引的元素;

(6).替换指定索引的元素;

3.1.2添加元素

将添加的元素置于数组arrayData的最后,然后从下向上调整堆。

代码如下:

publicvoidPush(Tdata)

{

if(length==capacity)//判断二叉堆是否已满

thrownewException("FullHeap");

inti;

for(i=length+1;i>1&&Compare(data,arrayData[(i>>1)-1]);i>>=1)

{

arrayData[i-1]=arrayData[(i>>1)-1];

}

arrayData[i-1]=data;

length++;

}

3.1.3获取堆顶元素

顶部元素即数组的第1个元素,只需获取arrayData[0]就能得到堆顶元素;

代码如下:

publicTPeek()

{

if(length==0)

thrownewException("EmptyHeap");

returnarrayData[0];

}

3.1.4删除堆顶元素

删除堆顶元素后需要重新调整堆,可以从堆顶向下调整,把值小的提取到上一层,直到最底层为止;

代码如下:

publicTPop()

{

if(length==0)

thrownewException("EmptyHeap");

Tret=arrayData[0];

Ttemp=arrayData[--length];

inti;

for(i=1;(i<<1)+1<=length;)

{

if(Compare(arrayData[(i<<1)-1],arrayData[i<<1]))

{

if(Compare(arrayData[(i<<1)-1],temp))

{

arrayData[i-1]=arrayData[(i<<1)-1];

i=i<<1;

}

else

{

break;

}

}

else

{

if(Compare(arrayData[i<<1],temp))

{

arrayData[i-1]=arrayData[i<<1];

i=(i<<1)+1;

}

else

{

break;

}

}

}

if(i<<1==length&&Compare(arrayData[(i<<1)-1],temp))

{

arrayData[i-1]=arrayData[(i<<1)-1];

arrayData[(i<<1)-1]=temp;

}

else

{

arrayData[i-1]=temp;

}

returnret;

}

3.1.5查找指定元素

遍历数组arrayData,查找比较值相等的元素,并返回索引,如果未找到则返回-1。

代码如下:

publicintIndexOf(Titem)

{

for(inti=0;i

{

if(arrayData[i].Equals(item))

{

returni;

}

}

return-1;

}

3.1.6获取和替换元素

获取元素后,不可以改变元素中排序比较的值,改变其值会使堆乱序;替换指定索引的元素后,需要重新调整堆,从替换的索引位置向上或向下调整。

以最小堆为例,如果替换的元素小于它的父节点,则向上调整堆,否则向下调整。

代码如下:

publicTthis[intindex]

{

get

{

if(index>=length)

{

thrownewIndexOutOfRangeException();

}

returnarrayData[index];

}

set

{

inti=index+1;

boolisUp=false;

while(i>1&&Compare(value,arrayData[(i>>1)-1]))

{

arrayData[i-1]=arrayData[(i>>1)-1];

i>>=1;

isUp=true;

}

if(isUp)

arrayData[i-1]=value;

else

{

for(i=index+1;(i<<1)+1<=length;)

{

if(Compare(arrayData[(i<<1)-1],arrayData[i<<1]))

{

if(Compare(arrayData[(i<<1)-1],value))

{

arrayData[i-1]=arrayData[(i<<1)-1];

i=i<<1;

}

else

{

break;

}

}

else

{

if(Compare(arrayData[i<<1],value))

{

arrayData[i-1]=arrayData[i<<1];

i=(i<<1)+1;

}

else

{

break;

}

}

}

if(i<<1==length&&Compare(arrayData[(i<<1)-1],value))

{

arrayData[i-1]=arrayData[(i<<1)-1];

arrayData[i<<1-1]=value;

}

else

{

arrayData[i-1]=value;

}

}

}

}

3.2AStarNode抽象类

AStarNode是所有A*结点的基类。

通过继承此类在不同的地图类型上应用A*算法。

属性和方法如图2所示。

图2AStarNode类图

3.2.1属性

F,G,H分别是f(n),g(n)和h(n)的计算结果。

GoalNode存储目标结点。

Parent存储当前结点的父结点。

3.2.2方法

GetHeuristic:

启发函数。

GetChildren:

获取当前结点的子结点。

IsOpen:

当前结点是否可通行。

Equals:

判断A*结点中的地图位置信息是否相等,重载于Object。

GetHashCode:

哈希函数,重载于Object,相同的地图信息的A*结点必须返回相同的值。

以上都是抽象方法。

IsGoalNode:

判断是否与目标结点相等。

CompareTo:

IComparable接口的方法,比较结点之间估计值的大小。

3.3AStar类

AStar是A*算法的主程序,通过构造函数创建AStar对象,执行Run方法后可以从PathNodes属性获取路径。

属性和方法如图3所示。

图3AStar类图

3.3.1属性

openList:

A*算法的Open表,用二叉堆实现。

closeList:

A*算法的Close表,用.Net类库中的HashSet实现。

startNode:

起始结点。

goalNode:

目标结点。

PathNodes:

存放A*算法计算的路径。

MaxPathLen:

最大的Open表长度,当算法运行超过Open表的容量,即返回false,表示未找到路径。

3.3.2方法

Run:

A*算法运行的开始,当找到路径时返回true,未找到则返回false。

代码如下:

publicboolRun()

{

if(!

startNode.isOpen()||!

goalNode.isOpen())

returnfalse;

openList=newBinaryHeap(MaxPathLen,HeapType.MinHeap);

closeList=newHashSet();

openList.Push(startNode);

AStarNodecurNode;

Listchildren;

while(openList.Length>0)

{

curNode=openList.Pop();

if(curNode.IsGoalNode())

{

pathNodes=newLinkedList();

while(curNode!

=null)

{

pathNodes.AddFirst(curNode);

curNode=curNode.Parent;

}

returntrue;

}

children=curNode.GetChildren();

foreach(AStarNodechildinchildren)

{

if(closeList.Contains(child))

continue;

intindex=openList.IndexOf(child);

if(index!

=-1)

{

if(child.G

{

openList[index]=child;

}

continue;

}

if(openList.Length==openList.Capacity)

returnfalse;

openList.Push(child);

}

closeList.Add(curNode);

}

returnfalse;

}

3.4AStarNode2D类

AStarNode2D继承自AStarNode,用来处理二维网格地图类型的A*结点。

属性和方法如图4所示。

图4AStarNode2D类图

3.4.1属性

AStarMap2D:

为了能够在不同的二维地图中重用AStarNode2D类,创建了IAStarMap接口,将GetChildren、isOpen和GetHeuristic委托给了实现IAStarMap接口的类[9,10,15]。

X,Y分别是横坐标和纵坐标。

3.4.2方法

AStarNode2D的方法都AStarNode的抽象方法的重载。

其中GetHeuristic和GetChildren委托给了AStarMap2D来实现。

3.5IAStarMap接口

接口方法如图5所示。

图5IAStarMap接口

地图类通过实现IAStarMap接口,能够被继承AStarNode的类所调用。

例如用IAStarMap实现二维网格地图AStarMap2D。

地图数据用二维数组表示,小于0的位置表示不能通过;大于0的位置则能够通过,并且其值为通过该位置所需的代价。

代码如下:

publicclassAStarMap2D:

IAStarMap

{

privateint[,]mapData;

publicAStarMap2D(int[,]map)

{

mapData=map;

}

publicAStarMap2D(intx,inty)

{

mapData=newint[x,y];

}

publicint[,]MapData

{

set

{

mapData=value;

}

get

{

returnmapData;

}

}

publicintGetCost(intx,inty)

{

if(x<0||x>mapData.GetLength(0))

return-1;

if(y<0||y>mapData.GetLength

(1))

return-1;

returnmapData[y,x];

}

publicboolisOpen(AStarNode2Dnode)

{

if(GetCost(node.X,node.Y)<0)

returnfalse;

else

returntrue;

}

publicdoubleGetHeuristic(AStarNode2DcurNode,AStarNode2DgoalNode)

{

if(goalNode!

=null)

{

doublexd=curNode.X-goalNode.X;

doubleyd=curNode.Y-goalNode.Y;

returnxd*xd+yd*yd;

}

return0;

}

publicListGetChildren(AStarNode2Dparent,AStarNode2DgoalNode)

{

Listchildren=newList();

intx=parent.X;

inty=parent.Y;

AddChild(children,parent,goalNode,x,y+1,1);

AddChild(children,parent,goalNode,x,y-1,1);

AddChild(children,parent,goalNode,x-1,y,1);

AddChild(children,parent,goalNode,x+1,y,1);

AddChild(children,parent,goalNode,x-1,y-1,1.414);

AddChild(children,parent,goalNode,x-1,y+1,1.414);

AddChild(children,parent,goalNode,x+1,y-1,1.414);

AddChild(children,parent,goalNode,x+1,y+1,1.414);

returnchildren;

}

privatevoidAddChild(Listchildren,AStarNode2Dparent,AStarNode2DgoalNode,intx,inty,doublefactor)

{

doubleg=this.

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 成人教育 > 远程网络教育

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1