A*寻路算法
class Node//结点
{
    public bool isOb = false;
    public Node Parent;
    public int F;
    public int G;
    public int H;
    public int X;
    public int Y;
    public bool isLine = false;
}

class Map//地图
{
    public Node[][] nodes;
    public int XLength;
    public int YLength;
    
    public void Clear()
    {
        for (int i = 0; i < XLength; i++)
        {
            for (int j = 0; j < YLength; j++)
            {
                Node node = nodes[i][j];
                node.F = 0;
                node.G = 0;
                node.H = 0;
                node.isLine = false;
                node.Parent = null;
            }
        }
    }
    
    //输出地图和寻路结果信息
    public void PrintMap()
    {
        for (int i = 0; i < YLength; i++)
        {
            if (i == 0)
            {
                Console.Write(" " + " ");
                for (int k = 0; k < XLength; k++)
                {
                    Console.Write(k + " ");
                }
                Console.WriteLine();
            }
            for (int j = 0; j < XLength; j++)
            {
                if (j == 0)
                {
                    Console.Write(i + " ");
                }
                Node node = nodes[j][i];
                if (node.isLine)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                }
                else
                {
                    Console.ForegroundColor = ConsoleColor.Green;
                }
                if (node.isOb)
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("#" + " ");
                }
                else
                {   
                    Console.Write("*" + " ");
                }
                Console.ForegroundColor = ConsoleColor.White;
            }
            Console.WriteLine();
        }
    }
}

class AXin//寻找
{
    private List<Node> openList;
    private List<Node> closeList;
    private Map map;
    private Node startNode;
    private Node endNode;
    private Node preNode;
    
    public AXin()
    {
        openList = new List<Node>();
        closeList = new List<Node>();
    }
    
    public void Search(int startX, int startY,int endX,int endY)
    {
        if (map == null)
        {
            Console.WriteLine("请设置地图");
            return;
        }
        openList.Clear();
        closeList.Clear();
        map.Clear();
        startNode = map.nodes[startX][startY];
        if (startNode.isOb)
        {
            Console.WriteLine("开始位置为障碍物!");
            return;
        }
        endNode = map.nodes[endX][endY];
        openList.Add(startNode);
        Search();
    }
    
    private void Search()
    {
        bool isSearch = false;
        Node tempNode = null;
        while (openList.Count > 0 && !isSearch)
        {
            tempNode = GetLowerFNode();
            openList.Remove(tempNode);
            closeList.Add(tempNode);
            preNode = tempNode; //用来计算其他的结点的F
            int x = tempNode.X, y = tempNode.Y;
            //遍历周边结点8个  障碍和在close的 忽略
            for (int i = -1; i < 2; i++)//x
            {
                for (int j = -1; j < 2; j++)//y
                {
                    x = tempNode.X + i;
                    y = tempNode.Y + j;
                    if (x < 0 || x >= map.XLength || y < 0 || y >= map.YLength)
                    {
                        //越界了
                        continue;
                    }
                    if (i == 0 && j == 0) continue;//中心点
                    Node node = map.nodes[x][y];
                    //障碍跳过
                    if (node.isOb) continue;
                    //在close里面的跳过
                    if (isInClose(node)) continue;
                    //检查对角线 两边有障碍物的不能过
                    if (i != 0 && j != 0)
                    {
                        Node oneNode = map.nodes[tempNode.X][y];
                        if (oneNode.isOb) continue;
                        oneNode = map.nodes[x][tempNode.Y];
                        if (oneNode.isOb) continue;
                    }
                    //已经在openList
                    if (isInOpen(node))
                    {
                        //判断是否需要重新算 F
                        int offsetX = Math.Abs(node.X - preNode.X);
                        int offsetY = Math.Abs(node.Y - preNode.Y);
                        int len = 10;
                        if (offsetX + offsetY == 2)
                        {
                            len = 14;
                        }
                        if (preNode.G + len < node.G)
                        {
                            //重新算
                            node.G = preNode.G + len;
                            node.F = node.G + node.H;
                            node.Parent = preNode;
                        }
                    }
                    else {
                        //计算F 加进去 设置父物体
                        CalF(node);
                        openList.Add(node);
                        node.Parent = preNode;
                        if (node == endNode)
                        {
                            isSearch = true;
                        }
                    }
                }
            }
        }
        
        if (isSearch)
        {
            Node go = endNode;
            //输出结果
            while (go != startNode)
            {
                //Console.WriteLine("X:" + go.X +"||" + "Y:" + go.Y);
                //Console.WriteLine("权重F="+go.F +" G="+go.G + " H="+go.H);
                go.isLine = true;//标志路径
                go = go.Parent;
            }
            go.isLine = true;
            PrintMap();
        }
        else {
            Console.WriteLine("没有找到!");
        }
    }
    
    public void SetMap(Map m)
    {
        map = m;
    }
    
    //计算F的值
    private void CalF(Node node)
    {
        int offsetX = Math.Abs(node.X - preNode.X);
        int offsetY = Math.Abs(node.Y - preNode.Y);
        if (offsetX + offsetY == 2)
        {
            node.G += 14;
        }
        else {
            node.G += 10;
        }
            
        //计算方向
        int DirectX = Math.Sign(endNode.X - node.X);
        int DirectY = Math.Sign(endNode.Y - node.Y);
        //计算H的X
        int indexX = node.X;
        while (indexX != endNode.X)
        {
            if (map.nodes[indexX][node.Y].isOb == false)
            {
                node.H += 10;
            }
            indexX += DirectX;
        }
        //计算H的Y
        int indexY = node.Y;
        while (indexY != endNode.Y)
        {
            if (map.nodes[node.X][indexY].isOb == false)
            {
                node.H += 10;
            }
            indexY += DirectY;
        }
        node.F = node.G + node.H;
    }
    
    private bool isInClose(Node node)
    {
        //可优化 在节点中保存信息
        bool isIn = false;
        foreach (var i in closeList)
        {
            if (node == i)
                isIn = true;
        }
        return isIn;
    }
    
    private bool isInOpen(Node node)
    {
        //可优化 在节点中保存信息
        bool isIn = false;
        foreach (var i in openList)
        {
            if (node == i)
                isIn = true;
        }
        return isIn;
    }
    
    private Node GetLowerFNode()
    {
        //可优化,用最小堆
        if (openList.Count < 1) {
            Console.WriteLine("怎么会没有了呢!");
            return null;
        }
        Node node = openList[0];
        for (int i = 1; i < openList.Count; i++)
        {
            if (openList[i].F < node.F)
            {
                node = openList[i];
            }
        }
        return node;
    }
    
    public void PrintMap()
    {
        map.PrintMap();
    }
}


首先是将寻路区域分作一个个小方格(我这里是),这是基础

还需要一个评估节点承前启后的消耗,也就是F的计算,G是来到这个节点需要的消耗,H是预计到达目标点的消耗,F是这两个值的和

一个openList记录需要检查周边的节点,closeList记录已经去过的节点(就不用再访问了)


逻辑是这样子的,从openList取出F值最小的加入到closeList中,一开始起点结点会放到openList中

然后遍历这个节点周边的8个节点,障碍物的、在closeList的(访问过的)、斜角边附近有障碍物的都不能通过,直接不做处理;其他的做处理,如果在openList的,要检查当前路径到达这个节点的消耗是否优于以前的,是的话就更换一下F和G值,如果不是,新节点没访问过的,计算一下FGH,加入进去openList。

一直进行下去,直到找到了目标,它要加入到openList,或则openList里面一个元素都没有了。


G - 一个横 一个竖 都是10消耗,一个斜线14消耗(勾股定理)

H - 距离目标横竖偏移和*10(除开障碍物)

F = G + H


首页 我的博客
粤ICP备17103704号