Unity | 宽度优先搜索算法的简单实现

理论的知识,这篇文章就不作过多的扩展了,网上随便一搜就有很多好的文章。

最近做算法练习,看完理论先自己动手试试。


不废话,下面开始吧:

蓝色是开始点,红色是终点,绿色表示遍历过的点。

主要的逻辑类:

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


/*
 * 
 *  Writer:June
 * 
 *  Date: 2021.1.13
 * 
 *  Function:游戏控制
 * 
 *  Remarks:
 * 
 */


public class GameController : MonoBehaviour
{
    #region 单例
    /// <summary>
    /// 单例
    /// </summary>
    public static GameController Instance { get; private set; }
    private void Awake()
    {
        if (Instance == null)
            Instance = this;
        else
        {
            if (Instance != this)
            {
                Destroy(gameObject);
            }
        }
        DontDestroyOnLoad(gameObject);
    }
    #endregion
    /// <summary>
    /// x轴物体个数
    /// </summary>
    [SerializeField]private int xLength;
    /// <summary>
    /// y轴物体个数
    /// </summary>
    [SerializeField]private int yLength;
    /// <summary>
    /// 方块预设
    /// </summary>
    public GameObject cube;
    /// <summary>
    /// 玩家
    /// </summary>
    public GameObject player;
    /// <summary>
    /// 地图父节点
    /// </summary>
    public Transform mapParent;
    /// <summary>
    /// 开始位置颜色
    /// </summary>
    [SerializeField]private Color startColor;
    /// <summary>
    /// 结束位置颜色
    /// </summary>
    [SerializeField]private Color endColor;
    /// <summary>
    /// 路径点链表
    /// </summary>
    public List<Transform> pathList = new List<Transform>();
    /// <summary>
    /// Vector2Int方向,用于判断当前位置点的相邻四个方向
    /// </summary>
    private Vector2Int[] vector2Ints = { Vector2Int.up, Vector2Int.right, Vector2Int.down, Vector2Int.left };
    /// <summary>
    /// 存储地图的字典
    /// </summary>
    private Dictionary<Vector2Int, PointData> dicV2Int = new Dictionary<Vector2Int, PointData>();
    /// <summary>
    /// 队列,存储要判断的点
    /// </summary>
    private Queue<PointData> pointQueue = new Queue<PointData>();
    /// <summary>
    /// 开始位置点
    /// </summary>
    [SerializeField] private GameObject startPoint;


    private void Start()
    {
        CreatMap();
    }

    /// <summary>
    /// 构建地图
    /// </summary>
    public void CreatMap()
    {
        for (int x = 0; x < xLength; x++)
        {
            for (int y = 0; y < yLength; y++)
            {
                GameObject go = Instantiate(cube);
                go.transform.SetParent(mapParent);
                go.transform.localPosition = new Vector3(x, 0, y);
                go.name = "X=" + x + "  Y=" + y;
                go.GetComponentInChildren<TextMeshPro>().text = "(" + x + "," + y + ")";
                PointData pointData = go.GetComponent<PointData>();
                //设置坐标信息
                pointData.x = x;
                pointData.y = y;
                //开始点
                if (x == 0 && y == 0) 
                {
                    go.GetComponent<MeshRenderer>().material.color = startColor;
                    startPoint = go;
                }
                Vector2Int v2Int = new Vector2Int(x, y);
                if (!dicV2Int.ContainsKey(v2Int))
                    dicV2Int.Add(v2Int, pointData);
            }
        }
        //随机一个点,设置为结束点
        Vector2Int randomV2 = new Vector2Int(Random.Range(1, xLength), Random.Range(1, yLength));
        //结束点处理
        dicV2Int[randomV2].GetComponent<PointData>().IsEndPoint = true;

        BFS();

        CreatPlayer();
        //结束点设置一下颜色,加以区分
        dicV2Int[randomV2].GetComponent<MeshRenderer>().material.color = endColor;
    }


    #region 主角相关
    /// <summary>
    /// 生成主角
    /// </summary>
    public void CreatPlayer()
    {
        GameObject playerGo = Instantiate(player);
        playerGo.transform.position = new Vector3(0, 0.4f, 0);
        //开始移动
        StartCoroutine(PlayerMove(pathList, playerGo));
    }

    /// <summary>
    /// 玩家移动
    /// </summary>
    IEnumerator PlayerMove(List<Transform> _path, GameObject _player)
    {
        for (int i = 0; i < _path.Count; i++)
        {
            yield return new WaitForSeconds(1f);
            _player.transform.position = new Vector3(_path[i].position.x, 0.4f, _path[i].position.z);
        }
    }
    #endregion

    #region 宽度优先搜索

    /// <summary>
    /// 扩展周围,即找到当前点周围相邻的点(上右下左),并将其添加到队列中
    /// </summary>
    private void ExploreAround(Vector2Int _currentPoint)
    {
        foreach (Vector2Int dir in vector2Ints)
        {
            Vector2Int v2Int = dir + _currentPoint;
            if (dicV2Int.ContainsKey(v2Int))
            {
                var pointDataComp = dicV2Int[v2Int];
                //判断是否已经搜索过的
                if (pointDataComp.IsCheck || pointQueue.Contains(pointDataComp)) continue;
                //设置当前路径点的父级
                pointDataComp.parentPoint = dicV2Int[_currentPoint];
                pointQueue.Enqueue(pointDataComp);
                //设置检测的颜色
                dicV2Int[v2Int].GetComponent<MeshRenderer>().material.color = Color.green;
            }
        }
    }

    /// <summary>
    /// 宽度优先搜索核心逻辑
    /// </summary>
    private void BFS()
    {
        //开始时,把开始点加入到队列中判断
        pointQueue.Enqueue(startPoint.GetComponent<PointData>());

        while (pointQueue.Count > 0)
        {
            //先找到当前点相邻的点
            var pointDataComp = pointQueue.Dequeue();
            if (!pointDataComp.IsCheck)
            {
                //标记为检测
                pointDataComp.IsCheck = true;
                //如果找到,立即终止算法
                if (pointDataComp.IsEndPoint)
                {
                    GetPath(pointDataComp);
                    //找到终点后,清空队列,以免继续遍历
                    pointQueue.Clear();
                    break;
                }
                ExploreAround(pointDataComp.GetV2IntPos());
            }
        }
    }

    /// <summary>
    /// 获取最短路径,从终点获取上一节点,一直获取到开始点,则得到路径
    /// </summary>
    /// <param name="_endPoint">终点</param>
    private void GetPath(PointData _endPoint)
    {
        pathList.Add(_endPoint.transform);
        PointData point = _endPoint.parentPoint;
        while (point != startPoint.GetComponent<PointData>())
        {
            //将当前点的父级(上一级),添加到路径中
            pathList.Add(point.transform);
            //重复从当前点取父级点的操作,直到父级点是开始点为止
            point = point.parentPoint;
        }
        //反转链表
        pathList.Reverse();
    }
    #endregion
}

生成的每个路径点,都会挂有PointData类:

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


/*
 * 
 *  Writer:June
 * 
 *  Date: 2021.1.13
 * 
 *  Function:玩家移动
 * 
 *  Remarks:
 * 
 */


public class PointData : MonoBehaviour
{
    /// <summary>
    /// 当前点是否已经检测了
    /// </summary>
    public bool IsCheck { get; set; }
    /// <summary>
    /// X位置
    /// </summary>
    public int x;
    /// <summary>
    /// Y位置
    /// </summary>
    public int y;
    /// <summary>
    /// 当前点是否是终点
    /// </summary>
    public bool IsEndPoint { get; set; }
    /// <summary>
    /// (父节点)上一个节点,即当前的点,是由哪个父节点搜索而来的
    /// </summary>
    public PointData parentPoint;


    /// <summary>
    /// 获取Vector2Int坐标
    /// </summary>
    /// <returns></returns>
    public Vector2Int GetV2IntPos()
    {
        Vector2Int vector2Int = new Vector2Int(x, y);
        return vector2Int;
    }

}


工程文件:

链接:https://pan.baidu.com/s/16wYCX9MdG9Q6Odm7lAyhfQ 
提取码:9agf 
unity版本  2019.4.12f1

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值