主要参考:unity3D 简单实现A*算法

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using SimpleFrame;

public class GridManager : MonoSingleton<GridManager>
{

    GridController gridController;

    MapData curMapData;
    GridItem[,] curGrids;
    AStarNode[,] curNodes;

    //可连接直线数量(直线行走,转弯次数)
    int curLinkCount = 3;

    GridItem firstGrid;

    void Start()
    {
        gridController = new GridController();
    }

    //开始游戏
    public void GameStart(int mapSize, int spriteStyle)
    {
        if(curMapData != null)
        {
            Debug.Log("Map Data Is Already Has");
            return;
        }
        //获取地图数据
        curMapData = DataManager.Instance.GetMapData(mapSize, spriteStyle);
        //创建地图格子
        curGrids = gridController.CreatGrids(curMapData);
        //根据地图格子信息,创建A*算法所需的每个格子对应的数据
        curNodes = new AStarNode[curGrids.GetLength(0), curGrids.GetLength(1)];
        for (int i = 0; i < curGrids.GetLength(0); i++)
        {
            for (int j = 0; j < curGrids.GetLength(1); j++)
            {
                if (curGrids[i, j].data != null)
                    curNodes[i, j] = new AStarNode(curGrids[i, j].isActive, curGrids[i, j].data.index);
                else
                    curNodes[i, j] = null;
            }
        }

        firstGrid = null;
    }

    //结束游戏
    public void GameEnd()
    {
        //回收地图格子
        gridController.ResetGrids();
        //清空数据
        curMapData = null;
        curGrids = null;
        curNodes = null;
    }

    //格子点击时调用
    public void OnClickGrid(GridItem clickGrid)
    {
        if (!clickGrid.isActive)
            return;

        //第一次点击
        if (firstGrid == null)
        {
            firstGrid = clickGrid;
            //高亮显示选中格子
            firstGrid.HighLight();
            return;
        }

        //两次点击同一个格子
        if (firstGrid == clickGrid)
        {
            //取消高亮显示, 取消选中
            firstGrid.CancelHighLight();
        }
        //点击到匹配的格子, 判断是否可消除
        else if (CheckMoveToGrid(firstGrid, clickGrid))
        {
            //可移动
            firstGrid.OnClickTrue();
            clickGrid.OnClickTrue();
        }
        else
        {
            //不可移动
            firstGrid.OnClickFalse();
            clickGrid.OnClickFalse();
        }
        firstGrid = null;
    }

    // ------

    bool CheckMoveToGrid(GridItem startGrid, GridItem endGrid)
    {
        //刷新当前格子节点数据
        for (int i = 0; i < curGrids.GetLength(0); i++)
        {
            for (int j = 0; j < curGrids.GetLength(1); j++)
            {
                if (curNodes[i, j] != null)
                    curNodes[i, j].isWall = curGrids[i, j].isActive;
            }
        }

        //计算路径
        List<AStarNode> pathNodes = FindNodePath(curNodes[startGrid.data.index.x, startGrid.data.index.y], curNodes[endGrid.data.index.x, endGrid.data.index.y], curNodes);

        if (pathNodes == null || pathNodes.Count == 0)
            return false;

        //计算路径折点
        List<Vector2Int> pathPoints = GetPathPoint(pathNodes);

        //计算折点的格子的世界坐标
        List<Vector3> pathLines = new List<Vector3>();
        for (int i = 0; i < pathPoints.Count; i++)
        {
            pathLines.Add(curGrids[pathPoints[i].x, pathPoints[i].y].transform.position);
        }

        //路径折点多余指定数量, 不可移动
        if (pathPoints.Count - 1 > curLinkCount)
        {
            StartCoroutine(ShowClickLine(pathLines, false));
            return false;
        }

        //可移动
        StartCoroutine(ShowClickLine(pathLines, true));
        return true;
    }

    //连线提示
    IEnumerator ShowClickLine(List<Vector3> pathLines, bool success)
    {
        yield return null;
        //effectController.ShowLinkLine(pathLines, success);
    }

    //计算路径折点
    List<Vector2Int> tmpPointList = new List<Vector2Int>();
    List<Vector2Int> GetPathPoint(List<AStarNode> path)
    {
        tmpPointList.Clear();

        if (path.Count == 1)
            return tmpPointList;

        tmpPointList.Add(path[0].pos);
            
        bool lastDirIsX = path[1].pos.x == path[0].pos.x;

        for (int i = 2; i < path.Count; i++)
        {
            if (path[i].pos.x == path[i - 1].pos.x)
            {
                if (!lastDirIsX)
                {
                    tmpPointList.Add(path[i - 1].pos);
                    lastDirIsX = true;
                }
            }
            else
            {
                //(path[i].pos.x == path[i - 1].pos.x)
                if (lastDirIsX)
                {
                    tmpPointList.Add(path[i - 1].pos);
                    lastDirIsX = false;
                }
            }
        }
        tmpPointList.Add(path[path.Count - 1].pos);

        return tmpPointList;
    }

    #region --- A*算法 ---

    //计算最短有效路径
    List<AStarNode> openList = new List<AStarNode>();
    List<AStarNode> closeList = new List<AStarNode>();
    List<AStarNode> aroundNodes;
    List<AStarNode> FindNodePath(AStarNode startNode, AStarNode endNode, AStarNode[,] allNodes)
    {
        //计算范围内的节点
        openList.Clear();
        //不在计算范围内的节点
        closeList.Clear();

        //添加起点
        openList.Add(startNode);

        AStarNode curNode;
        //从起点开始循环判断
        while (openList.Count > 0)
        {
            //初始当前位置
            curNode = openList[0];
            //计算最优当前位置
            for (int i = 0; i < openList.Count; i++)
            {
                //F:从起点到目标点的移动步数
                //H:从当前位置到目标位置的移动步数
                if (openList[i].CostF < curNode.CostF && openList[i].costH < curNode.costH)
                {
                    curNode = openList[i];
                }
            }
            //锁定当前位置节点
            openList.Remove(curNode);
            closeList.Add(curNode);

            //已经计算到目标点
            if (curNode.Equals(endNode))
            {
                //返回最优路径
                return GetPathWithNode(startNode, endNode);
            }

            //未计算到目标点, 继续

            //获取当前点的周围节点, 在周围节点中查找下一步的最优节点
            aroundNodes = GetAroundNodes(curNode, allNodes);
            for (int i = 0; i < aroundNodes.Count; i++)
            {
                计算到目标点
                //if (aroundNodes[i].Equals(endNode))
                //{
                //    //设置上级节点
                //    aroundNodes[i].lastNode = curNode;
                //    //返回最优路径
                //    return GetPathWithNode(startNode, endNode);
                //}

                //不是目标点, 继续计算, 剔除周围节点不可通过、在不可计算范围内的节点
                if (!aroundNodes[i].isWall && !closeList.Contains(aroundNodes[i]))
                {
                    //计算 G H F
                    //F:从起点到目标点的移动步数

                    //G:从起点到当前位置的移动步数
                    int newCostG = curNode.costG + GetNodesDistance(curNode, aroundNodes[i]);

                    if (newCostG <= aroundNodes[i].costG || !openList.Contains(aroundNodes[i]))
                    {
                        //刷新赋值
                        aroundNodes[i].costG = newCostG;
                        //H:从当前位置到目标位置的移动步数
                        aroundNodes[i].costH = GetNodesDistance(aroundNodes[i], endNode);
                        //设置上级节点
                        aroundNodes[i].lastNode = curNode;
                        //添加到计算范围内
                        if (!openList.Contains(aroundNodes[i]))
                        {
                            openList.Add(aroundNodes[i]);
                        }
                    }
                }
            }
        }
        return null;
    }

    //计算距离
    int GetNodesDistance(AStarNode startNode, AStarNode endNode)
    {
        return Mathf.Abs(startNode.pos.x - endNode.pos.x) + Mathf.Abs(startNode.pos.y - endNode.pos.y);
    }

    //周围节点只取上下左右四个, 不取对角线
    Vector2Int[] aroundPos = { new Vector2Int(0, 1), new Vector2Int(0, -1), new Vector2Int(1, 0), new Vector2Int(-1, 0) };
    //获取周围Node
    List<AStarNode> tmpAroundList = new List<AStarNode>();
    List<AStarNode> GetAroundNodes(AStarNode curNode, AStarNode[,] allNodes)
    {
        tmpAroundList.Clear();
        for (int i = 0; i < aroundPos.Length; i++)
        {
            int x = curNode.pos.x + aroundPos[i].x;
            int y = curNode.pos.y + aroundPos[i].y;

            if (x >= 0 && x < allNodes.GetLength(0) && y >= 0 && y < allNodes.GetLength(1))
            {
                if (allNodes[x, y] != null)
                    tmpAroundList.Add(allNodes[x, y]);
            }
        }
        return tmpAroundList;
    }

    //获取路径(包含起点)
    List<AStarNode> tmpNodePath = new List<AStarNode>();
    List<AStarNode> GetPathWithNode(AStarNode startNode, AStarNode endNode)
    {
        tmpNodePath.Clear();
        if (endNode != null)
        {
            AStarNode temp = endNode;
            while (temp != startNode)
            {
                tmpNodePath.Add(temp);
                temp = temp.lastNode;
            }
            tmpNodePath.Add(startNode);
            tmpNodePath.Reverse();
        }
        return tmpNodePath;
    }

#endregion

}

public class AStarNode
{
    //A*算法节点类

    //是否能通过
    public bool isWall;
    //位置坐标
    public Vector2Int pos;

    //上级节点
    public AStarNode lastNode;

    //从起点到当前位置的移动步数
    public int costG;
    //从当前位置到目标位置的移动步数
    public int costH;

    //从起点到目标点的移动步数
    public int CostF
    {
        get { return costG + costH; }
    }

    public AStarNode(bool _isWall, Vector2Int _pos)
    {
        isWall = _isWall;
        pos = _pos;
    }

    //重写Equals
    public override bool Equals(object obj)
    {
        if (obj is AStarNode)
        {
            AStarNode objNode = (AStarNode)obj;
            return objNode.pos == pos;
        }
        return false;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}