代码改变世界

AStar法

2010-04-10 22:36  宝宝合凤凰  阅读(755)  评论(0编辑  收藏  举报

AS3 AStar算法(1)

Flash AS3 2009-12-03 17:25:44 阅读119 评论0 字号:

最近再次看了一下AStar算法,并把理论转化成了代码。后来在一个2.5D的格子上测试了一把,哈哈,很不错。

先说理论:
A-Star算法是一种静态路网中求解最短路最有效的方法。简单的说,就是从起点开始,计算出经过周围节点的代价。找到一条代价最小的通向终点的路径。整个过程就是不断把周围代价最小的节点作为新的起点,最后达终点,同时找到最佳路径。
上面说的节点,在网格中就是一个方格,不过也可以是其他形状,比如六边形。另外一个就是代价,代价包含两个方面的意思:从起点到当前点的代价和当前点到终点的估计代价。这两个代价值加起来就是当前点的总代价了。
通常用公式表示为:f = g + h.
g就是从起点到当前点的代价。
h是当前点到终点的估计代价,是通过估价函数计算出来的。这个函数可以说是整个算法的关键了,因为算法的不同直接影响最终结果,以及算法的效率。这里有一个简单而有效算法,对于简单的网格来说就是计算当前网格到终点的距离:Math.sqrt(dx*dx+dy*dy);

搜索过程是这样的:
有两个数组,openList(带考察表)存放已经估价的节点,其中代价最小的节点是下一次计算的起点。closedList(已考察表)存放从待考察表中取出的代价最小的节点,在对其周围的各个节点进行估价后就将其放入closedList。
对于每个节点都会有一个父节点,以一个点计算周围节点时,这个点就是其它节点的父节点。到达终点时,就通过每一个点的父节点一直找到起点。路径也就找到了。

计算代价:
对于一个不再边上的节点,他周围会有8个节点,可以看成他到周围8个点的代价都是1。精确点,到上下左右4个点的代价是1,到左上左下右上右下的1.414就是“根号2”,这个值就是前面说的g。大概就是下面这个样子
2.8  2.4  2  2.4  2.8
2.4  1.4  1  1.4  2.4
  2    1    0    1    2
2.4  1.4  1  1.4  2.4
2.8  2.4  2  2.4  2.8
对于h,需要用一个估价函数来计算,前面有说到一个简单算法就是计算直线距离。假设终点是(50,50),对于点(20,30)来说我们用前面提到的估价函数来计算的话就是这样:
dx = 50 - 20 = 30;
dy = 50 - 30 = 20;
h = Math.sqrt(dx * dx + dy * dy) = 36.1

理论好像差不多了。接下来就是代码来。
 
 

AS3 AStar算法(2)

Flash AS3 2009-12-03 22:31:34 阅读82 评论0 字号:

前面说了理论,该开始写代码来。首先,构造一个节点类AStarNode:
package com.cyy.astar 
{
/**
* ...
* @author Will Chen
* @version 1.0
* @email c_youyou@163.com
* @msn chenyouyou@live.cn
* @description ...
*/
public class AStarNode 
{
public var x:int; 
public var y:int; 
public var f:Number; 
public var g:Number; 
public var h:Number; 
public var parent:AStarNode; 
public var isEmpty:Boolean = true;
public function AStarNode(x:int, y:int)
this.x = x; 
this.y = y;
}
}
}

接着是网格类AStarGrid:
package com.cyy.astar 
{
/**
* ...
* @author Will Chen
* @version 1.0
* @email c_youyou@163.com
* @msn chenyouyou@live.cn
* @description ...
*/
public class AStarGrid 
{
public var startNode:AStarNode;
public var endNode:AStarNode;
public var nodes:Array;
public var columnNum:int;
public var rowNum:int;
public function AStarGrid(columnNum:int, rowNum:int)
{
this.columnNum = columnNum;
this.rowNum = rowNum;
nodes = new Array();
for(var i:int = 0; i < columnNum; i++)
{
nodes[i] = new Array();
for(var j:int = 0; j < rowNum; j++)
{
nodes[i][j] = new AStarNode(i, j);
}
}
}
public function getNode(x:int, y:int):AStarNode
{
return nodes[x][y];
}
public function setEndNode(x:int, y:int):AStarNode
{
if (x < this.columnNum && y < this.rowNum)
{
endNode = nodes[x][y];
}
else
{
endNode = null;
}
return endNode;
}
public function setStartNode(x:int, y:int):AStarNode
{
if (x < this.columnNum && y < this.rowNum)
{
startNode = nodes[x][y];
}
else
{
startNode = null;
}
return startNode;
}
public function setNodeEmpty(x:int, y:int, value:Boolean):void
{
nodes[x][y].isEmpty = value;
}
}
}

然后就是AStar:
package com.cyy.astar 
{
/**
* ...
* @author Will Chen
* @version 1.0
* @email c_youyou@163.com
* @msn chenyouyou@live.cn
* @description ...
*/
public class AStar
{
private var openList:Array;
private var closedList:Array;
private var _path:Array;
private var grid:AStarGrid;
private var endNode:AStarNode;
private var startNode:AStarNode;
private var costOne:Number = 1;
private var costSqrt:Number = Math.SQRT2;
public function AStar() 
{
}
public function findPath(grid:AStarGrid):Boolean 
this.grid = grid;
openList = new Array();
closedList = new Array();
grid.startNode.g = 0;
grid.startNode.h = getH(grid.startNode);
grid.startNode.f = grid.startNode.g + grid.startNode.h;
return begin();
}
private function begin():Boolean
{
var node:AStarNode = grid.startNode;
while (node != grid.endNode)
{
var beginX:int = node.x == 0 ? 0 : node.x - 1;
var endX:int = node.x == grid.columnNum - 1 ? grid.columnNum - 1 : node.x + 1;
var beginY:int = node.y == 0 ? 0 : node.y - 1;
var endY:int = node.y == grid.rowNum - 1 ? grid.rowNum - 1 : node.y + 1;
for (var i:int = beginX; i <= endX; i++) 
{
for (var j:int = beginY; j <= endY; j++) 
{
var currentNode:AStarNode = grid.getNode(i, j);
if (currentNode == node || !currentNode.isEmpty
|| (!grid.getNode(node.x, currentNode.y).isEmpty && !grid.getNode(currentNode.x, node.y).isEmpty))
{
continue;
}
var cost:Number = costOne;
if (!((node.x == currentNode.x) || (node.y == currentNode.y)))
{
cost = costSqrt;
}
var g:Number = node.g + cost;
var h:Number = getH(currentNode);
var f:Number = g + h;
if (openList.indexOf(currentNode) == -1 && closedList.indexOf(currentNode) == -1)
{
currentNode.f = f;
currentNode.g = g;
currentNode.h = h;
currentNode.parent = node;
openList.push(currentNode);
}
}
}
closedList.push(node);
if (openList.length == 0)
{
return false
}
openList.sortOn("f", Array.NUMERIC);
node = openList.shift();
}
_path = new Array(); 
node = grid.endNode;
_path.push(node);
while (node != grid.startNode)
node = node.parent; 
_path.unshift(node);
}
return true;
}
private function getH(node:AStarNode):Number 
var dx:Number = node.x - grid.endNode.x;
var dy:Number = node.y - grid.endNode.y;
return Math.sqrt(dx * dx + dy * dy); 
}
public function get path():Array { return _path; }
}
}

OK,完成,核心代码就这么多了,不过还有很多地方可以优化。
下次再写一个测试用的类

AS3 AStar算法(3)

Flash AS3 2009-12-04 10:30:32 阅读79 评论0 字号:

开始测试。。。不多说,看代码:
package  
{
import com.cyy.astar.AStar;
import com.cyy.astar.AStarGrid;
import com.cyy.astar.AStarNode;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
/**
* ...
* @author Will Chen
* @version 1.0
* @email c_youyou@163.com
* @msn chenyouyou@live.cn
* @description ...
*/
public class AStarTest extends Sprite
{
private var player:Sprite;
private var grid:AStarGrid;
private var index:int;
private var path:Array;
private var speed:Number = 0.5;
public function AStarTest() 
{
player = new Sprite();
player.graphics.beginFill(0x0000ff);
player.graphics.drawCircle(0, 0, 5);
player.graphics.endFill();
player.x = 310;
player.y = 310;
addChild(player);
grid = new AStarGrid(50, 50);
for (var i:int = 0; i < 500; i++)
grid.setNodeEmpty(Math.floor(Math.random() * 50), Math.floor(Math.random() * 50), false);
}
drawGrid();
stage.addEventListener(MouseEvent.CLICK, onStageClickHandler);
}
private function drawGrid():void
{
graphics.clear();
for (var i:int = 0; i < grid.columnNum; i++) 
{
for (var j:int = 0; j < grid.rowNum; j++)
{
graphics.lineStyle(0);
graphics.beginFill(getColor(grid.getNode(i, j)));
graphics.drawRect(i * 20, j * 20, 20, 20);
graphics.endFill();
}
}
}
private function getColor(node:AStarNode):uint 
{
if (!node.isEmpty)
{
return 0x000000;
}
if (node == grid.startNode || node == grid.endNode)
{
return 0x00ff00;
}
return 0xffffff;
}
private function onStageClickHandler(event:MouseEvent):void 
var posX:int = Math.floor(mouseX / 20); 
var posY:int = Math.floor(mouseY / 20);
grid.setEndNode(posX, posY);
posX = Math.floor(player.x / 20); 
posY = Math.floor(player.y / 20); 
grid.setStartNode(posX, posY);
drawGrid();
getPath(); 
}
private function getPath():void 
var astar:AStar = new AStar(); 
if (astar.findPath(grid))
path = astar.path; 
index = 1;
addEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
else
{
trace("not found the path!!!");
}
}
private function onEnterFrameHandler(e:Event):void 
{
var pathX:Number = path[index].x * 20 + 20 / 2;
var pathY:Number = path[index].y * 20 + 20 / 2;
var dx:Number = pathX - player.x;
var dy:Number = pathY - player.y;
var temp:Number = Math.sqrt(dx * dx + dy * dy);
if (temp < 1)
{
index++;
if (index == path.length)
{
removeEventListener(Event.ENTER_FRAME, onEnterFrameHandler);
}
}
else
{
player.x += dx * speed;
player.y += dy * speed;
}
}
}
}
直接编译就能跑了,貌似还不错,呵呵