用Unity实现A星寻路算法

13 篇文章 0 订阅

先上图

兰色格子是起点
黄色格子是终点
绿框格子是将要探索的地方
蓝框格子是已经走过的路
最后出现的绿色格子就是算出的路线
在这里插入图片描述

思路

我是看的他的文章, 写的很棒, 深入浅出:
https://blog.csdn.net/zhulichen/article/details/78786493

下面放代码

花了一个中午时间瞎写的, 代码很乱, 知道思路就行

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

public struct AStarDef
{
    public static Color colorDead = new Color(60 / 255f, 60 / 255f, 60 / 255f, 100 / 255f);
    public static Color colorStart = new Color(60 / 255f, 1, 1, 100 / 255f);
    public static Color colorEnd = new Color(1, 1, 60 / 255f, 100 / 255f);
    public static Color colorPath = new Color(60 / 255f, 1, 60 / 255f, 100 / 255f);

    public static Color colorCur = new Color(60 / 255f, 1, 1);
    public static Color colorCanWalk = new Color(60 / 255f, 1, 60 / 255f);
    public static Color colorNull = new Color(200 / 255f, 200 / 255f, 200 / 255f);
    public static Color colorChecked = new Color(60 / 255f, 60 / 255f, 1);
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AStarMgr : MonoBehaviour
{
    public Transform parent;
    public AStarCube prefab;
    float height;
    Vector3 offset;

    AStarCube startCube;
    AStarCube endCube;
    bool isComplete = false;
    bool isAuto = false;
    const int maxX = 10;
    const int maxY = 10;
    const int deadNum = 40;

    //全部方块
    Dictionary<int, AStarCube> allCubes = new Dictionary<int, AStarCube>();
    //每个方块 和它的8个邻居
    Dictionary<AStarCube, List<AStarCube>> allNeighbours = new Dictionary<AStarCube, List<AStarCube>>();
    //每个可走的方块 和它的可走的8个邻居
    Dictionary<AStarCube, List<AStarCube>> availableNeighbours = new Dictionary<AStarCube, List<AStarCube>>();
    //待走的方块
    List<AStarCube> canWalks = new List<AStarCube>();
    //不可走的方块
    List<AStarCube> deads = new List<AStarCube>();

    void Awake()
    {
        height = prefab.GetComponent<RectTransform>().rect.height;
        offset = Vector3.one * height;
        CreateAllCubes();
        Reset();
        StartCoroutine(AutoNext());
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Next();
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            Reset();
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            isAuto = !isAuto;
        }
    }

    void Reset()
    {
        foreach (AStarCube item in allCubes.Values)
        {
            item.Reset();
        }
        CreateDiedCubes();
        CalcuAvailableNeighbours();
        SetStartCubeEndCube();
    }

    void SetStartCubeEndCube()
    {
        isComplete = false;
        startCube = null;
        endCube = null;
        while (startCube == null || endCube == null)
        {
            int x = Random.Range(0, maxX);
            int y = Random.Range(0, maxY);
            int id = GetID(x, y);
            if (allCubes.ContainsKey(id))
            {
                if (!deads.Contains(allCubes[id]))
                {
                    if (startCube == null)
                    {
                        startCube = allCubes[id];
                        deads.Add(allCubes[id]);
                    }
                    else if (endCube == null)
                    {
                        endCube = allCubes[id];
                        deads.Add(allCubes[id]);
                    }
                }
            }
        }
        startCube.SetStart();
        endCube.SetEnd();
        canWalks.Clear();
        canWalks.Add(startCube);
    }

    WaitForSeconds wait = new WaitForSeconds(0.2f);
    WaitForSeconds wait1 = new WaitForSeconds(1);
    IEnumerator AutoNext()
    {
        while (true)
        {
            if (isAuto)
            {
                if (!isComplete)
                {
                    Next();
                }
                else
                {
                    yield return wait1;
                    Reset();
                }
            }
            yield return wait;
        }
    }

    void Next()
    {
        if (isComplete)
        {
            print("已完成, 别再按空格了, 按R键重置");
            return;
        }
        if (canWalks.Count == 0)
        {
            isComplete = true;
            print("无路可走");
            return;
        }
        AStarCube me = null;
        for (int i = 0; i < canWalks.Count; i++)
        {
            //这里用>=, 而不是用>, 是为了优先选择最新的
            if (me == null || me.F >= canWalks[i].F)
            {
                me = canWalks[i];
            }
        }
        canWalks.Remove(me);
        List<AStarCube> ns = availableNeighbours[me];
        if (ns.Count == 0)
        {
            Next();
        }
        for (int i = 0; i < ns.Count; i++)
        {
            AStarCube neighbour = ns[i];
            if (neighbour.isDead)
            {
                continue;
            }
            if (neighbour.isChecked)
            {
                continue;
            }
            neighbour.SetCanWalk();
            me.SetChecked();
            if (!canWalks.Contains(neighbour))
            {
                canWalks.Add(neighbour);
            }
            neighbour.G = me.G + GetDistance(me, neighbour);
            neighbour.H = GetDistance(neighbour, endCube);
            neighbour.F = neighbour.G + neighbour.H;
            if (neighbour.myDir == null)
            {
                neighbour.myDir = me;
            }

            if (neighbour == endCube)
            {
                print("找到了!");
                MarkPath(endCube);
                isComplete = true;
            }
        }
    }

    float GetDistance(AStarCube a, AStarCube b)
    {
        int xDis = Mathf.Abs(a.x - b.x);
        int yDis = Mathf.Abs(a.Y - b.Y);
        int max = xDis > yDis ? xDis : yDis;
        int orthogonal = Mathf.Abs(xDis - yDis);
        int diagonal = max - orthogonal;
        return diagonal * 1.4f + orthogonal;
    }

    void MarkPath(AStarCube item)
    {
        if (item.myDir != null)
        {
            item.myDir.SetPathMark();
            MarkPath(item.myDir);
        }
        else
        {
            return;
        }
    }

    //生成白色方块
    void CreateAllCubes()
    {
        for (int x = 0; x < maxX; x++)
        {
            for (int y = 0; y < maxX; y++)
            {
                AStarCube go = Instantiate<AStarCube>(prefab, new Vector3(x, y) * height + offset, Quaternion.identity, parent);
                go.x = x;
                go.Y = y;
                int id = GetID(x, y);
                allCubes.Add(id, go);
            }
        }
        foreach (KeyValuePair<int, AStarCube> item in allCubes)
        {
            int[] nbsArray = GetMyNeighbours(item.Key);
            List<AStarCube> nbs = new List<AStarCube>();
            for (int i = 0; i < nbsArray.Length; i++)
            {
                if (allCubes.ContainsKey(nbsArray[i]))
                {
                    nbs.Add(allCubes[nbsArray[i]]);
                }
            }
            allNeighbours.Add(item.Value, nbs);
        }
    }

    int[] GetMyNeighbours(int me)
    {
        int y = me % 100;
        int x = (me - y) / 100;

        int next0 = GetID(x - 1, y);
        int next1 = GetID(x + 1, y);
        int next2 = GetID(x, y - 1);
        int next3 = GetID(x, y + 1);
        int next4 = GetID(x - 1, y - 1);
        int next5 = GetID(x - 1, y + 1);
        int next6 = GetID(x + 1, y - 1);
        int next7 = GetID(x + 1, y + 1);

        return new int[] { next0, next1, next2, next3, next4, next5, next6, next7 };
    }

    //生成不可走的方块
    void CreateDiedCubes()
    {
        deads.Clear();
        for (int i = 0; i < deadNum; i++)
        {
            int x = Random.Range(0, maxX);
            int y = Random.Range(0, maxY);
            int id = GetID(x, y);
            if (allCubes.ContainsKey(id))
            {
                allCubes[id].SetDead();
                if (!deads.Contains(allCubes[id]))
                {
                    deads.Add(allCubes[id]);
                }
            }
        }
    }
    void CalcuAvailableNeighbours()
    {
        availableNeighbours.Clear();
        foreach (KeyValuePair<AStarCube, List<AStarCube>> item in allNeighbours)
        {
            if (!item.Key.isDead)
            {
                List<AStarCube> n = new List<AStarCube>();
                for (int i = 0; i < item.Value.Count; i++)
                {
                    if (!item.Value[i].isDead)
                    {
                        n.Add(item.Value[i]);
                    }
                }
                availableNeighbours.Add(item.Key, n);
                CalcuNeighbours(item.Key, n);
            }
        }
    }

    void CalcuNeighbours(AStarCube me, List<AStarCube> ns)
    {
        int x = me.x;
        int y = me.Y;
        int up = GetID(x, y + 1);
        int down = GetID(x, y - 1);
        int left = GetID(x - 1, y);
        int right = GetID(x + 1, y);
        int upleft = GetID(x - 1, y + 1);
        int upright = GetID(x + 1, y + 1);
        int downleft = GetID(x - 1, y - 1);
        int downright = GetID(x + 1, y - 1);
        if (allCubes.ContainsKey(up) && allCubes[up].isDead)
        {
            if (allCubes.ContainsKey(upleft))
            {
                ns.Remove(allCubes[upleft]);
            }
            if (allCubes.ContainsKey(upright))
            {
                ns.Remove(allCubes[upright]);
            }
        }
        if (allCubes.ContainsKey(down) && allCubes[down].isDead)
        {
            if (allCubes.ContainsKey(downleft))
            {
                ns.Remove(allCubes[downleft]);
            }
            if (allCubes.ContainsKey(downright))
            {
                ns.Remove(allCubes[downright]);
            }
        }
        if (allCubes.ContainsKey(left) && allCubes[left].isDead)
        {
            if (allCubes.ContainsKey(upleft))
            {
                ns.Remove(allCubes[upleft]);
            }
            if (allCubes.ContainsKey(downleft))
            {
                ns.Remove(allCubes[downleft]);
            }
        }
        if (allCubes.ContainsKey(right) && allCubes[right].isDead)
        {
            if (allCubes.ContainsKey(upright))
            {
                ns.Remove(allCubes[upright]);
            }
            if (allCubes.ContainsKey(downright))
            {
                ns.Remove(allCubes[downright]);
            }
        }
    }

    int GetID(int x, int y)
    {
        return x * 100 + y;
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;



public class AStarCube : MonoBehaviour
{
    public AStarCube myDir;
    public bool isDead;
    public bool isChecked;

    public Image bg;
    public Text textXY;
    public Text textG;
    public Text textH;
    public Text textF;
    public Image dead;

    public int x;

    private int y;
    public int Y
    {
        get { return y; }
        set
        {
            y = value;
            textXY.text = "(" + x.ToString() + "," + y.ToString() + ")";
        }
    }

    private float g;
    public float G
    {
        get { return g; }
        set
        {
            g = value;
            textG.text = g.ToString("F1");
        }
    }

    private float h;
    public float H
    {
        get { return h; }
        set
        {
            h = value;
            textH.text = h.ToString("F1");
        }
    }

    private float f;
    public float F
    {
        get { return f; }
        set
        {
            f = value;
            textF.text = f.ToString("F1");
        }
    }


    public void SetStart()
    {
        dead.gameObject.SetActive(true);
        dead.color = AStarDef.colorStart;
    }
    public void SetEnd()
    {
        dead.gameObject.SetActive(true);
        dead.color = AStarDef.colorEnd;
    }
    public void SetCanWalk()
    {
        bg.color = AStarDef.colorCanWalk;
    }

    public void SetDead()
    {
        dead.gameObject.SetActive(true);
        dead.color = AStarDef.colorDead;
        isDead = true;
    }

    public void SetChecked()
    {
        bg.color = AStarDef.colorChecked;
        isChecked = true;
    }
    public void SetCur()
    {
        bg.color = AStarDef.colorCur;
    }

    public void SetPathMark()
    {
        dead.gameObject.SetActive(true);
        dead.color = AStarDef.colorPath;
    }
    public void Reset()
    {
        isChecked = false;
        isDead = false;
        dead.gameObject.SetActive(false);
        bg.color = AStarDef.colorNull;
        G = 0;
        H = 0;
        F = 0;
        myDir = null;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
A*寻路算法是一种常用的路径规划算法,它在地图上寻找两个点之间的最短路径。在Unity中,我们可以使用以下步骤实现A*寻路算法: 1. 创建地图:我们需要创建一个网格地图,其中每个格子都代表着地图上的一个点。每个点都有一个坐标、可通过性和一些其他属性。 2. 创建节点类:我们需要创建一个节点类,用于保存每个点的信息。每个节点都有一个父节点、位置、代价和其他属性。 3. 创建Open和Close列表:我们需要创建两个列表,一个是Open列表,用于存储待搜索的节点,另一个是Close列表,用于存储已搜索过的节点。 4. 初始化起点和终点:我们需要初始化起点和终点,并将起点加入到Open列表中。 5. 搜索路径:我们需要重复以下步骤直到找到终点或者Open列表为空: a. 从Open列表中选取代价最小的节点作为当前节点。 b. 将当前节点从Open列表中删除,并将其加入到Close列表中。 c. 检查当前节点是否为终点,如果是则返回路径。 d. 遍历当前节点的相邻节点,计算它们的代价,并将它们加入到Open列表中。 6. 返回路径:如果找到了终点,则从终点开始沿着父节点一直往回走,直到回到起点。这样就得到了一条最短路径。 以下是示例代码: ```csharp using System.Collections; using System.Collections.Generic; using UnityEngine; public class AStar : MonoBehaviour { public LayerMask wallMask; public Vector2 gridSize; public float nodeRadius; Node[,] grid; float nodeDiameter; int gridSizeX, gridSizeY; void Start() { nodeDiameter = nodeRadius * 2; gridSizeX = Mathf.RoundToInt(gridSize.x / nodeDiameter); gridSizeY = Mathf.RoundToInt(gridSize.y / nodeDiameter); CreateGrid(); } void CreateGrid() { grid = new Node[gridSizeX, gridSizeY]; Vector3 worldBottomLeft = transform.position - Vector3.right * gridSize.x / 2 - Vector3.forward * gridSize.y / 2; for (int x = 0; x < gridSizeX; x++) { for (int y = 0; y < gridSizeY; y++) { Vector3 worldPoint = worldBottomLeft + Vector3.right * (x * nodeDiameter + nodeRadius) + Vector3.forward * (y * nodeDiameter + nodeRadius); bool walkable = !Physics.CheckSphere(worldPoint, nodeRadius, wallMask); grid[x, y] = new Node(walkable, worldPoint, x, y); } } } public List<Node> GetNeighbours(Node node) { List<Node> neighbours = new List<Node>(); for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { if (x == 0 && y == 0) continue; int checkX = node.gridX + x; int checkY = node.gridY + y; if (checkX >= 0 && checkX < gridSizeX && checkY >= 0 && checkY < gridSizeY) { neighbours.Add(grid[checkX, checkY]); } } } return neighbours; } public Node NodeFromWorldPoint(Vector3 worldPosition) { float percentX = (worldPosition.x + gridSize.x / 2) / gridSize.x; float percentY = (worldPosition.z + gridSize.y / 2) / gridSize.y; percentX = Mathf.Clamp01(percentX); percentY = Mathf.Clamp01(percentY); int x = Mathf.RoundToInt((gridSizeX - 1) * percentX); int y = Mathf.RoundToInt((gridSizeY - 1) * percentY); return grid[x, y]; } public List<Node> FindPath(Vector3 startPos, Vector3 targetPos) { Node startNode = NodeFromWorldPoint(startPos); Node targetNode = NodeFromWorldPoint(targetPos); List<Node> openSet = new List<Node>(); HashSet<Node> closedSet = new HashSet<Node>(); openSet.Add(startNode); while (openSet.Count > 0) { Node currentNode = openSet[0]; for (int i = 1; i < openSet.Count; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } openSet.Remove(currentNode); closedSet.Add(currentNode); if (currentNode == targetNode) { return RetracePath(startNode, targetNode); } foreach (Node neighbour in GetNeighbours(currentNode)) { if (!neighbour.walkable || closedSet.Contains(neighbour)) { continue; } int newCostToNeighbour = currentNode.gCost + GetDistance(currentNode, neighbour); if (newCostToNeighbour < neighbour.gCost || !openSet.Contains(neighbour)) { neighbour.gCost = newCostToNeighbour; neighbour.hCost = GetDistance(neighbour, targetNode); neighbour.parent = currentNode; if (!openSet.Contains(neighbour)) { openSet.Add(neighbour); } } } } return null; } List<Node> RetracePath(Node startNode, Node endNode) { List<Node> path = new List<Node>(); Node currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode); currentNode = currentNode.parent; } path.Reverse(); return path; } int GetDistance(Node nodeA, Node nodeB) { int distX = Mathf.Abs(nodeA.gridX - nodeB.gridX); int distY = Mathf.Abs(nodeA.gridY - nodeB.gridY); if (distX > distY) { return 14 * distY + 10 * (distX - distY); } return 14 * distX + 10 * (distY - distX); } public class Node { public bool walkable; public Vector3 worldPosition; public int gridX; public int gridY; public int gCost; public int hCost; public Node parent; public Node(bool _walkable, Vector3 _worldPos, int _gridX, int _gridY) { walkable = _walkable; worldPosition = _worldPos; gridX = _gridX; gridY = _gridY; } public int fCost { get { return gCost + hCost; } } } } ``` 在上面的代码中,我们首先创建了一个网格地图,并在其中创建了节点类。然后,我们实现了A*算法的核心部分,并将其用于在网格地图上搜索路径。在搜索结束后,我们返回了一条最短路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值