为什么我的小规模 A* 路径脚本会跳过所有内容?

Why is my small scale A* pathing script skipping everything?

我正在为 7x7 方格网格上的游戏编写 A* 路径脚本,玩家始终位于中间(方格 24)。零作为视觉对象添加,它实际上是一个数组,而不是 7x7 二维数组。

[00,01,02,03,04,05,06]
[07,08,09,10,11,12,13]
[14,15,16,17,18,19,20]
[21,22,23,24,25,26,27]
[28,29,30,31,32,33,34]
[35,36,37,38,39,40,41]
[42,43,44,45,46,47,48]

该游戏是服务器驱动的,因此玩家使用相对坐标。这意味着,如果玩家移动,tile[0] 就会改变。简而言之,玩家将始终从中间的第 24 块牌开始移动。网格是硬编码的,但如果我 post 公开它,我会稍微更改代码;没问题。

该函数应该有一个目的地,并找到从 24 号地块到那个方块的良好路径,但它实际上做了什么return“未定义”。

如果我输入 24 我希望游戏输出这样的数组

[18,12,6]

代码如下:

z = 0;
function pathTo(goal){
    var createPath = function (goal){
        var createNode = function(i){
            this.id = i;
            this.g = Infinity;
            this.f = Infinity;
            this.parent = null;
            this.open = null;
        };
        
        this.nodes = Array(49);
        for(i=0;i<this.nodes.length;i++){
            this.nodes[i] = new createNode(i);
        }
        this.start = this.nodes[24];
        this.start.g = 0;
        this.currentNodeId = 24;
        this.goal = this.nodes[goal];
        this.bestPath = null;
    };//end createPath
    var getBestNeighbor = function(nodeId){
        z++
        if(z>50){throw z}debugger;
        console.log(nodeId);
        var getG = function(parentG){
            //here you can check the map for water, sand, and ruins penalties
            /*
                default = 1
                path = .9
                water = 3
            */

            return (parentG + 1);
        };
        var closeNode = function (node){
            node.open = false;
        };//end closeNode
        var getF = function(startId,endId,g){
            if(g>9){
                throw g;
            }
            var startX = startId % 7;
            var startY = (startId - startX) / 7;
            var endX = endId % 7;
            var endY = (endId - endX) / 7;
            var h = Math.sqrt( Math.pow((startX - endX) , 2 ) + Math.pow(( startY - endY ), 2 ) );
            console.log("Start.id:"+startId+"H:"+h+"  Start.id.g:"+g);
            return (h + g);
        };//end getF
        var tracePath = function(tracedNode){
            path.bestPath = [];
            while(tracedNode != path.start){
                path.bestPath.unshift(tracedNode.id);
                tracedNode = tracedNode.parent;
            }
            return path.bestPath;
        };//end tracePath
        var getNeighborNodeId = function(x,y,currentId){return currentId + (y*7) + x;};//end getNeighborNodeId
        if(path.bestPath === null){
            var neighborNode = {};
            var bestNode = {f: Infinity};
            if(nodeId == path.goal.id){//may need to pass path
                return tracePath(path.nodes[nodeId]);
            }else{
                for(x=-1;x<=1;x++){
                    for(y=-1;y<=1;y++){
                        var nnId = getNeighborNodeId(x,y,nodeId);
                        if(nnId==24){debugger}
                            if( ( (x!=0) && (y!=0) ) ||( (nnId>=0) && (nnId<=48))){
                                var neighborNode = path.nodes[nnId];
                                if(neighborNode.open === null){ neighborNode.open = true; }
                                if(neighborNode.open === true ){//don't check closed neighbors
                                    if(typeof neighborNode === "object"){
                                        neighborNode.parent = path.nodes[nodeId]
                                        debugger;
                                        neighborNode.g = getG(neighborNode.parent.g);
                                        neighborNode.f = getF(neighborNode.id , path.goal.id , neighborNode.g);
                                        if( neighborNode.f < bestNode.f){
                                            bestNode = neighborNode;
                                        }//endif
                                    }//endif
                                }//endif Note: if the node isn't null or true, it's false.
                            }
                    }//endfor
                }//endfor - Note: Here we should have the best neighbor
                if(bestNode.f == Infinity){
                    closeNode(path.nodes[nodeId]);//need escape for no possible path
                    return;
                }else{
                    //bestNode.parent = path.nodes[nodeId];
                    path.currentNodeId = bestNode.id;
                    getBestNeighbor(bestNode.id);
                }//endelse
            }//endelse
        }//endif
    };//end getBestNeighbor
    var path = new createPath(goal);
    while(path.bestPath === null){
        getBestNeighbor(path.currentNodeId);
    }//end while
    return path.bestPath;
}//end pathTo
console.log(pathTo(41));  //testing with 6

和一个 JSFiddle link:https://jsfiddle.net/jb4xtf3h/

这是我第一次不只是到处打全局变量,所以它可能有一个我不熟悉的范围问题。

很可能我的问题出在 getNeighborId 函数中;我不认为我有任何声明好的节点的父节点。

问题是它去了 NW 3 次而不是 NE 3 次。这可能意味着我在 getBestNeighbor 函数中有一个错误,我将 -1 读为 1。

而且我认为我没有正确地转义递归函数。

出于某种原因,当我输入 41 时,它变得非常混乱。这要么与我如何设置 G 和 H 有关,它们通常在 A* 中用于记录在这条路径上行驶的距离和估计的剩余距离。特别是 G 数是错误的,因为它出于某种原因采取了错误的步骤。

这是工作代码。我没有实现墙壁或任何东西,但我确实展示了你会在哪里做。您需要做的就是在开始寻路之前关闭所有作为墙的节点,如果您希望 AI“知道”避免水或沙子,您可以分配移动惩罚。

我实际上无法确定一个问题,但一个主要问题是声明的方式:

if( ( (x!=0) && (y!=0) ) ||( (nnId>=0) && (nnId<=48))){

已更改为:

if( ( !(x==0 && y==0) && ( nnId>=0 && nnId<=48))){

此行的目的是防止搜索您站在 x,y = (0,0) 上的图块,并确保您要查看的邻居在网格上(7x7 网格有49 个方格,编号为 0-48)

我想说的是“如果 X 和 Y 都不为零”显然这实际上使它与 or 语句相同,所以如果任何一个方块为 0,它就会跳过它和需要它的方块 space 遇到了问题,因为有几个方向不起作用。

如果有人需要一个很好的简单路径脚本,我希望对他们有所帮助我非常努力地使代码可读,我不是世界上最强大的编码员,但我认为一个 100 行的工作 A* 脚本很容易理解。如果您正在阅读本文并且不熟悉 A* 路径,您可能需要知道的是

H 是您的启发值,它是对从图块剩余距离的估计。在此代码中,它位于路径对象 path.nodes[array#].h

G 是您到目前为止移动到那个方块的距离 path.nodes[array#].g

F 只是将 h+g 加到总值上。 This pseudocode on Wikipedia帮我写的

var z =  0;
    function pathTo(goal){
        var createPath = function (goal){
            var createNode = function(i){
                this.id = i;
                this.g = Infinity;
                this.f = Infinity;
                this.parent = null;
                this.open = null;
            };
            
            this.nodes = Array(49);
            for(i=0;i<this.nodes.length;i++){
                this.nodes[i] = new createNode(i);
            }
            this.start = this.nodes[24];
            this.start.g = 0;
            this.currentNodeId = 24;
            this.goal = this.nodes[goal];
            this.bestPath = null;
        };//end createPath
        var path = new createPath(goal);
        var getBestNeighbor = function(nodeId){

            var getG = function(parentG){
                //here you can check the map for water, sand, and ruins penalties
                /*
                    default = 1
                    path = .9
                    water = 3
                */

                return (parentG + 1);
            };
            var closeNode = function (node){
                node.open = false;
            };//end closeNode
            var getF = function(startId,endId,g){
                var startX = startId % 7;
                var startY = (startId - startX) / 7;
                var endX = endId % 7;
                var endY = (endId - endX) / 7;
                var h = Math.sqrt( Math.pow((startX - endX) , 2 ) + Math.pow(( startY - endY ), 2 ) );
                return (h + g);
            };//end getF
            var tracePath = function(tracedNode){
                path.bestPath = [];
                while(tracedNode != path.start){
                    path.bestPath.unshift(tracedNode.id);
                    tracedNode = tracedNode.parent;
                }
                return path.bestPath;
            };//end tracePath
            var getNeighborNodeId = function(x,y,currentId){return currentId + (y*7) + x;};//end getNeighborNodeId
            debugger;
            z++
            if(z>50){throw z}
            if(path.bestPath === null){
                var neighborNode = {};
                var bestNode = {f: Infinity};
                if(nodeId == path.goal.id){//may need to pass path
                    return tracePath(path.nodes[nodeId]);
                }else{
                    for(y=-1;y<=1;y++){
                        for(x=-1;x<=1;x++){
                            var nnId = getNeighborNodeId(x,y,nodeId); 
                                if( ( !(x==0 && y==0) && ( nnId>=0 && nnId<=48))){
                                    var neighborNode = path.nodes[nnId];
                                    if(path.nodes[nodeId].parent!=neighborNode){
                                        if(neighborNode.open === null){ neighborNode.open = true; }
                                        if(neighborNode.open === true ){//don't check closed neighbors
                                            if(typeof neighborNode === "object"){
                                                neighborNode.parent = path.nodes[nodeId]
                                                neighborNode.g = getG(neighborNode.parent.g);
                                                neighborNode.f = getF(neighborNode.id , path.goal.id , neighborNode.g);
                                                if( neighborNode.f < bestNode.f){
                                                    bestNode = neighborNode;
                                                }//endif
                                            }//endif
                                        }
                                    }//endif Note: if the node isn't null or true, it's false.
                                }
                        }//endfor
                    }//endfor - Note: Here we should have the best neighbor
                    if(bestNode.f >= 50){
                        closeNode(path.nodes[nodeId]);//need escape for no possible path
                        return;
                    }else{
                        path.currentNodeId = bestNode.id;
                        getBestNeighbor(bestNode.id);
                    }//endelse
                }//endelse
            }//endif
        };//end getBestNeighbor
        while(path.bestPath === null){
            getBestNeighbor(path.currentNodeId);
        }//end while
        return path.bestPath;
    }//end pathTo
    myPath = pathTo(41);  //testing with 6
    console.log("path2:"+myPath);