“AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (上)

简介: 一提到“A*算法”,可能很多人都有"如雷贯耳"的感觉。用最白话的语言来讲:把游戏中的某个角色放在一个网格环境中,并给定一个目标点和一些障碍物,如何让角色快速“绕过障碍物”找出通往目标点的路径。(如下图) 在寻路过程中,角色总是不停从一个格子移动到另一个相邻的格子,如果单纯从距离上讲,移动到与自身斜对角的格子走的距离要长一些,而移动到与自身水平或垂直方面平行的格子,则要近一些。

一提到“A*算法”,可能很多人都有"如雷贯耳"的感觉。用最白话的语言来讲:把游戏中的某个角色放在一个网格环境中,并给定一个目标点和一些障碍物,如何让角色快速“绕过障碍物”找出通往目标点的路径。(如下图)

img_5ead52103ac94ebfbf222b4fb6c1ba12.jpg

在寻路过程中,角色总是不停从一个格子移动到另一个相邻的格子,如果单纯从距离上讲,移动到与自身斜对角的格子走的距离要长一些,而移动到与自身水平或垂直方面平行的格子,则要近一些。为了描述这种区别,先引入二个概念:

节点(Node):每个格子都可以称为节点。

代价(Cost):描述角色移动到某个节点时所走的距离(或难易程度)。

img_513549fd813da8dae0ef1546bfa7c93d.jpg

如上图,如果每水平或垂直方向移动相邻一个节点所花的代价记为1,则相邻对角节点的代码为1.4(即2的平方根--勾股定理)

通常寻路过程中的代价用f,g,h来表示

g代表(从指定节点到相邻)节点本身的代价--即上图中的1或1.4

h代表从指定节点到目标节点(根据不同的估价公式--后面会解释估价公式)估算出来的代价。

f = g + h 表示节点的总代价,为了方便后面的代码描述,这里把节点封装成一个类Node.as

package {	
	public class Node
	{
		public var x:int;
		public var y:int;
		public var f:Number;
		public var g:Number;
		public var h:Number;
		public var walkable:Boolean=true;//是否可穿越(通常把障碍物节点设置为false)
		public var parent:Node;
		public var costMultiplier:Number=1.0;//代价因子

		public function Node(x:int, y:int)
		{
			this.x=x;
			this.y=y;
		}
	}
}

注意:这里有二个新的东东walkable和parent。

通常障碍物本身也可以看成是由若干个不可通过的节点所组成,所以walkable实际上是用来标记该节点是否为障碍物(节点)。

另外:在考查从一个节点移动到另一个节点时,总是拿自身节点周围的8个相邻节点来说事儿,相对于周边的节点来讲,自身节点称为它们的父节点(parent).

前面一直在提“网格,网格”,干脆把它也封装成类Grid.as

package
{

	public class Grid
	{
		private var _startNode:Node;//开始节点
		private var _endNode:Node;//目标节点
		private var _nodes:Array;//节点数组
		private var _numCols:int;//列数
		private var _numRows:int;//行数

		public function Grid(numCols:int, numRows:int)
		{
			_numCols=numCols;
			_numRows=numRows;
			_nodes=new Array();
			for (var i:int=0; i < _numCols; i++)
			{
				_nodes[i]=new Array();
				for (var j:int=0; j < _numRows; j++)
				{
					_nodes[i][j]=new Node(i, j);
				}
			}
		}

		public function getNode(x:int, y:int):Node
		{
			return _nodes[x][y] as Node;
		}


		public function setEndNode(x:int, y:int):void
		{
			_endNode=_nodes[x][y] as Node;
		}


		public function setStartNode(x:int, y:int):void
		{
			_startNode=_nodes[x][y] as Node;
		}


		public function setWalkable(x:int, y:int, value:Boolean):void
		{
			_nodes[x][y].walkable=value;
		}


		public function get endNode():Node
		{
			return _endNode;
		}


		public function get numCols():int
		{
			return _numCols;
		}


		public function get numRows():int
		{
			return _numRows;
		}


		public function get startNode():Node
		{
			return _startNode;
		}
	}
}

然而,在寻路的过程中“条条道路通罗马”,路径通常不止一条,只不过所花的代价不同而已

img_1b774af01f6253834eb3e498610d2881.jpg

如上图,如果按照黄色路径走,所花的总代价是14,而按照粉红色路径走,所花的总代价是16,所以我们要做的事情,就是要尽最大努力找一条代价最小的路径。

但是,“好事总多磨”,即使是代价相同的最佳路径,也有可能出现不同的走法:

 img_238184267fd853b620a1ac35100573d2.jpg

上图中三种不同的走法,总代价都是4.8,就上图而言,最佳路径(最小代价)用肉眼就能很快找出来,但是用代码如何估算起点与终点之间的代价呢?

//曼哈顿估价法
private function manhattan(node:Node):Number
{
	return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y + _endNode.y) * _straightCost;
}

//几何估价法
private function euclidian(node:Node):Number
{
	var dx:Number=node.x - _endNode.x;
	var dy:Number=node.y - _endNode.y;
	return Math.sqrt(dx * dx + dy * dy) * _straightCost;
}

//对角线估价法
private function diagonal(node:Node):Number
{
	var dx:Number=Math.abs(node.x - _endNode.x);
	var dy:Number=Math.abs(node.y - _endNode.y);
	var diag:Number=Math.min(dx, dy);
	var straight:Number=dx + dy;
	return _diagCost * diag + _straightCost * (straight - 2 * diag);
}

上面的代码给出了三种基本的估价算法(也称估价公式),其算法示意图如下:

img_d4b94ef455260001f450e88baf4f803d.jpg

如上图,对于“曼哈顿算法”最贴切的描述莫过于孙燕姿唱过的那首成名曲“直来直往”,笔直的走,然后转个弯,再笔直的继续。

“几何算法”的最好解释就是“勾股定理”,算出起点与终点之间的直线距离,然后乘上代价因子。

“对角算法”综合了以上二种算法,先按对角线走,一直走到与终点水平或垂直平行后,再笔直的走。

我们可以针对刚才的情况做下测试:

package
{
	import flash.display.Sprite;

	public class GridTest extends Sprite
	{
		private var _endNode:Node;
		private var _startNode:Node;
		private var _straightCost:Number=1.0;
		private var _diagCost:Number = 1.4;


		public function GridTest()
		{
			var g:Grid=new Grid(5, 5);
			g.setStartNode(0, 3);
			g.setEndNode(4, 1);
			
			_endNode = g.endNode;
			_startNode = g.startNode;
			
			var c1:Number = manhattan(_startNode);//8 
			var c2:Number = euclidian(_startNode);//4.47213595499958
			var c3:Number = diagonal(_startNode);//4.8
			
			trace(c1,c2,c3);
		}

		//曼哈顿估价法
		private function manhattan(node:Node):Number
		{
			return Math.abs(node.x - _endNode.x) * _straightCost + Math.abs(node.y - _endNode.y) * _straightCost;
		}

		//几何估价法
		private function euclidian(node:Node):Number
		{
			var dx:Number=node.x - _endNode.x;
			var dy:Number=node.y - _endNode.y;
			return Math.sqrt(dx * dx + dy * dy) * _straightCost;
		}

		//对角线估价法
		private function diagonal(node:Node):Number
		{
			var dx:Number=Math.abs(node.x - _endNode.x);
			var dy:Number=Math.abs(node.y - _endNode.y);
			var diag:Number=Math.min(dx, dy);
			var straight:Number=dx + dy;
			return _diagCost * diag + _straightCost * (straight - 2 * diag);
		}
	}
}

从输出结果可以看到“对角线估价法”跟肉眼预测的实际结果完全一致,总代价为4.8,以后默认情况下就用它了,不过这里提醒一下:这种代价是大概估计出来的,没有考虑到障碍物的因素,并非寻路过程中的实际代价,所以这也是“价计算公式”而非“代价计算公式”得名的由来。

目录
相关文章
|
11天前
|
存储 算法 安全
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
数据结构与算法系列学习之串的定义和基本操作、串的储存结构、基本操作的实现、朴素模式匹配算法、KMP算法等代码举例及图解说明;【含常见的报错问题及其对应的解决方法】你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
2024重生之回溯数据结构与算法系列学习之串(12)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丟脸好嘛?】
|
2天前
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
16 2
|
7天前
|
机器学习/深度学习 人工智能 自然语言处理
【EMNLP2024】基于多轮课程学习的大语言模型蒸馏算法 TAPIR
阿里云人工智能平台 PAI 与复旦大学王鹏教授团队合作,在自然语言处理顶级会议 EMNLP 2024 上发表论文《Distilling Instruction-following Abilities of Large Language Models with Task-aware Curriculum Planning》。
|
11天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习(8)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
11天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
11天前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
11天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之王道第2.3章节之线性表精题汇总二(5)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
IKU达人之数据结构与算法系列学习×单双链表精题详解、数据结构、C++、排序算法、java 、动态规划 你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
17天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
2天前
|
算法 数据挖掘 数据安全/隐私保护
基于FCM模糊聚类算法的图像分割matlab仿真
本项目展示了基于模糊C均值(FCM)算法的图像分割技术。算法运行效果良好,无水印。使用MATLAB 2022a开发,提供完整代码及中文注释,附带操作步骤视频。FCM算法通过隶属度矩阵和聚类中心矩阵实现图像分割,适用于灰度和彩色图像,广泛应用于医学影像、遥感图像等领域。
|
4天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。
下一篇
无影云桌面