unity3D 简单实现A*算法

这篇博客介绍了如何在Unity3D中实现A*算法,用于寻找两个物体间的最短路径。文章详细讲解了F、G、H的含义及计算方法,OpenList和CloseList的作用,并提供了CS脚本代码示例,包括结点类、地图生成类、路径查找类和人物移动实现类。A*算法通过不断更新OpenList和CloseList来寻找最短路径。
摘要由CSDN通过智能技术生成
前言

在两个物体之间寻找最短有效路径

  1. 学习A*思想
  2. 其他算法,计算机估计距离
  3. 曼哈顿(城市街区算法、直来直去)
  4. 几何法(对角线,勾股定理)
  5. 估算法(先对角线,再直线)本文使用估算法计算距离
  6. GHF
  7. G : 从开始到当前位置的移动步数
  8. H : 从当前位置到目标位置的距离,使用上面3种算法获得
  9. F :G + H 的总和
  10. OpenList 和 CloseList
  11. OpenList :被考虑最短有效路径的位置集合
  12. CloseList : 不被考虑最短语效路径的位置集合
  13. 寻路思想:知道F = G + H 且知道如何计算得出,知道OpenList 和CloseList并知道如何得出
F G H 分别表示什么,有什么用,如何计算机它们?

  1. 首先得G表示的是从起始结点走到下一个结点(下下…下个)的距离,因此这个G是每走一格就会叠加一次并赋值给新的结点,从而累计得到从当前结点到下下下..个结点的距离
    newCostg = gCost + GetDistance()
    nodeCell.gCost = newCostg; //移动步数
  2. 其次是H,它表示从当前结点到终点结点的最短距离
  3. F 就是 G + H 得到的估算值

再根据百度百科的解释做个比较或许更容易理解:

f(n)=g(n)+h(n)
f(n) 是从初始状态经由状态n到目标状态的代价估计,
g(n) 是在状态空间中从初始状态到状态n的实际代价,
h(n) 是从状态n到目标状态的最佳路径的估计代价。
(对于路径搜索问题,状态就是图中的节点,代价就是距离)

这里需要写一个两点之间的距离估算方法,来计算得出G H 从而得出F
注意最好避免小数计算,
因为计算机计算机浮点的效率较低
而这里的x表示长 y表示高 可以通过画图理解,x = y 就是正方形,两点距离就是1.4 ,避免小数计算乘10 就是14整数了

    //计算距离 ,这里是因为NodeItem是嵌套类,在Grid里面 
    public int GetDistance(Grid.NodeItem a,Grid.NodeItem b) {
        //估算法
        int x = Mathf.Abs (a.x - b.x);
        int y = Mathf.Abs (a.y - b.y);
        //根号2 = 1.4  然后都扩大10倍 去除小数计算,这里返回值都放大了10倍
        if (x > y) {
            return 14 * y + 10 * (x - y);  
        }else{
            return 14 * x + 10 * (y - x);
        }
    //也可以用曼哈顿得到距离
    //return Mathf.Abs (a.x-b.x)+Mathf.Abs(a.y-b.y);//得到正数,但是可能是浮点数
    }
OpenList和CloseList是什么,可以用来做什么?

OpenList :被考虑最短有效路径的位置集合
CloseList : 不被考虑最短语效路径的位置集合

1、从第一个结点开始,放入OpenList容器,然后这个结点获取全部周围符合条件(1)的结点;
2、把符合条件的结点全部放入OpenList容器;
3、并且还要对周围符合条件(2)的每个结点进行更新他们的G值和H值,记录这个当前结点为它们的父结点;
4、然后把这第一个结点放入CloseList容器里 这是第一次循环

5、随后进入第二次循环,
此时就从OpenList容器找到符合条件(3)的当前结点,
当前结点是要在OpenList容器中H值最小,且F值最小的;
6、找到后就获取这个当前结点的周围结点,然后重新进入第2步骤到第5步骤;
7、此时它会慢慢接近终点,直到发现当前结点==最终结点;
8、最终结点找到后根据最终结点的父结点一个一个返回(类似数据结构的链表)找到开始结点;
9、此时用一个容器(List容器、Stack容器等)来接收这些结点,这些结点的Position就是最短路径

介绍符合条件:
第一个符合条件(1)是对周围结点的判断,该结点如果是标有墙类标记、或不可走标记就是不符合条件的,
第二个符合条件(2)是在满足第一个条件的前提下,在CloseList容器下遍历过,或更新后的G值比原来G值还要大的结点,2者不满足一个就是不满足条件,不能更新该结点的G,H值
第三个符合条件(3)就是存储在OpenList容器中的结点中,找一个H值最小,且, F值最小的结点,找到这个H值且F值最小的结点后,就把这个结点设置为当前结点。

当前结点在这里指为离终点结点的最短距离估计点。这个点其实并不会太智能,一下就找到最短路径。
而是根据OpenList里的全部结点来判断找到离终点最短估计值最小的结点,让周围的结点加入OpenList。然后就放入CloseList容器这个最短估计值最小的结点以后不再做任何判断。

CS脚本代码

结点类
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

  • 1
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单Unity C#脚本来实现A*算法: ```csharp using UnityEngine; using System.Collections.Generic; public class AStar : MonoBehaviour { public Transform seeker, target; // 寻路的起点和终点 Grid grid; // 存储地图信息的网格 void Awake() { grid = GetComponent<Grid>(); } void Update() { FindPath(seeker.position, target.position); } void FindPath(Vector3 startPos, Vector3 targetPos) { Node startNode = grid.NodeFromWorldPoint(startPos); // 起点所在的网格节点 Node targetNode = grid.NodeFromWorldPoint(targetPos); // 终点所在的网格节点 List<Node> openSet = new List<Node>(); // 未处理的节点集合 HashSet<Node> closedSet = new HashSet<Node>(); // 已处理的节点集合 openSet.Add(startNode); // 将起点加入未处理的节点集合 while (openSet.Count > 0) { Node currentNode = openSet[0]; // 当前处理的节点 for (int i = 1; i < openSet.Count; i++) { if (openSet[i].fCost < currentNode.fCost || (openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)) { currentNode = openSet[i]; } } openSet.Remove(currentNode); closedSet.Add(currentNode); if (currentNode == targetNode) { // 已找到终点 RetracePath(startNode, targetNode); return; } foreach (Node neighbor in grid.GetNeighbors(currentNode)) { if (!neighbor.walkable || closedSet.Contains(neighbor)) { continue; } int newMovementCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor); if (newMovementCostToNeighbor < neighbor.gCost || !openSet.Contains(neighbor)) { neighbor.gCost = newMovementCostToNeighbor; neighbor.hCost = GetDistance(neighbor, targetNode); neighbor.parent = currentNode; if (!openSet.Contains(neighbor)) { openSet.Add(neighbor); } } } } } void RetracePath(Node startNode, Node endNode) { List<Node> path = new List<Node>(); Node currentNode = endNode; while (currentNode != startNode) { path.Add(currentNode); currentNode = currentNode.parent; } path.Reverse(); grid.path = path; } int GetDistance(Node nodeA, Node nodeB) { int dstX = Mathf.Abs(nodeA.gridX - nodeB.gridX); int dstY = Mathf.Abs(nodeA.gridY - nodeB.gridY); if (dstX > dstY) { return 14 * dstY + 10 * (dstX - dstY); } else { return 14 * dstX + 10 * (dstY - dstX); } } } ``` 这个脚本依赖于一个名为`Grid`的组件,它存储了地图信息的网格。 `Grid`组件的实现不在本文的讨论范围内,你可以参考其他教程或者使用你自己的实现。 在`Update()`函数,我们不断地调用`FindPath()`函数来执行A*算法。在`FindPath()`函数,我们首先找到起点和终点所在的网格节点,然后使用A*算法找到从起点到终点的最短路径。最后,我们使用`RetracePath()`函数来反向遍历路径链表,并将路径保存在`Grid`组件的`path`变量
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值