美文网首页
AStar模块

AStar模块

作者: Qkuang | 来源:发表于2019-12-21 23:30 被阅读0次

    IF框架总目录

    简介

    A* 算法 ,用于寻路。可以寻找到最短路径。由于本系列是介绍 如何只用IF框架的各个模块,并非探究各个模块的实现原理。所以本文着重讲如何使用IF框架中的A*寻路模块。

    测试源码

    using UnityEngine;
    using IFramework.Astar;    //引入IF框架 A* 空间
    public class MapGrid : MonoBehaviour
    {
        public GameObject r_player;
        public GameObject r_Enemy;
    
        //********构建地图 部分********//
        public LayerMask r_wallPlayer;
        public Vector2 mapSize;
        Node[,] aStarMap;
        Vector3 startPosOfWoldPos;
        Vector3 buffeV2;
    
        //********* 引入 IF框架,进行A* 计算********//
        AStarMap2X AMape = new AStarMap2X();        // 该类是 IF框架中 A* 模块的类,可以理解为 地图类
        AStarSeacher<AStarNode2X, AStarMap2X> sear;     // 该类IF框架中 A* 模块的类,可以理解为 搜寻者类。
        private void Start()
        {
            //********构建地图 部分********//
            int x = Mathf.FloorToInt(mapSize.x);
            int y = Mathf.FloorToInt(mapSize.y);
            aStarMap = new Node[x, y];
            startPosOfWoldPos = transform.position + new Vector3(0.4f - mapSize.x / 2, 0, 0.4f - mapSize.y / 2);
            buffeV2 = Vector2.zero;
            CreateGride();
    
            //********* 引入 IF框架,进行A* 计算********//
    
            AMape.ReadMap((value_) =>
            {
                if (value_.isWalk)
                {
                    return AStarNodeType.Walkable;
                }
                else
                {
                    return AStarNodeType.Wall;
                }
    
            }, aStarMap, true);
    
            sear = new AStarSeacher<AStarNode2X, AStarMap2X>();
            sear.LoadMap(AMape);
            SearchPaht(woldPosTomapPos(r_Enemy.transform.position ),woldPosTomapPos(r_player.transform.position ));
    
    
        }
    
        // 初始化地图方法
        private void CreateGride()
        {
            for (int i = 0; i < aStarMap.GetLength(0); i++)
            {
                for (int j = 0; j < aStarMap.GetLength(1); j++)
                {
                    buffeV2.x = startPosOfWoldPos.x + i;
                    buffeV2.z = startPosOfWoldPos.z + j;
                    aStarMap[i, j] = new Node(buffeV2, !(Physics.CheckSphere(buffeV2, 0.4f, r_wallPlayer)));
                }
            }
    
        }
        // 绘制 Cube
        private void OnDrawGizmos()
        {
            if (aStarMap != null)
            {
                foreach (Node item in aStarMap)
                {
                    Gizmos.color = item.isWalk ? Color.white : Color.red;
                    if (item.isPath)
                        Gizmos.color = Color.yellow;
                    Gizmos.DrawCube(item.woldPos, Vector3.one * 0.8f);
                }
            }
    
        }
    
        // 将世界位置转化为 地图位置。
        public Vector2 woldPosTomapPos(Vector3 targetPos)
        {
            Vector2 result = Vector2.zero;
            Vector3 delta = targetPos - startPosOfWoldPos;
            result.x = Mathf.Clamp(Mathf.RoundToInt(delta.x), 0, mapSize.x - 1);
            result.y = Mathf.Clamp(Mathf.RoundToInt(delta.z), 0, mapSize.y - 1);
            return result;
        }
    
    // 计算 最短 路径。
        public void SearchPaht(Vector2 nowPos, Vector2 aimPos)
        {
            try
            {
                AStarNode2X[] pathArray = sear.Search(AMape[(int)nowPos.x, (int)nowPos.y], AMape[(int)aimPos.x, (int)aimPos.y]);
                foreach (var item in aStarMap)
                {
                    item.isPath = false;
                }
                    
                if(pathArray==null)
                return;
                foreach (var item in pathArray)
                {
                    aStarMap[item.X, item.Y].isPath = true;
                }
            }
            catch
            {
                print("MapIndex crossing boundary");
            }
    
        }
    }
    
    /// <summary>
    /// 节点类,用于对对接IF框架的 地图节点。
    /// </summary>
    public class Node
    {
        public Vector3 woldPos; // 该节点在世界空间的位置
        public Vector2 mapPos; // 该节点在地图中的位置
        public bool isWalk; // 是否可以行走
        public bool isPath; // 是否是返回路径
    
        public Node() { }
    
        public Node(Vector3 woldPos, bool isWalk)
        {
            this.woldPos = woldPos;
            this.isWalk = isWalk;
    
        }
    }
    
    

    运行效果:


    image.png image.png

    白色的是 可以行走区域,红色和的是 障碍区域,不可行走。 黄色的方块是 寻找出来的最短 路径。

    源码说明

    源代码 主要 内容 分为 两个部分:

    1. 构建可视化地图
    2. 计算最短路径

    其中 构建可视化地图 属于 unity的基础。我用到的 API 可以通过 查阅 unity官方手册查询得到。本文 主要介绍 IF 框架的A* 模块使用,因此不多叙述。

    A*模块主要类

    AStarMap2X AMape ; // 该类是 IF框架中 A* 模块的类,可以理解为 地图类

    AStarSeacher<AStarNode2X, AStarMap2X> sear; // 该类IF框架中 A* 模块的类,可以理解为 搜寻者类。

    1. 类名后面的 2x 表示 地图的维度,除了二维,还有三维。 这里为了演示简单,采用了二维计算。

    2. AStarMap2x 类 是一个地图类 可以理解为 一个二维 地图。
      public void ReadMap<T>(Func<T, AStarNodeType> func, T[,] arr, bool walkSideways = true);
      此类 只需要 知道一个ReadMap 方法即可。
      该方法 有三个参数,为了便于理解,将从参数二 开始介绍。

    • 参数二:传入一个二维数组。作者这里传入了 一个自定义的Node二维数组。当然也可以传入一个int数组,因为是泛型,所以都支持。该数组 就是地图数据。
    • 参数一:传入一个委托。该委托会 将会用于 处理 二维数组 中的值,比如什么情况下 将被标记为 “墙”。
      public bool isWalk; // 是否可以行走 这里,我为 Node类定义 了isWalk字段,用于区分 可行走/不可行走 区域。
    • 参数三:用于设定 A* 寻路是否斜着行走。 默认True 就好。

    个人猜测:ReadMap 会将 泛型类型二维数组的值,转化为 AStarNode2x 类型。从而形成一个AStarNode2X的二维数组至于是否是这样,咱们作为一个 接口调用员,并不重要。

    1. AStartSeacher 类 是一个 搜寻者类。 可以提供最短路径的搜索。是A*的核心算法封装而来。
    • 构造注意:需要传入两个泛型,前者是一个节点类型,后者是一个map 类型。由于这里是 2维,所以 为:AStarNode2X, AStarMap2X
    • LoadMap函数:public void LoadMap(Map map); 用于 载入 地图。为 路径查找提供数据支持。
    • Search函数:public Node[] Search(Node start, Node end); 用于 查找 开始节点,结束节点之间的 最短路径。 返回一个 节点 数组。该返回值 中 按顺序 存放着 路径的 所有节点。

    我们拿到 “路径”后,就可以根据 自己的需要进行 转化。如何 在世界 空间和 二维数组地图间 经行 转化 ,可以参考 我前面 的源码。

    结尾

    以上内容是 作者个人的理解,如有错误,欢迎指出。
    顺便给出OnClick大佬自己 写的文档地址:002AStar寻路模块

    如果对 IF框架有其他看法,也欢迎大家 进群交流。

    相关文章

      网友评论

          本文标题:AStar模块

          本文链接:https://www.haomeiwen.com/subject/cacnnctx.html