Unity A*算法

概念

A*算法的思路,简单来说就是:

从每一个已搜索的位置中,找到一个代价最小的位置,再从这个位置继续搜索,直到找到终点。

开放列表:可以理解为待搜索列表

关闭列表:可以理解为已搜索列表

启发函数:用于计算一个点的总代价,以便找到最小代价的点,以启发后续的优先搜索方向,F=G+H

F:一个点的总代价

G:从起点走到该点的代价。如果是四邻域内,就在父节点的G值基础上加上10。如果是八邻域且非四邻域,就在父节点G值基础上加上14(10*1.414)。

H:从该点走到终点的估计距离。本文使用曼哈顿距离,就是该点与终点在水平方向的距离与垂直方向距离的和。

算法

将起点加入待搜索的列表

然后遍历待搜索列表(open)中的全部元素,直到列表空为止

每次遍历从中取一个总代价(F值)最小的点,把它从待搜索列表中移除,再看看这个点是不是终点。如果是,返回这个点。如果不是,则把它加入已搜索列表(close),再遍历它的八邻域。

遍历八邻域时,如果是障碍物或者在边界之外,不再遍历,如果在已搜索列表中,也不再遍历。

如果不在待搜索列表中,则记录其父节点,并计算其G、H、和F值,再把它加入待搜索列表。

如果在待搜索列表中,则比较其G值与新的G值谁更小,如果有更小的G值,则更新父节点,重新计算G值和F值。

效果

可以一步一步执行

也可以一次就执行完毕

 

如果起点或终点设置在了障碍物上,可以用鼠标左键重新选择。

显示层代码

AstarGUI.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using AStar;
using System.Data;
using System.Text;
using System.IO;
using UnityEditor;
using System;

public class AstarGUI : MonoBehaviour
{
    [Header("配置")]
    public RunMode runMode = RunMode.ByStep;
    public enum RunMode
    {
        Directly,
        ByStep,
    }

    public Vector2 Size;

    public Vector2 startPoint;
    public Vector2 endPoint;

    public GameObject canvas;
    public Texture2D tilePic;

    public int[,] map;              //0 路 1 墙
    public GameObject[,] tiles;
    public string mapReadPath = "map";     
    public string mapOutputName = "newmap";

    Vector3 originPos = new Vector3(100, 1100, 0);
    Vector3 cellScale = new Vector3(0.8f, 0.8f, 0.8f);
    public AStar.AStar astar;
    [Header("Read Only")]
    public int step = 0;

    void Start()
    {
        Init();
    }

    public void Init()
    {
        instance = this;
        font = Resources.GetBuiltinResource<Font>("Arial.ttf");
    }

    public void DisplayDirectly()
    {
        astar.FindPath(new AStar.Cell((int)startPoint.x, (int)startPoint.y), new AStar.Cell((int)endPoint.x, (int)endPoint.y));
        MarkValue();
        MarkKeyCells();
        SaveMapToCsv();
    }

    public void DisplayByStep(int step)
    {
        astar.FindPathByStep(startPoint, endPoint, step);
        MarkValue();
        MarkKeyCells();
    }

    void Update()
    {
        //RefreshPos();

        if (Input.GetKeyDown(KeyCode.N))
        {
            Debug.Log(step);
            DisplayByStep(step);
            step++;
        }

        if (Input.GetKeyDown(KeyCode.O))
        {
            SetRandomObstacle();
            MarkBasicMap();
        }

        if (Input.GetKeyDown(KeyCode.C))
        {
            CreateMapByGivenSize();
            DrawTheMap();
            SetRandomObstacle();
            MarkBasicMap();
        }

        if (Input.GetMouseButtonDown(0) && setWhich != -1)
            SetStartAndEndPoint();

        if (Input.GetMouseButtonDown(0) && setWhich == -1)
            SetObstacle();
    }

    public void DrawTheMap()
    {    
        //创建画布
        if (canvas == null)
        {
            canvas = new GameObject("Canvas");
            canvas.AddComponent<Canvas>().renderMode = RenderMode.ScreenSpaceOverlay;
            canvas.AddComponent<CanvasScaler>();
            canvas.GetComponent<CanvasScaler>().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
            canvas.GetComponent<CanvasScaler>().referenceResolution = new Vector2(2400, 1200);
            canvas.GetComponent<CanvasScaler>().screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            canvas.GetComponent<CanvasScaler>().matchWidthOrHeight = 0;

            canvas.AddComponent<GraphicRaycaster>();
        }

        //清空地图
        if (canvas.transform.childCount > 0) ClearChilds(canvas.transform);

        //绘制网格地图
        for (int x = 0; x < map.GetLength(0); x++)
            for (int y = 0; y < map.GetLength(1); y++)
            {
                //地图坐标系转换为Unity坐标系
                float posX = y;
                float posY = -x;
                Vector3 pos = new Vector3(posX, posY, 0);
                tiles[x, y] = new GameObject(x + " " + y);
                tiles[x, y].transform.SetParent(canvas.transform);
                tiles[x, y].transform.position = originPos + pos * 100;
                tiles[x, y].AddComponent<Image>();
                tiles[x, y].GetComponent<Image>().sprite = Sprite.Create(tilePic, new Rect(0, 0, tilePic.width, tilePic.height), new Vector2(0.5f, 0.5f));
                tiles[x, y].transform.localScale = cellScale;
                if (map[x, y] == 1) MarkPoint(x, y, Color.black);

                //使用Selectable接收点击事件
                tiles[x, y].AddComponent<Selectable>();
                //使用物理碰撞体接收点击事件并不明智
                //tiles[x, y].AddComponent<BoxCollider2D>().size = new Vector2(100,100);
                //tiles[x, y].AddComponent<GraphicRaycaster>();
            }
    }

    public void ErrorCheck()
    {
        if (!astar.isReachable(startPoint))
        {
            Debug.LogError("起点或终点设置有误,请重新设置");
            MarkPoint(startPoint, Color.red);
            setWhich = 0;
        }

        if (!astar.isReachable(endPoint))
        {
            Debug.LogError("起点或终点设置有误,请重新设置");
            MarkPoint(endPoint, Color.red);
            setWhich = 0;
        }
    }

    public void ClearObstacle()
    {
        Array.Clear(map, 0, map.Length);
    }

    //清除子物体
    public void ClearChilds(Transform parent)
    {
        if (parent.childCount > 0)
        {
            int totalCount = parent.childCount;
            for (int i = 0; i < totalCount; i++)
            {
                DestroyImmediate(parent.GetChild(0).gameObject);
            }
        }
    }

    Font font;
    void MarkValue()
    {
        for (int x = 0; x < map.GetLength(0); x++)
            for (int y = 0; y < map.GetLength(1); y++)
            {
                Cell c = astar.Get(x, y);
                if (c == null) continue;
                if (c.F != null)//c.F != 0
                {
                    if (tiles[x, y].GetComponentInChildren<Text>() == null)
                    {
                        GameObject obj = new GameObject("Text");
                        obj.AddComponent<Text>();
                        obj.GetComponent<Text>().color = Color.black;
                        obj.GetComponent<Text>().font = font;
                        obj.transform.SetParent(tiles[x, y].transform);
                        obj.transform.localPosition = new Vector3(40, -30, 0);
                    }
                    Transform text = tiles[x, y].transform.GetChild(0);
                    text.GetComponent<Text>().text = c.F + "\n" + c.G + "\n" + c.H;
                }
            }
    }

    public static AstarGUI instance;
    public void MarkPoint(int x, int y, Color color)
    {
        tiles[x, y].GetComponent<Image>().color = color;
    }

    public void MarkPoint(Cell c, Color color)
    {
        tiles[c.x, c.y].GetComponent<Image>().color = color;
    }

    public void MarkPoint(Vector2 p, Color color)
    {
        tiles[(int)p.x, (int)p.y].GetComponent<Image>().color = color;
    }

    public void MarkKeyCells()
    {
        //绘制开放列表
        foreach (var i in astar.openList)
        {
            MarkPoint(i, Color.cyan);
        }
        //绘制关闭列表
        foreach (var i in astar.closeList)
        {
            MarkPoint(i, Color.grey);
        }
        //绘制最小F点
        MarkPoint(astar.openList.GetCellwithMinF(), Color.red);
        //绘制路径
        MarkPath();
        //绘制起点、终点
        MarkPoint(startPoint, Color.yellow);
        MarkPoint(endPoint, Color.yellow);
    }

    //绘制障碍
    public void MarkBasicMap()
    {
        //绘制路与障碍
        for (int x = 0; x < map.GetLength(0); x++)
        {
            for (int y = 0; y < map.GetLength(1); y++)
            {
                if (map[x, y] == 1) MarkPoint(x, y, Color.black);
                else MarkPoint(x, y, Color.white);
            }
        }

        //绘制起点终点
        MarkPoint(startPoint, Color.yellow);
        MarkPoint(endPoint, Color.yellow);

        ErrorCheck();
    }

    public void MarkPath()
    {
        var parent = astar.openList.GetCellwithMinF();
        while (parent != null)
        {
            MarkPoint(parent, Color.green);
            parent = parent.parent;
        }
    }

    /// <summary>
    /// 用于调整地图的位置
    /// </summary>
    void RefreshPos()
    {
        for (int x = 0; x < map.GetLength(0); x++)//数组第一维是各行(x)的头指针
            for (int y = 0; y < map.GetLength(1); y++)//数组第二维是各行的各个元素(列(y))
            {
                float posX = x;
                float posY = -y;
                Vector3 pos = new Vector3(posX, posY, 0);
                tiles[x, y].transform.position = originPos + pos * 100;
            }
    }

    public void CreateMapByPraseCSV()
    {
        string text = System.IO.File.ReadAllText(Application.streamingAssetsPath + "/"+mapReadPath+".csv");
        //TextAsset ta = Resources.Load<TextAsset>(mapReadPath);//不能有后缀名.csv
        //按行读取
        if (text == null) Debug.LogError("读取失败");
        string[] lines = text.Split('\n');

        map = null;
        tiles = null;

        for (int x = 0; x < lines.Length; x++)
        {
            if (string.IsNullOrEmpty(lines[x])) continue;
            //移除回车
            lines[x] = lines[x].Replace("\r", "");
            //按逗号解析
            string[] linePrased = lines[x].Split(',');

            //注:地图文档最后一行不能是空行
            if(map == null) map = new int[lines.Length, linePrased.Length];  //行,列
            if(tiles == null) tiles = new GameObject[lines.Length, linePrased.Length];

            for (int y = 0; y < linePrased.Length; y++)
            {
                //转换为整形
                map[x, y] = int.Parse(linePrased[y]);
            }
        }
    }
    //注:表中每行最后一位无逗号

    public void CreateMapByGivenSize()
    {
        map = new int[(int)Size.x, (int)Size.y];
        tiles = new GameObject[(int)Size.x, (int)Size.y];
    }

    public void SetRandomObstacle()
    {
        for (int x = 0; x < map.GetLength(0); x++)
        {
            for (int y = 0; y < map.GetLength(1); y++)
            {
                var p = new Vector2(x, y);
                if (p == startPoint || p == endPoint) continue;

                map[x, y] = UnityEngine.Random.Range(0, 100) % 10 == 0 ? 1 : 0;     //障碍块出现率 0.1
            }
        }
    }

    public void SaveMapToCsv()
    {
        //读取地图数据到字符串
        StringBuilder stringBuilder = new StringBuilder();
        for (int x = 0; x < map.GetLength(0); x++)
        {
            for (int y = 0; y < map.GetLength(1); y++)
            {
                if(y != map.GetLength(1)-1)
                    stringBuilder.Append(map[x, y] + ",");
                else
                    stringBuilder.Append(map[x, y]);
            }
            if(x != map.GetLength(0) - 1) stringBuilder.Append("\r\n");
        }

        //创建目录
        if (Directory.Exists(Application.streamingAssetsPath) == false)
        {
            Directory.CreateDirectory(Application.streamingAssetsPath);
        }

        //写入文件
        using (FileStream fileStream = new FileStream(Application.streamingAssetsPath + "\\" + mapOutputName + ".csv", FileMode.Create, FileAccess.Write))
        {
            using (TextWriter textWriter = new StreamWriter(fileStream, Encoding.UTF8))
            {
                textWriter.Write(stringBuilder.ToString());
            }
        }
    }

    [HideInInspector]
    public int setWhich = 2;
    public void SetStartAndEndPoint()
    {
        var obj = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
        Debug.Log(obj.transform.name);

        string[] pos = obj.transform.name.Split(' ');
        if (pos.Length != 2) return;

        if (setWhich >= 2) return;

        if(setWhich %2 == 0)
        {
            startPoint.x = int.Parse(pos[0]);
            startPoint.y = int.Parse(pos[1]);
            MarkBasicMap();
        }
        else if (setWhich % 2 == 1)
        {
            endPoint.x = int.Parse(pos[0]);
            endPoint.y = int.Parse(pos[1]);
            MarkBasicMap();
        }
        setWhich++;
    }

    public void SetObstacle()
    {
        var obj = UnityEngine.EventSystems.EventSystem.current.currentSelectedGameObject;
        Debug.Log(obj.transform.name);

        string[] pos = obj.transform.name.Split(' ');
        if (pos.Length != 2) return;

        int x = int.Parse(pos[0]);
        int y = int.Parse(pos[1]);

        if (map[x, y] == 0) map[x, y] = 1;
        else if(map[x, y] == 1) map[x, y] = 0;

        MarkBasicMap();
    }
}

//通过特性指定按钮在哪个组件下
[CustomEditor(typeof(AstarGUI))]
[CanEditMultipleObjects]
public class InspectorExtension : Editor
{
    public override void OnInspectorGUI()
    {
        //这个是绘制原生的GUI
        base.OnInspectorGUI();

        GUILayout.Space(10f);

        if (GUILayout.Button("创建地图"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.CreateMapByGivenSize();
            drawMap.astar = new AStar.AStar(drawMap.map);
            drawMap.DrawTheMap();
            drawMap.MarkBasicMap();
        }

        if (GUILayout.Button("加载地图"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.CreateMapByPraseCSV();
            drawMap.astar = new AStar.AStar(drawMap.map);
            drawMap.DrawTheMap();
            drawMap.MarkBasicMap();
        }

        if (GUILayout.Button("保存地图"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.SaveMapToCsv();
        }

        if (GUILayout.Button("清空地图"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.ClearChilds(drawMap.canvas.transform);
        }

        GUILayout.Space(10f);

        if (GUILayout.Button("设置起点、终点"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.setWhich = 0;
        }

        if (GUILayout.Button("设置障碍"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.setWhich = -1;
        }

        if (GUILayout.Button("随机设置障碍"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.SetRandomObstacle();
            drawMap.MarkBasicMap();
        }

        if (GUILayout.Button("清除障碍块"))
        {
            AstarGUI drawMap = this.target as AstarGUI;
            drawMap.ClearObstacle();
            drawMap.MarkBasicMap();
        }

        GUILayout.Space(10f);

        if (GUILayout.Button("执行"))
        {
            AstarGUI drawMap = this.target as AstarGUI;

            if(drawMap.runMode == AstarGUI.RunMode.ByStep)
            {
                Debug.Log(drawMap.step);
                drawMap.DisplayByStep(drawMap.step);
                drawMap.step++;
            }
            else
            {
                drawMap.DisplayDirectly();
            }
        }
    }
}

//数组下标需要统一标准
//地图数据是从0开始存的
//x为横坐标,表示行; y为纵坐标,表示列

计算层代码

AStar.cs

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace AStar
{
    public class AStar
    {
        public const int obliqueUnit = 14;                  //斜向代价
        public const int unit = 10;                     //横向代价
        public int[,] map { get; private set; }   //迷宫数组
        public List<Cell> closeList;
        public List<Cell> openList;

        public AStar(int[,] map)
        {
            this.map = map;
            openList = new List<Cell>(this.map.Length);
            closeList = new List<Cell>(this.map.Length);
        }

        public Cell FindPath(Cell start, Cell end, int step = -1)//可选参数
        {
            if (!isReachable(start) || !isReachable(end))
            {
                Debug.LogError("起点和终点不能设在障碍块上");
                return null;
            }

            //Debug.Log(map.GetLength(0)+"## "+map.GetLength(1));

            openList.Add(start);
            AstarGUI.instance.MarkPoint(start, Color.yellow);
            AstarGUI.instance.MarkPoint(end, Color.yellow);

            int n = 0;
            while (openList.Count != 0)
            {
                if (n == step) 
                {
                    //返回F值最小的点,通过parent可以获取到路径
                    return openList.GetCellwithMinF();
                }
                n++;

                var pointWithMinF = openList.GetCellwithMinF();
                openList.Remove(pointWithMinF);//??

                //如果成功找到解:F值最小的点是终点
                if (pointWithMinF.Equal(end))
                {
                    //返回终点
                    //逆溯路径
                    //倒序输出
                    return openList.Get(end);
                }
                //不是终点
                else
                {
                    //加入关闭列表,遍历时忽略该点
                    closeList.Add(pointWithMinF);

                    AstarGUI.instance.MarkPoint(pointWithMinF, Color.gray);

                    //将下一步可到达的位置加入openList,并检查记录的最短路径G是否需要更新,记录最短路径经过的上一个点
                    //维护当前已知的最短G,如果未遍历或需更新
                    TraverseNeighbour(pointWithMinF,end);
                }

                if (openList.Get(end) != null)
                {
                    return openList.Get(end);
                }
            }
            return openList.Get(end);
        }

        public Cell FindPathByStep(Vector2 s, Vector2 e, int step)//可选参数
        {
            Cell start = new Cell(s);
            Cell end = new Cell(e);

            if (!isReachable(start) || !isReachable(end))
            {
                Debug.LogError("起点和终点不能设在障碍块上");
                return null;
            }

            //Debug.Log(map.GetLength(0)+"## "+map.GetLength(1));

            if(step==0)
            {
                openList.Add(start);
                AstarGUI.instance.MarkPoint(start, Color.yellow);
                AstarGUI.instance.MarkPoint(end, Color.yellow);
            }

            if (openList.GetCellwithMinF().Equal(end))
                return end;

            if (openList.Count == 0)
            {
                openList.Get(end);
            }
            else
            {
                var pointWithMinF = openList.GetCellwithMinF();
                openList.Remove(pointWithMinF);//??

                //如果成功找到解:F值最小的点是终点
                if (pointWithMinF.Equal(end))
                {
                    return openList.Get(end);
                }
                //不是终点
                else
                {
                    //加入关闭列表,遍历时忽略该点
                    closeList.Add(pointWithMinF);
                    //将下一步可到达的位置加入openList,并检查记录的最短路径G是否需要更新,记录最短路径经过的上一个点
                    //维护当前已知的最短G,如果未遍历或需更新
                    TraverseNeighbour(pointWithMinF, end);
                }

                if (openList.Get(end) != null)
                {
                    return openList.Get(end);
                }
                return pointWithMinF;
            }
            return null;
        }

        public void TraverseNeighbour(Cell cellWithMinF,Cell end)
        {
            for (int x = cellWithMinF.x - 1; x <= cellWithMinF.x + 1; x++)
            {
                for (int y = cellWithMinF.y - 1; y <= cellWithMinF.y + 1; y++)
                {
                    //如果是障碍物,不再遍历
                    if (!isReachable(x,y))
                    {
                        continue;
                    }

                    //如果已在关闭列表中,不再遍历
                    if (closeList.Contains(x, y))
                    {
                        continue;
                    }

                    var p = openList.Get(x, y) == null ? new Cell(x, y) : openList.Get(x, y);

                    //如果未遍历
                    if (!openList.Contains(x,y))
                    {
                        p.parent = cellWithMinF;
                        p.G = CalG(p);
                        CalH(end, p);
                        CalF(p);
                        openList.Add(p);
                        //DrawMap.instance.MarkPoint(p.x, p.y, Color.cyan);
                    }
                    //需更新
                    else
                    {
                        //用G值检查这条路径是否更好
                        if (p.G > CalNewG(p, cellWithMinF))//这里不能写CalG(p),因为要拿minF计算,而不是父节点
                        {
                            p.parent = cellWithMinF;
                            p.G = CalG(p);
                            p.F = CalF(p);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 判断某个格子是否为路
        /// </summary>
        /// <param name="x">行</param>
        /// <param name="y">列</param>
        /// <returns></returns>
        public bool isReachable(int x,int y)
        {
            //超越地图边界
            if (x < 0 || y < 0 || x > map.GetLength(0)-1 || y > map.GetLength(1)-1)
            {
                return false;
            }

            //判断是否是障碍物
            return map[x, y] == 0;
        }

        public bool isReachable(Cell n)
        {
            //超越地图边界
            if (n.x < 0 || n.y < 0 || n.x > map.GetLength(0) - 1 || n.y > map.GetLength(1) - 1)
            {
                return false;
            }

            //判断是否是障碍物
            return map[n.x, n.y] == 0;
        }

        public bool isReachable(Vector2 n)
        {
            //超越地图边界
            if (n.x < 0 || n.y < 0 || n.x > map.GetLength(0) - 1 || n.y > map.GetLength(1) - 1)
            {
                return false;
            }

            //判断是否是障碍物
            return map[(int)n.x, (int)n.y] == 0;
        }

        /// <summary>
        /// 计算s-n的移动代价
        /// </summary>
        /// <param name="start"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private int CalG(Cell n)
        {
            //起点G值为0
            if (n.parent == null)
                return 0;

            //判断节点(可能是父节点的节点)到当前节点的方向
            if (Math.Abs(n.parent.x - n.x) + Math.Abs(n.parent.y - n.y) == 1)//四邻域和为1,八邻域除四邻域外和为2
            {
                //直线单位距离+10
                return n.parent.G + unit;
            }
            else
            {
                //斜线单位距离+14
                return n.parent.G + obliqueUnit;
            }
        }

        private int CalNewG(Cell n, Cell cellWithMinF)
        {
            //起点G值为0
            if (n.parent == null)
                return 0;

            //判断节点(可能是父节点的节点)到当前节点的方向
            if (Math.Abs(cellWithMinF.x - n.x) + Math.Abs(cellWithMinF.y - n.y) == 1)//四邻域和为1,八邻域除四邻域外和为2
            {
                //直线单位距离+10
                return cellWithMinF.G + unit;
            }
            else
            {
                //斜线单位距离+14
                return cellWithMinF.G + obliqueUnit;
            }
        }

        /// <summary>
        /// 计算n-e的预估成本——曼哈顿距离(对于每个点,H是一开始就固定的)
        /// </summary>
        /// <param name="end"></param>
        /// <param name="n"></param>
        /// <returns></returns>
        private int CalH(Cell end, Cell n)
        {
            //忽略障碍物,使用曼哈顿距离
            n.H = (Math.Abs(end.x - n.x) + Math.Abs(end.y - n.y)) * unit;
            return n.H;
        }

        int CalF(Cell n)
        {
            n.F = n.G + n.H;
            return n.F;
        }

        /// <summary>
        /// 判断某点是路
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        private bool isRoad(int x, int y)
        {
            return map[x, y] == 0;
        }

        public Cell Get(int x,int y)
        {
            return closeList.Get(x, y) == null ? openList.Get(x, y) : closeList.Get(x, y);
            //return closeList.Get(x, y);
        }
    }

    public class Cell
    {
        /// <summary>
        /// 行(对应Unity的y轴负方向)
        /// </summary>
        public int x { get; set; }
        /// <summary>
        /// 列(对应Unity的x轴)
        /// </summary>
        public int y { get; set; }
        public Cell parent { get; set; }
        public int F { get; set; }  //F=G+H
        public int G { get; set; }
        public int H { get; set; }

        public Cell(int x, int y)
        {
            this.x = x;
            this.y = y;
        }

        public Cell(Vector2 p)
        {
            this.x = (int)p.x;
            this.y = (int)p.y;
        }

        public void CalF()
        {
            this.F = this.G + this.H;
        }

        /// <summary>
        /// 比较两个点是否相等
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public bool Equal(Cell p)
        {
            if (p.x == this.x && p.y == this.y)
                return true;
            else return false;
        }
    }

    //对 List<Point> 的一些扩展方法
    public static class ListHelper
    {
        /// <summary>
        /// 判断某点是否存在
        /// </summary>
        /// <param name="cells"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static bool Contains(this List<Cell> cells, Cell cell)
        {
            foreach (Cell c in cells)
                if ((c.x == cell.x) && (c.y == cell.y))
                    return true;
            return false;
        }


        /// <summary>
        /// 判断某点是否存在
        /// </summary>
        /// <param name="cells"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public static bool Contains(this List<Cell> cells, int x, int y)
        {
            foreach (Cell c in cells)
            {
                if ((c.x == x) && (c.y == y))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// 返回列表中F值最小的点(排在列表首位)
        /// </summary>
        /// <param name="cells"></param>
        /// <returns></returns>
        public static Cell GetCellwithMinF(this List<Cell> cells)
        {
            //对points升序排列
            cells = cells.OrderBy(c => c.F).ToList();
            //返回最小的那个
            return cells[0];
        }

        public static void Add(this List<Cell> cells, int x, int y)
        {
            Cell cell = new Cell(x, y);
            cells.Add(cell);
        }

        public static Cell Get(this List<Cell> cells, int x, int y)
        {
            foreach (Cell c in cells)
                if ((c.x == x) && (c.y == y))
                    return c;
            return null;
        }

        public static Cell Get(this List<Cell> cells, Cell cell)
        {
            foreach (Cell c in cells)
                if ((c.x == cell.x) && (c.y == cell.y))
                    return c;
            return null;
        }

        /// <summary>
        /// 移除指定的点
        /// </summary>
        /// <param name="cells"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public static void Remove(this List<Cell> cells, int x, int y)
        {
            foreach (Cell c in cells)
            {
                if (c.x == x && c.y == y)
                    cells.Remove(c);
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_43673589/article/details/127233539