提供一個A*尋徑算法的源碼+DEMO,做WEB遊戲很有用。
下面是DEMO:
<html><head><title>use A* to find path...</title></head>
<body style="margin:0px">
<script>
//地圖寬高
var w=30,h=20;
//地圖數組
var map = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]
var start=[0,0];
var end = [29,19];
//設定相關
var flag = 0;
var startTD = null;
var endTD = null;
var passList = [];
function setFlag()
{
var ele = event.srcElement;
switch(flag)
{
case 0:
{
if(startTD != null)
startTD.style.background = "#ffffff";
ele.style.background = "#0000ff";
start = ele.getAttribute("pos").split(',');
startTD = ele;
break;
}
case 1:
{
if(endTD != null)
endTD.style.background = "#ffffff";
ele.style.background = "#ff0000";
end = ele.getAttribute("pos").split(',');
endTD = ele;
break;
}
case 2:
ele.style.background = "#c1c1c1";
var pos = ele.getAttribute("pos").split(',');
map[pos[1]][pos[0]]=1;//設置障礙點
passList[passList.length]=ele;
break;
default:
break;
}
}
var lastPath = null;
function findIt()
{
lastPath = AStar.getPath(map,start,end);
for(var i=0;i<lastPath.length;i++)
{
maptt.rows[lastPath[i][1]].cells[lastPath[i][0]].style.backgroundColor="#00ff00";
}
alert(lastPath.join(','));
}
function clearIt()
{
if(lastPath == null)
return;
for(var i=0;i<lastPath.length;i++)
{
maptt.rows[lastPath[i][1]].cells[lastPath[i][0]].style.backgroundColor="#ffffff";
}
if(startTD != null)
startTD.style.backgroundColor="#ffffff";
if(endTD != null)
endTD.style.backgroundColor="#ffffff";
if(passList.length >0)
{
for(var j=0;j<passList.length;j++)
passList[j].style.backgroundColor="#ffffff";
}
flag = 0;
startTD = null;
endTD = null;
passList = [];
}
</script>
<script language="javascript" src="myAStar.js"></script>
<table id="maptt" style="position:absolute;left:100px;top:50px;" cellspacing="1" cellpadding="0" border="0" bgcolor="#000000">
<script>
for(var i=0;i<h;i++){
document.write("<tr>");
for(var j=0;j<w;j++){
document.write('<td pos='+j+","+i+' bgcolor="#ffffff" width="20" height="20" onclick="setFlag();"></td>');
}
document.write("</tr>");
}
</script>
</table>
<div id="info" style="position:absolute;left:100px;top:550px;">
</div>
<input type="button" value="start" onclick="flag=0;">
<input type="button" value="end" onclick="flag=1;">
<input type="button" value="non" onclick="flag=2;">
<input type="button" value="find" onclick="findIt();">
<input type="button" value="clear" onclick="clearIt();">
</body>
</html>
源碼:
// JScript 文件
var AStar={};
AStar.map = [];//矩陣
AStar.closelist=[];//已考察表
AStar.openlist=[];//待考察表
AStar.start = [0,0];
AStar.end = [0,0];
AStar.sPath = [];
AStar.gw=10;
AStar.gh=10;
AStar.gwh=14;
AStar.num=0;
AStar.intA = function(t)
{
t[0]=parseInt(t[0]);t[1]=parseInt(t[1]);
}
AStar.getPath = function(m,s,e)
{
this.closelist.length = 0;
this.openlist.length = 0;
this.num=0;
this.intA(s);
this.intA(e);
this.map = m;
this.start = s;
this.end = e;
var h=(Math.abs(e[0]-s[0])+Math.abs(e[1]-s[1]))*this.gw;
this.sPath=[h,0,h,s,e];
if(this.doLoop())
return this._getPath();
return [];
}
AStar._getPath = function(){
var path=[]
var t=this.closelist[this.closelist.length-1][4];
while(1){
path[path.length]=t;
for(var i=0;i<this.closelist.length;i++){
if(this.closelist[i][3][0]==t[0]&&this.closelist[i][3][1]==t[1])
t=this.closelist[i][4];
}
if(t[0]==this.start[0]&&t[1]==this.start[1])
break;
}
return path;
}
AStar.doLoop = function(){
this.GetF(this.getRound(this.sPath[3]));
this.Sort(this.openlist);
this.sPath=this.openlist[this.openlist.length-1];
this.closelist[this.closelist.length]=this.sPath;
this.openlist[this.openlist.length-1]=null;
if(this.openlist.length==0){return false;}
this.openlist.length=this.openlist.length-1;
if( (this.sPath[3][0] == this.end[0]) && (this.sPath[3][1] == this.end[1]) ){
return true;
}
else{
//maptt.rows[this.sPath[3][1]].cells[this.sPath[3][0]].style.backgroundColor="#00ff00";
return this.doLoop();
}
}
AStar.GetF = function (arr){
var t,G,H,F;
for(var i=0;i<arr.length;i++){
t=arr[i].split(",");
t[0]=parseInt(t[0]);t[1]=parseInt(t[1]);
if(this.IsOutScreen([t[0],t[1]])||this.IsPass(arr[i])||this.InClose([t[0],t[1]])||this.IsStart([t[0],t[1]])||!this.IsInTurn([t[0],t[1]]))
continue;
if((t[0]-this.sPath[3][0])*(t[1]-this.sPath[3][1])!=0)
G=this.sPath[1]+this.gwh;
else
G=this.sPath[1]+this.gw;
if(this.InOpen([t[0],t[1]])){
if(G<this.openlist[this.num][1]){
this.openlist[this.num][0]=(G+this.openlist[this.num][2]);
this.openlist[this.num][1]=G;
this.openlist[this.num][4]=this.sPath[3];
}
else{G=this.openlist[this.num][1];}
}
else{
H=(Math.abs(this.end[0]-t[0])+Math.abs(this.end[1]-t[1]))*this.gw;
F=G+H;
arr[i]=new Array();
arr[i][0]=F;arr[i][1]=G;arr[i][2]=H;arr[i][3]=[t[0],t[1]];arr[i][4]=this.sPath[3];
this.openlist[this.openlist.length]=arr[i];
}
// maptt.rows[t[1]].cells[t[0]].style.backgroundColor="#FF00FF";
}
}
//獲取周圍八個方向的點的位置
AStar.getRound = function (pos){
var a=new Array();
a[0]=(pos[0]+1)+","+(pos[1]-1);
a[1]=(pos[0]+1)+","+pos[1];
a[2]=(pos[0]+1)+","+(pos[1]+1);
a[3]=pos[0]+","+(pos[1]+1);
a[4]=(pos[0]-1)+","+(pos[1]+1);
a[5]=(pos[0]-1)+","+pos[1];
a[6]=(pos[0]-1)+","+(pos[1]-1);
a[7]=pos[0]+","+(pos[1]-1);
return a;
}
AStar.InOpen = function (pos){
var bool=false;
for(var i=0;i<this.openlist.length;i++)
{
if(pos[0]==this.openlist[i][3][0]&&pos[1]==this.openlist[i][3][1])
{
bool=true;
this.num=i;
break;
}
}
return bool;
}
AStar.InClose = function (pos){
var bool=false;
for(var i=0;i<this.closelist.length;i++){
if((pos[0]==this.closelist[i][3][0])&&(pos[1]==this.closelist[i][3][1])){
bool=true;break;}
}
return bool;
}
//是否是障礙點
AStar.IsPass = function (_pos){
var pos = _pos.split(',');
if(this.map[pos[1]][pos[0]] == 1)
return true;
return false;
}
AStar.IsStart = function (pos){
if(pos[0]==this.start[0]&&pos[1]==this.start[1])
return true;
return false;
}
AStar.IsInTurn = function(pos){
if(pos[0]>this.sPath[3][0]){
if(pos[1]>this.sPath[3][1]){
if(this.IsPass((pos[0]-1)+","+pos[1])||this.IsPass(pos[0]+","+(pos[1]-1)))
return false;
}
else if(pos[1]<this.sPath[3][1]){
if(this.IsPass((pos[0]-1)+","+pos[1])||this.IsPass(pos[0]+","+(pos[1]+1)))
return false;
}
}
else if(pos[0]<this.sPath[3][0]){
if(pos[1]>this.sPath[3][1]){
if(this.IsPass((pos[0]+1)+","+pos[1])||this.IsPass(pos[0]+","+(pos[1]-1)))
return false;
}
else if(pos[1]<this.sPath[3][1]){
if(this.IsPass((pos[0]+1)+","+pos[1])||this.IsPass(pos[0]+","+(pos[1]+1)))
return false;
}
}
return true;
}
AStar.IsOutScreen = function (pos){
if(pos[0]<0||pos[1]<0||pos[0]>(this.map[0].length-1)||pos[1]>(this.map.length-1))
return true;
return false;
}
AStar.Sort = function (arr){
var temp;
for(var i=0;i<arr.length;i++){
if(arr.length==1)break;
if(arr[i][0]<=arr[i+1][0]){
temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
if((i+1)==(arr.length-1))
break;
}
}