用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
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值