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;
}
}
}
}
直接编译就能跑了,貌似还不错,呵呵