Unity A*算法实现+演示

news2025/1/23 22:25:22

注意:

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

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.coloradmin.cn/o/2262395.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

博弈论3:图游戏SG函数(Graph Games)

目录 一、图游戏是什么 1.游戏特征 2.游戏实例 二、图游戏的必胜策略 1.SG 函数&#xff08;Sprague-Grundy Function&#xff09; 2.必胜策略&#xff08;利用SG函数&#xff09; 3.拿走游戏转化成图游戏&#xff08;Take-away Game -> Graph Game&#xff09; 一、图…

0101多级nginx代理websocket配置-nginx-web服务器

1. 前言 项目一些信息需要通过站内信主动推动给用户&#xff0c;使用websocket。web服务器选用nginx&#xff0c;但是域名是以前通过阿里云申请的&#xff0c;解析ip也是阿里云的服务器&#xff0c;甲方不希望更换域名。新的系统需要部署在内网服务器&#xff0c;简单拓扑图如…

qt-C++笔记之自定义类继承自 `QObject` 与 `QWidget` 及开发方式详解

qt-C笔记之自定义类继承自 QObject 与 QWidget 及开发方式详解 code review! 参考笔记 1.qt-C笔记之父类窗口、父类控件、对象树的关系 2.qt-C笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理 3.qt-C笔记之自定义类继承自 QObject 与 QW…

Elastic 8.17:Elasticsearch logsdb 索引模式、Elastic Rerank 等

作者&#xff1a;来自 Elastic Brian Bergholm 今天&#xff0c;我们很高兴地宣布 Elastic 8.17 正式发布&#xff01; 紧随一个月前发布的 Elastic 8.16 之后&#xff0c;我们将 Elastic 8.17 的重点放在快速跟踪关键功能上&#xff0c;这些功能将带来存储节省和搜索性能优势…

[C++]类的继承

一、什么是继承 1.定义&#xff1a; 在 C 中&#xff0c;继承是一种机制&#xff0c;允许一个类&#xff08;派生类&#xff09;继承另一个类&#xff08;基类&#xff09;的成员&#xff08;数据和函数&#xff09;。继承使得派生类能够直接访问基类的公有和保护成员&#xf…

Docker 用法详解

文章目录 一、Docker 快速入门1.1 部署 MYSQL1.2 命令解读&#xff1a; 二、Docker 基础2.1 常见命令&#xff1a;2.1.1 命令介绍&#xff1a;2.1.2 演示&#xff1a;2.1.3 命令别名&#xff1a; 2.2 数据卷&#xff1a;2.2.1 数据卷简介&#xff1a;2.2.2 数据卷命令&#xff…

【自动化】Python SeleniumUtil 油猴 工具 自动安装用户脚本

【自动化】Python SeleniumUtil 油猴 工具 【自动化】Python SeleniumUtil 工具-CSDN博客【自动化】Python SeleniumUtil 工具。https://blog.csdn.net/G971005287W/article/details/144565691 油猴工具 import timefrom selenium.webdriver.support.wait import WebDriverW…

盛元广通畜牧与水产品检验技术研究所LIMS系统

一、系统概述 盛元广通畜牧与水产品检验技术研究所LIMS系统集成了检测流程管理、样品管理、仪器设备管理、质量控制、数据记录与分析、合规性管理等功能于一体&#xff0c;能够帮助实验室实现全流程的数字化管理。在水产、畜牧产品的质检实验室中&#xff0c;LIMS系统通过引入…

clickhouse-数据库引擎

1、数据库引擎和表引擎 数据库引擎默认是Ordinary&#xff0c;在这种数据库下面的表可以是任意类型引擎。 生产环境中常用的表引擎是MergeTree系列&#xff0c;也是官方主推的引擎。 MergeTree是基础引擎&#xff0c;有主键索引、数据分区、数据副本、数据采样、删除和修改等功…

GEE+本地XGboot分类

GEE本地XGboot分类 我想做提取耕地提取&#xff0c;想到了一篇董金玮老师的一篇论文&#xff0c;这个论文是先提取的耕地&#xff0c;再做作物分类&#xff0c;耕地的提取代码是开源的。 但这个代码直接在云端上进行分类&#xff0c;GEE会爆内存&#xff0c;因此我准备把数据下…

Docker搭建kafka环境

系统&#xff1a;MacOS Sonoma 14.1 Docker版本&#xff1a;Docker version 27.3.1, build ce12230 Docker desktop版本&#xff1a;Docker Desktop 4.36.0 (175267) 1.拉取镜像 先打开Docker Desktop&#xff0c;然后在终端执行命令 docker pull lensesio/fast-data-dev …

校园点餐订餐外卖跑腿Java源码

简介&#xff1a; 一个非常实用的校园外卖系统&#xff0c;基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化&#xff0c;提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合&am…

Linux文件属性 --- 硬链接、所有者、所属组

三、硬链接数 1.目录 使用“ll”命令查看&#xff0c;在文件权限的后面有一列数字&#xff0c;这是文件的硬链接数。 对于目录&#xff0c;硬链接的数量是它具有的直接子目录的数量加上其父目录和自身。 下图的“qwe”目录就是“abc”目录的直接子目录。 2.文件 对于文件可…

Centos7 部署ZLMediakit

1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…

无管理员权限 LCU auth-token、port 获取(全网首发 go)

一&#xff1a; 提要&#xff1a; 参考项目&#xff1a; https://github.com/Zzaphkiel/Seraphine 想做一个 lol 查战绩的软件&#xff0c;并且满足自己的需求&#xff08;把混子和大爹都表示出来&#xff09;&#xff0c;做的第一步就是获取 lcu token &#xff0c;网上清一色…

《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制

从本节课程开始&#xff0c;我们将来介绍K8s安全框架&#xff0c;这是保障K8s集群安全比较关键的安全机制。接下来&#xff0c;让我们一起来探索K8s安全框架的运行机制。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; K8s安全框架&#xff1a;由认证、鉴权和准入控…

spring\strust\springboot\isp前后端那些事儿

后端 一. 插入\更新一条数据&#xff08;老&#xff09; Map<String, Object> parameterMap MybatisUtil.initParameterSave("Send_ProjectFrozenLog", sendProjectFrozenLog); commonMapper.insert(parameterMap);parameterMap MybatisUtil.initParameter…

UE5安装Fab插件

今天才知道原来Fab也有类似Quixel Bridge的插件&#xff0c;于是立马就安装上了&#xff0c;这里分享一下安装方法 在Epic客户端 - 库 - Fab Library 搜索 Fab 即可安装Fab插件 然后重启引擎&#xff0c;在插件面板勾选即可 然后在窗口这就有了 引擎左下角也会多出一个Fab图标…

对于使用exe4j打包,出现“NoClassDefFoundError: BOOT-INF/classes”的解决方案

jar使用exe4j打包exe&#xff0c;出现NoClassDefFoundError: BOOT-INF/classes 注意选取的jar包是使用build&#xff0c;而不是maven中的install 本文介绍解决这个方法的方案 点击Project Structure 按照如图所示选择 选择main class&#xff0c;选择你要打的main 如果遇到/M…

文件上传之文件内容检测

一.基本概念 介绍&#xff1a;文件内容检测就是检测上传的文件里的内容。 文件幻数检测 通常情况下&#xff0c;通过判断前10个字节&#xff0c;基本就能判断出一个文件的真实类型。 文件加载检测 一般是调用API或函数对文件进行加载测试。常见的是图像渲染测试&#xff0c;再…