当前位置: 首页 > article >正文

Unity A*算法实现+演示

注意:

本文是对基于下方文章链接的理论,并最终代码实现,感谢作者大大的描述,非常详细,流程稍微做了些改动,文末有工程网盘链接,感兴趣的可以下载。

A*算法详解(个人认为最详细,最通俗易懂的一个版本)-CSDN博客

1、效果演示:

2、A*算法流程:

(1)         把起点加入 open list 。

(2)         重复如下过程:

                        a.         遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点。

                        b.         对当前方格的 8连通的每一个方格            

                                         ◆     如果它是不可抵达的或者它在 close list 中,忽略它。否则,做如下操作。

                                         ◆     如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,记录该方格的 F , G 和 H 值。

                                         ◆     如果它已经在 open list 中,检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,用 G 值作参考。更小的 G 值表示这是更好的路径。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。

                         c.         把这个节点移到 close list 。

                         d.         停止,当你

                                         ◆     把终点加入到了 open list 中,此时路径已经找到了,或者

                                         ◆     查找终点失败,并且 open list 是空的,此时没有路径。

(3)         保存路径。从终点开始,每个方格沿着父节点移动直至起点,这就是你的路径。

3、代码:

逻辑层Node结点定义:
using UnityEngine;

public enum GridState
{
    Empty,
    Block,
}
public class Node
{
    public GridState curState;
    public int X;
    public int Y;
    public int F;
    public int G;
    public int H;
    public Node parentNode;
    public Node(int x, int y)
    {
        X = x;
        Y = y;
        ResetNode();
    }

    public void ResetNode()
    {
        curState = GridState.Empty;
        F = 0;
        G = 0;
        H = 0;
        parentNode = null;
    }
    
    public void CalculateValue(Node endNode)
    {
        if (parentNode == null) return;
        //计算G
        G = GetPredictGValue(parentNode);
        //曼哈顿距离计算H
        H = Mathf.Abs(endNode.X - X) + Mathf.Abs(endNode.Y - Y);
        F = G + H;
    }

    public int GetPredictGValue(Node targetNode)
    {
        int predictG = 0;
        //四连通
        if (targetNode.X == X || targetNode.Y == Y)
        {
            predictG = targetNode.G + 10;
        }
        //八连通
        else
        {
            predictG = targetNode.G + 14;
        }

        return predictG;
    }
    
}
寻路算法:
using System.Collections.Generic;

public partial class PathFind
{
    private List<Node> openList;
    private List<Node> closeList;
    private Node[,] allNodeList;
    public void AStarInit(Node[,] allNodes)
    {
        openList = new List<Node>();
        closeList = new List<Node>();
        allNodeList = allNodes;
    }

    public void FindRoad(Node startNode, Node endNode)
    {
        openList.Add(startNode);
        LoopFindRoad(endNode);
    }

    private void LoopFindRoad(Node endNode)
    {
        //找到终点或者不存在路径
        if (openList.Count == 0||openList.Contains(endNode))
        {
            return;
        }
        //找到F值最小的
        Node smallestFNode = null;
        for (int i = 0; i < openList.Count; i++)
        {
            if (smallestFNode == null)
            {
                smallestFNode = openList[i];
                continue;
            }
            if (openList[i].F<=smallestFNode.F)
            {
                smallestFNode = openList[i];
            }
        }
        //获得八连通格子
        List<Node> eightAdjacent = GetRoundNode(smallestFNode);
        //更新代价
        for (int i = 0; i < eightAdjacent.Count; i++)
        {
            //不在openList里面
            if (!openList.Contains(eightAdjacent[i]))
            {
                eightAdjacent[i].parentNode = smallestFNode;
                eightAdjacent[i].CalculateValue(endNode);
                openList.Add(eightAdjacent[i]);
            }
            else
            {
                //判断是否需要更新F,G,H
                if (eightAdjacent[i].GetPredictGValue(smallestFNode) <= eightAdjacent[i].G)
                {
                    eightAdjacent[i].parentNode = smallestFNode;
                    eightAdjacent[i].CalculateValue(endNode);
                }
            }
        }
        //转移结点
        openList.Remove(smallestFNode);
        closeList.Add(smallestFNode);
        LoopFindRoad(endNode);
    }

    /// <summary>
    /// 获得八连通格子中可达到并且不在closeList中的格子
    /// </summary>
    /// <returns></returns>
    private List<Node> GetRoundNode(Node targetNode)
    {
        int x = targetNode.X;
        int y = targetNode.Y;
        List<Node> tempList = new List<Node>();
        if (IsReachableNode(x, y + 1)) tempList.Add(allNodeList[x, y + 1]);
        if (IsReachableNode(x + 1, y + 1)) tempList.Add(allNodeList[x + 1, y + 1]);
        if (IsReachableNode(x + 1, y)) tempList.Add(allNodeList[x + 1, y]);
        if (IsReachableNode(x + 1, y - 1)) tempList.Add(allNodeList[x + 1, y - 1]);
        if (IsReachableNode(x, y - 1)) tempList.Add(allNodeList[x, y - 1]);
        if (IsReachableNode(x - 1, y - 1)) tempList.Add(allNodeList[x - 1, y - 1]);
        if (IsReachableNode(x - 1, y)) tempList.Add(allNodeList[x - 1, y]);
        if (IsReachableNode(x - 1, y + 1)) tempList.Add(allNodeList[x - 1, y + 1]);
        return tempList;
    }

    /// <summary>
    /// 判断格子是否可到达
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private bool IsReachableNode(int x, int y)
    {
        if (x >= allNodeList.GetLength(0) || x <= 0)
        {
            return false;
        }
        if (y >= allNodeList.GetLength(1) || y <= 0)
        {
            return false;
        }
        if (allNodeList[x, y].curState == GridState.Block)
        {
            return false;
        }
        if (closeList.Contains(allNodeList[x, y]))
        {
            return false;
        }
        return true;
    }
}

4、补充:

如果希望过障碍时,不允许他斜向过障碍,可以额外加个判断,原理很简单,对于8连通的角落点,判断角落点的4连通是否有障碍,如果有障碍就不算入可到达格子。

代码如下:

演示:

5、工程网盘链接:

通过网盘分享的文件:AStarDemo.unitypackage
链接: https://pan.baidu.com/s/1L_f1DIkqe9Oqm_dnFSSVew 提取码: 1212


http://www.kler.cn/a/441971.html

相关文章:

  • Go-知识 版本演进
  • TP4056锂电池充放电芯片教程文章详解·内置驱动电路资源!!!
  • Vue3数据响应式原理
  • 财务RPA就是财务机器人吗?有什么作用
  • Django简介与虚拟环境安装Django
  • [EAI-018] π0: A Vision-Language-Action Flow Model for General Robot Control
  • clickhouse 分布式表创建、增加、更新、删除、查询
  • 如何在 Ubuntu 22.04 上安装 JupyterLab 环境教程
  • gdb调试常用指令及案例讲解
  • 三菱FX系列PLC以太网通讯处理器ModbusTCP通讯
  • 原生JS实现聊天窗口
  • 技术解决方案|复合机器人在cnc行业的上下料
  • 利用CNN与多尺度特征、注意力机制的融合实现低分辨率人脸表情识别,并给出模型介绍与代码实现
  • 【HarmonyOS NEXT】Web 组件的基础用法以及 H5 侧与原生侧的双向数据通讯
  • 校园点餐系统|Java|SSM|JSP|
  • 实景三维在城乡规划、地下管网管理和智慧城市中的应用
  • 单片机 GPIO
  • Qwen文章阅读笔记
  • 问题总结一
  • 深入探索Vue.js中的插值表达式:数据绑定的艺术
  • MapGIS 10.7大数据GIS升级!打造数据要素底座,赋能新质生产力
  • 自签名CA证书
  • 在 Linux 系统中,让 apt 使用 HTTP 代理
  • 文件上传之文件内容检测
  • 项目23:简易网络爬虫 --- 《跟着小王学Python·新手》
  • 突破时间与空间限制的富媒体百宝箱——智能工具箱:让云上内容生产更easy