关于A*寻路在Unity3D里面的简单应用

这是之前做过的一个关于A*寻路算法的小Demo ,现在分享出来 大家一起学习!


注意:在运行测试的时候 请将Cube的Scale设置成(0.9,0.9,0.9)

   在运行的时候 行列尽量设置的小点 不要超过35  (我设置40*40(1600个盒子)已经报出内存缓存不够的错误)

这是一种代价值的计算方式,也就是计算所需点的消耗值F 然后取出代价最低的点依次进行计算。


实现步骤:

G值和H值的计算方式

F=G+H
G=父格子的G+(当前位置在父格子的正【前后左右】+10否则+14)
H=从起点到重点的水平距离加上竖直距离*10

【-1】先把起点加入开启列表
【0】然后计算周围的非障碍非关闭格子的F值  计算过程=【1】【2】【3】
【1】计算G值 判断是否更新G值 (新G值<原G值||原G值==0)更新
【2】计算H F值 并更新
【3】判断如过当前计算的格子不在开启列表中 加入开启列表
【4】将当前父格子(就是中心点的盒子 由中心开启周围的盒子的那个盒子)移除开启列表 加入关闭列表
【5】按照F值从小到大的顺序进行排序
【6】判断当前最小的F值是不是终点 不是的话 回到【0】步,是的话 寻路成功

【7】取出路线:从重点死循环取出父格子,判断是起点跳出循环

下面是具体代码 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public enum CubeType
{
    /// <summary>
    /// 正常
    /// </summary>
    Normal,
    /// <summary>
    /// 障碍
    /// </summary>
    Barrier,
    /// <summary>
    /// 开始
    /// </summary>
    Start,
    /// <summary>
    /// 结束
    /// </summary>
    End
}
public class AStar : MonoBehaviour {

    /// <summary>
    /// 是否可以斜着走
    /// </summary>
    public bool MayInclinedToWalk;
    /// <summary>
    /// 生成路障的几率
    /// </summary>
    public int jiLv;
    /// <summary>
    /// 行
    /// </summary>
    public int Row=20;
    /// <summary>
    /// 列
    /// </summary>
    public int Line=20;
    /// <summary>
    /// 用来生成路面的Cube
    /// </summary>
    public GameObject Prefab;
    /// <summary>
    /// 存储路面信息
    /// </summary>
    public Grid[,] Cubes;
    /// <summary>
    /// 开始点的位置
    /// </summary>
    public Vector3 start;
    /// <summary>
    /// 结束点的位置
    /// </summary>
    public Vector3 end;
    /// <summary>
    /// 终点的格子信息
    /// </summary>
    public Grid End;
    /// <summary>
    /// 用于寻路时对计算次数的记录
    /// </summary>
    public int count;
     /// <summary>
     /// 
     /// </summary>
    public Stack<Grid> stack;
    /// <summary>
    /// 开放列表
    /// </summary>
    public List<Grid> Open=new List<Grid>();
    /// <summary>
    /// 闭合列表
    /// </summary>
    public List<Grid> Close=new List<Grid>();
    /// <summary>
    /// 寻路算法
    /// </summary>
    void WayFinding()
    {
        while (true)//死循环遍历 直到找到寻路结果或者寻路失败的时候跳出
        {
            if (Open.Count == 0)//当开启列表没有值时 寻路失败
            {
                Debug.Log("寻路失败" + count);
                break;
            }
            if (Open[0].type == CubeType.End)//当到达重点的F值最小的格子是终点时 到达重点
            {
                Debug.Log("寻路成功" + count);
                if (End.parent)
                {
                    Grid grid = End;
                    while (true)
                    {
                        if (grid.parent.type == CubeType.Start)
                        {
                            stack.Push(grid);
                            break;
                        }
                        else
                        {
                            stack.Push(grid);
                            grid = grid.parent;
                        }
                    }
                }
                break;
            }
            //遍历次数计数器
            count++;
            //遍历当前格子周围的8个格子
            for (int i = -1; i <= 1; i++)
            {
                for (int j = -1; j <= 1; j++)
                {
                    //如果不可以斜着走
                    if (!MayInclinedToWalk)
                    {
                        if (Mathf.Abs(i) == 1 && Mathf.Abs(j) == 1)//跳过不是上下左右的路径
                            continue;
                    }
                    if (i == 0 && j == 0)//如果检测的是当前格子就跳过
                        continue;
                    //计算当前检测的格子
                    int x = (int)Open[0].transform.position.x + i;
                    int y = (int)Open[0].transform.position.z + j;
                    //计算条件(在范围内 不是障碍 ***不在关闭列表)
                    if (x >= 0 && y >= 0 && x < Row && y < Line
                        && Cubes[x, y].type != CubeType.Barrier
                        && !Close.Contains(Cubes[x, y]))
                    {
                        //计算G
                        int g = (int)(Vector3.Distance(Cubes[x, y].gameObject.transform.position
                            , Open[0].transform.position) * 10) + Open[0].g;
                        //判断更新G和父格子 (没赋值过 或者新值小的时候)
                        if (Cubes[x, y].g == 0 || Cubes[x, y].g > g)
                        {
                            Cubes[x, y].parent = Open[0];//设置父格子
                            Cubes[x, y].g = g;//设置G值
                        }
                        Cubes[x, y].h = (Mathf.Abs((int)end.x - x) + Mathf.Abs((int)end.z - y)) * 10;//更新H值
                        Cubes[x, y].f = Cubes[x, y].h + Cubes[x, y].g;//更新F值
                        if (!Open.Contains(Cubes[x, y]))//不在开启列表的时候加入开启列表 相当于在开启列表更新值 不在的话计算后加入
                            Open.Add(Cubes[x, y]);//加入开启列表
                    }
                }
            }
            //遍历结束 加入关闭列表
            Close.Add(Open[0]);
            //移除开始列表
            Open.Remove(Open[0]);
            //重新排序计算
            Open.Sort();
        }
    }
    void Start () {
        Cubes = new Grid[Row, Line];
        stack = new Stack<Grid>();
        ///按行列生成盒子
        for (int i = 0; i < Row; i++)
        {
            for (int j = 0; j < Line; j++)
            {                
                GameObject cube= GameObject.Instantiate(Prefab, new Vector3((float)i, 0, (float)j), Quaternion.identity);
                if (Random.Range(1, 101) < jiLv)
                {
                    cube.GetComponent<Grid>().type = CubeType.Barrier;
                    cube.GetComponent<MeshRenderer>().material.color = Color.blue;
                }
                if (Vector3.Distance(cube.transform.position, start) <= 0.1f)
                {
                    cube.GetComponent<Grid>().type = CubeType.Start;
                    cube.GetComponent<MeshRenderer>().material.color = Color.green;
                    Open.Add(cube.GetComponent<Grid>());
                }
                if (Vector3.Distance(cube.transform.position, end) <= 0.1f)
                {
                    cube.GetComponent<Grid>().type = CubeType.End;
                    End = cube.GetComponent<Grid>();
                    cube.GetComponent<MeshRenderer>().material.color = Color.red;
                }                
                cube.name += "_" + i + "|" + j;
                Cubes[i,j]=cube.GetComponent<Grid>();              
            }
        }
        WayFinding();
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            StartCoroutine(Move());
        }
        if (Input.GetKeyDown(KeyCode.A))
        {
            SceneManager.LoadScene(0);
        }
        if (Input.GetKeyDown(KeyCode.Z))
        {
            zanTing = false;
        }
    }
    /// <summary>
    /// 用于协成里的暂停标识位
    /// </summary>
    public bool zanTing;
    /// <summary>
    /// 走路效果实现//用盒子变黑模拟走路
    /// </summary>
    /// <returns></returns>
    IEnumerator Move()
    {

        while(stack.Count!=0)
        {
            stack.Pop().GetComponent<MeshRenderer>().material.color = Color.black;
            yield return new WaitForSeconds(0.2f);
        }      
    }
}


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
[System.Serializable]
public class Grid : MonoBehaviour, IComparable//用于该类(Grid)实现比较的方法:CompareTo
{
    //对当前盒子所在坐标初始化 用于计算g,h,f的值
    private void Start()
    {
      x = (int)transform.position.x;
      y = (int)transform.position.z; 
    }

    /// <summary>
    /// 坐标x
    /// </summary>
    public int x;
    /// <summary>
    /// 坐标y
    /// </summary>
    public int y;

    /// <summary>
    /// F=G+H
    /// G=父格子的G+(当前位置在父格子的正【前后左右】+10否则+14)
    /// H=从起点到终点的水平距离加上竖直距离*10
    /// </summary>
    public int f;
    public int g;
    public int h;
    /// <summary>
    /// 盒子类型 默认路面=Normal
    /// </summary>
    public CubeType type= CubeType.Normal;
    /// <summary>

    /// 盒子的父盒子 就是从父亲便利 发现的该盒子
    /// </summary>
    public Grid parent;
    /// <summary>
    /// 比较接口的实现
    /// </summary>
    /// <param name="obj"></param>
    /// <returns></returns>
    public int CompareTo(object obj)
    {
        Grid grid = (Grid)obj;
        if (f < grid.f)
        {
            return -1;
        }
        else if (f == grid.f)
        {
            return 0;
        }
        else
            return 1;
    }
}

这个是可以斜着走路的版本 也就是勾选第一个选项的时候

下面 是不可以斜着走的的情况,也就是在遍历周围格子的时候不要吧斜着方向的盒子加入开启列表 ;这里路障产生几率较大 不是每次都有能到终点的路线


                
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值