Unity 中的简单A*寻路 (AStar寻路)实现

这篇具有很好参考价值的文章主要介绍了Unity 中的简单A*寻路 (AStar寻路)实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文实现的A*算法,未经过大量的优化,后续文章会进一步实现优化

后篇:A*优化讨论

寻路代码实现

结点类:

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

public enum E_Node_Type
{
    /// <summary>
    /// 可行走
    /// </summary>
    Normal,
    /// <summary>
    /// 障碍
    /// </summary>
    Obstacles
}


public class AStarNode 
{
    /// <summary>
    /// x坐标
    /// </summary>
    public int x;
    /// <summary>
    /// y坐标
    /// </summary>
    public int y;

    /// <summary>
    /// 寻路消耗
    /// </summary>
    public float f;
    /// <summary>
    /// 距离起点距离
    /// </summary>
    public float g;
    /// <summary>
    /// 距离终点距离
    /// </summary>
    public float h;

    /// <summary>
    /// 父结点
    /// </summary>
    public AStarNode father;

    /// <summary>
    /// 结点类型
    /// </summary>
    public E_Node_Type type;

    public AStarNode(int xPos, int yPos, E_Node_Type t)
    {
        this.x = xPos;
        this.y = yPos;
        this.type = t;
    }

}

结点管理类:

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

public class AStarManger : BaseManger<AStarManger>
{
    public int mapW;
    public int mapH;

    private AStarNode startNode;
    private AStarNode endNode;

    //private Vector2 endVec = Vector2.right * -1;

    public AStarNode[,] nodes;

    List<AStarNode> openList = new List<AStarNode>();
    List<AStarNode> closeList = new List<AStarNode>();

    /// <summary>
    /// 初始化
    /// </summary>
    public void InitAStarManger(int mapW, int mapH)
    {
        if(mapH < 0 || mapW < 0)
        {
            Debug.LogError("地图尺寸存在问题");
            return;
        }
        this.mapW = mapW;
        this.mapH = mapH;
        
        nodes = new AStarNode[mapW, mapH];

        for(int i = 0; i < this.mapW; i++)
        {
            for(int j = 0; j < this.mapH; j++)
            {
                AStarNode an = new AStarNode(i, j, Random.Range(0, 100) < 20 ? E_Node_Type.Obstacles : E_Node_Type.Normal);
                nodes[i, j] = an;
            }
        }

    }

    /// <summary>
    /// 开始寻径
    /// </summary>
    /// <returns></returns>
    public List<AStarNode> FindPath(Vector2 startPos, Vector2 endPos)
    {
        if(startPos.x < 0 || startPos.y < 0 || endPos.x < 0 || endPos.y < 0 
            || startPos.x >= this.mapW || startPos.y >= this.mapH 
            || endPos.x >= this.mapW || endPos.y >= this.mapH)
        {
            Debug.LogError("坐标超出范围");
            return null;
        }

        startNode = nodes[(int)startPos.x, (int)startPos.y];
        endNode = nodes[(int)endPos.x, (int)endPos.y];

        if(startNode.type is E_Node_Type.Obstacles || endNode.type is E_Node_Type.Obstacles)
        {
            Debug.LogError("起始结点或目标结点为障碍");
            return null;
        }

        startNode.father = null;
        startNode.f = 0;
        startNode.g = 0;
        startNode.h = 0;

        openList.Clear();
        closeList.Clear();

        closeList.Add(startNode);

        while(true)
        {
            //找附近点,并将符合条件的添加进openList
            AddNearlyNode2OpenList(startNode.x - 1, startNode.y -1, 1.4f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x, startNode.y - 1, 1f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x + 1, startNode.y - 1, 1.4f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x - 1, startNode.y, 1f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x + 1, startNode.y, 1f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x - 1, startNode.y + 1, 1.4f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x, startNode.y + 1, 1f, startNode, endPos);
            AddNearlyNode2OpenList(startNode.x + 1, startNode.y + 1, 1.4f, startNode, endPos);

            if(openList.Count == 0)
            {
                Debug.LogWarning("终点不可达");
                return null;
            }

            //排序
            openList.Sort(SortRuler);
            //选择新的父节点
            startNode = openList[0];
            //添加到closeList
            closeList.Add(startNode);
            openList.RemoveAt(0);
            //判断是否到达终点
            if(startNode == endNode)
            {
                List<AStarNode> path = new List<AStarNode>();
                path.Add(endNode);
                while(endNode.father != null)
                {
                    path.Add(endNode.father);
                    endNode = endNode.father;
                }
                path.Reverse();
                return path;
            }
        }
    }

    private int SortRuler(AStarNode a, AStarNode b)
    {
        if (a.f >= b.f) return 1;
        else return -1;
    }

    /// <summary>
    /// 将父结点周围符合条件结点,添加进openList
    /// </summary>
    private void AddNearlyNode2OpenList(int x, int y, float g, AStarNode father, Vector2 endPos)
    {
        //判断传入坐标是否超范围
        if(x < 0 || y < 0 || x >= this.mapW || y >= this.mapH)
        {
            return;
        }

        AStarNode an = nodes[x, y];

        //如果该结点是障碍结点 或 ol,cl里有,忽略
        //如果结点为null,或是上下左右都有障碍物,忽略
        if(an.type is E_Node_Type.Obstacles
            || an is null || closeList.Contains(an)
            || nodes[x, father.y].type is E_Node_Type.Obstacles 
            || nodes[father.x, y].type is E_Node_Type.Obstacles)
        {
            return;
        }

        //单独判断是否在ol里,优化。如果结点已经在ol里,并且下一个最小f值的点也可以算到该点,那么该点的父节点不会变为这个最小值点,导致不是最短路径
        if(openList.Contains(an))
        {
            float gThis = father.g + g;
            if(gThis < an.g)
            {
                an.g = gThis;
                an.f = an.g + an.h;
                an.father = father;
            }
            return;
        }

        //父结点距离起点距离,加上自己距离父结点距离
        an.g = father.g + g;
        an.h = Mathf.Abs(endPos.x - x) + Mathf.Abs(endPos.y - y);
        //计算曼哈顿距离
        an.f = an.g + an.h;

        //将结点加入openList
        openList.Add(an);

        //设置父结点
        an.father = father;
    }
}

单例模板:

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

public class BaseManger<T> where T : new() 
{
    private static T instance;

    public static T Getinstance()
    {
        if(instance is null)
        {
            instance = new T();
        }
        return instance;
    }
}

Unity中界面构建

测试脚本:

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

public class Test : MonoBehaviour
{
    public float BeginX;
    public float BeginY;

    public float offsetX;
    public float offsetY;

    public int mapW;
    public int mapH;

    private Vector2 beginPos = Vector2.right * -1;
    private Dictionary<string, GameObject> cubes = new Dictionary<string, GameObject>();
    List<AStarNode> list = new List<AStarNode>();

    private GameObject tmpStart;
    private GameObject tmpEnd;
    // Start is called before the first frame update
    void Start()
    {
        Init();
    }

    // Update is called once per frame
    void Update()
    {
        if(Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit info;
            if(Physics.Raycast(ray, out info, 1000))
            {
                if(beginPos == Vector2.right * -1)
                {
                    if (list != null)
                    {                       
                        for (int i = 0; i < list.Count; i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.white;
                        }
                    }

                    string[] strs = info.collider.gameObject.name.Split('_');
                    beginPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));

                    if (tmpEnd) tmpEnd.GetComponent<MeshRenderer>().material.color = Color.white;
                    if (tmpStart) tmpStart.GetComponent<MeshRenderer>().material.color = Color.white;

                    tmpStart = info.collider.gameObject;
                    tmpStart.GetComponent<MeshRenderer>().material.color = Color.yellow;
                }
                else
                {
                    string[] strs = info.collider.gameObject.name.Split('_');
                    Vector2 endPos = new Vector2(int.Parse(strs[0]), int.Parse(strs[1]));
                   
                    tmpEnd = info.collider.gameObject;
                    tmpEnd.GetComponent<MeshRenderer>().material.color = Color.blue;
                    list = AStarManger.Getinstance().FindPath(beginPos, endPos);
                    if(list != null)
                    {                       
                        for (int i = 0; i < list.Count; i++)
                        {
                            cubes[list[i].x + "_" + list[i].y].GetComponent<MeshRenderer>().material.color = Color.green;
                        }
                    }

                    beginPos= Vector2.right * -1;
                }
            }
        }
    }

    public void Init()
    {
        AStarManger.Getinstance().InitAStarManger(mapW, mapH);
        tmpEnd = null; 
        tmpStart = null;
        list = null;

        if(cubes.Count != 0)
        {            
            foreach (var obj in cubes)
            {
                Destroy(obj.Value.gameObject);
            }
            cubes.Clear();
        }
        for (int i = 0; i < mapW; i++)
        {
            for (int j = 0; j < mapH; j++)
            {
                GameObject obj = GameObject.CreatePrimitive(PrimitiveType.Cube);
                obj.transform.position = new Vector3(BeginX + i * offsetX, BeginY + j * offsetY);
                obj.transform.name = i + "_" + j;
                
                cubes.Add(obj.name, obj);

                AStarNode an = AStarManger.Getinstance().nodes[i, j];
                if (an.type == E_Node_Type.Obstacles)
                {
                    MeshRenderer mr = obj.GetComponent<MeshRenderer>();
                    mr.material.color = Color.red;
                }
            }
        }
    }
}

新建一个场景,将测试脚本挂载在任意物体上

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

新建一个画布,并添加一个按钮。其它ui元素可随意设定

将按钮关联Init方法

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

实现效果

正常终点可达:

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

终点不可达情况:

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

Unity 中的简单A*寻路 (AStar寻路)实现,unity,算法,unity,算法

后续优化文章: 

 进一步优化


本文仅为学习阶段的一个总结,按需浏览。存在一定不足的情况文章来源地址https://www.toymoban.com/news/detail-769394.html

到了这里,关于Unity 中的简单A*寻路 (AStar寻路)实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • AI自动寻路AStar算法【图示讲解原理】

    AI自动寻路的算法可以分为以下几种: 1、A*算法:A*算法是一种 启发式搜索算法 ,它利用启发函数(heuristic function)来评估节点的估价函数(estimated cost function),从而寻找最短路径。A*算法综合考虑了 节点的实际代价 和 到目标节点的预计代价 ,因此能够快速而准确地寻找

    2023年04月19日
    浏览(28)
  • 【Unity】一篇文章搞定AStar(A*)算法

    AStar(A*)算法,是一种在静态网格中求解最短路径直接有效的搜索方法。在游戏开发中,A*算法常应用于部分RPG游戏和策略战棋类游戏。对于Unity开发者来说,掌握A*算法也是十分有必要的。不过在了解A*算法之前,有必要先回顾一下深度优先算法(DFS)、广度优先算法(BFS)

    2024年02月02日
    浏览(45)
  • Unity寻路A星算法

    在Unity中实现A星(A*,A-Star)算法是一种用于寻找两点之间最短路径的广泛应用的技术。该算法结合了启发式搜索与图论中的Dijkstra算法,通过评估每个节点到起点和终点的成本来确定最优路径。 以下是Unity中使用A*寻路算法的一个简要步骤和实例: 实现步骤概览: 构建网格

    2024年01月17日
    浏览(26)
  • 详解Unity中的Nav Mesh|导航寻路系统 (一)

    在类RTS、RPG游戏中,都会提供自动寻路功能,当玩家下达指令后,NPC就会自动计算到达目标的路径,实现这种功能的方式有很多种,其中Unity本身也自带了一种导航寻路系统,该系统会将游戏场景中复杂的对象烘焙为网格信息,通过网格来计算NPC抵达目标的最短路径,该系统

    2024年02月02日
    浏览(61)
  • 详解Unity中的Nav Mesh新特性|导航寻路系统 (一)

    之前我们讲解过Unity的Nav Mesh系统,其中提到过这个新版的Nav Mesh,它解决现有Nav Mesh的几个缺陷,比如无法动态烘焙,无法按照Agent的半径和高度适当的判断可行路径。现在新版Nav Mesh可以彻底解决这个问题!某种意义上讲,它只能算作一个补丁,因为它要依赖于原有的Nav Me

    2024年02月05日
    浏览(29)
  • Unity(四十七):寻路网格-内置组件实现自动寻路避障

    配置寻路区域 Navigation Static 配置静态游戏对象 Navigation Static 导航网格生成 Navigation 在 Navigation 窗口进行烘焙(菜单: Window AI Navigation )中进行处理的 自动寻路并绘制路线 Nav Mesh Agent 、 NavMeshPath 属性 功能 Agent Size Radius 代理的半径,用于计算障碍物与其他代理之间的碰撞

    2024年01月15日
    浏览(40)
  • 【Unity】NavMesh Agent实现自动寻路

    在Unity中,可以使用自带导航系统(Navigation System)实现自动寻路。 首先在Unity场景中创建一个NavMesh表面,用于描述场景中可行走的区域。 在Unity 5.6或更高版本中,可以通过以下步骤创建NavMesh表面: 选择场景中的地形或物体,

    2024年02月08日
    浏览(40)
  • 【Unity自动寻路】使用Navigation系统实现物体自动寻路绕开障碍物

    知识点流程图 我们在游戏场景中经常会有一些障碍物、墙壁、树木等等,如果我想要让角色或者怪物去墙的另一边,我直接在墙另一边点击左键,我希望角色自动跑过去,但是他不能直接穿透墙,他需要“智能”的绕开障碍物,自动找到可以走的路,自己过去!这就是Unity

    2024年02月03日
    浏览(34)
  • 【Unity2d】2DNavMesh自动寻路实现

            在u3d中,系统自带了NavMesh组件,能够极其方便的实现自动寻路,新版的NavMesh并没有随着Unity的更新添加进引擎功能中,在2d项目中,我们想要实现NavMesh自动寻路还需要下载新的组件。这里提供网址:GitHub - h8man/NavMeshPlus: Unity NavMesh 2D Pathfinding         要在小伙

    2024年02月14日
    浏览(34)
  • Godot插值、贝塞尔曲线和Astar寻路

    线性插值是采用一次多项式上进行的插值计算,任意给定两个值A和B,那么在A和B之间的任意值可以定义为: P(t) = A * (1 - t) + B * t,0 = t = 1。 数学中用于线性拟合,游戏应用可以做出跟随效果(宠物跟随、npc跟随) 贝塞尔是插值的应用之一。贝塞尔曲线是为工业设计,是图形

    2024年04月14日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包