unity A星尋路

算法 fCost = gCost + hCost
gCost 是當前節點到移動起始點的消耗,hCost是當前節點到終點的消耗
網格為變成為1的矩形,左右相鄰的兩個網格直接的gCost為1,斜對角相鄰的兩個網格的gCost為1.4
hCost 當前網格到終點網格的 水平距離 + 垂直距離
比如當前網格位置是(2,3),終點位置(10,8),則hCost = (10-2) + (8-3)
原始的算法是 fCost = gCost + hCost,均勻計算周圍網格
對于一些比較復雜的散裝障礙物場景,我們可以適當調整gCost和hCost的權重,比如 fCost = gCost x 4 + hCost x 6,這個修改會讓尋路的時候,在消耗相同的網格點上,優先在距離終點位置更近的網格周圍尋路。當我們從(1,1)向(3,10)位置尋路的時候,我們在店(3,1)和點(1,10)兩個位置的 gCost + hCost 是相等的,但是點(1,10)其實距離終點更近一些,用調整過權重的算法,(1,10)我們得出來的的值會小一些,就可以優先從(1,10)位置尋找可移動路徑

代碼如下
尋路節點

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class AstarNode
{public int gridX;public int gridY;public Vector3 position;public float hCost;public float gCost;public float fCost {get{return gCost + hCost;}}/// <summary>/// 優先處理距離終點近的node/// </summary>public float fCostNew {get{return gCost*4 + hCost*6;}}public bool walkable;public AstarNode parent;public AstarNode(){}public AstarNode(int x, int y, Vector3 pos, bool canWalk){gridX = x;gridY = y;position = pos;walkable = canWalk;}}

地圖網格生成

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Random = System.Random;
public enum SearchType
{NeighborFour,NeighborEight,
}
public class MapGridsManager : MonoBehaviour
{public int width = 100;public int height = 100;public float cellSize = 1f;public Mesh mesh;private Vector3 originPoint;private AstarNode[,] grids;public AStartManager manager;public AstartMoveTest moveTarget;public bool isUseCache = false;public bool isUseNewCost = false;public SearchType searchType = SearchType.NeighborFour;[Header("障礙物生成范圍") ]public Rect obstacleRect;void Awake(){originPoint = Vector3.zero;mesh = Resources.GetBuiltinResource<Mesh>("Quad.fbx");}private void Start(){grids = new AstarNode[width, height];Dictionary<int, AstarNode> nodeDic = GetCacheData();for (int i = 0; i < width; i++){for (int j = 0; j < height; j++){int keyId = i * 100000 + j;AstarNode node;if (isUseCache && nodeDic.ContainsKey(keyId)){node = nodeDic[keyId];}else{bool isObstacal = UnityEngine.Random.Range(1, 100) > 50;isObstacal = i >= obstacleRect.xMin && i < obstacleRect.xMax && j >= obstacleRect.yMin && j < obstacleRect.yMax  && isObstacal;bool warkAble = !isObstacal;node = new AstarNode(i, j, originPoint + new Vector3(i * cellSize, 0, j * cellSize), warkAble);    }grids[i, j] = node;}}serilizeNode();}void serilizeNode(){StringBuilder sb = new StringBuilder();for (int i = 0; i < width; i++){for (int j = 0; j < height; j++){var node = grids[i, j];int warkState = node.walkable ?1:0;if (i>0 || j > 0){sb.Append("|");}var data = $"{node.gridX}_{node.gridY}_{warkState}_{node.position.x}_{node.position.y}_{node.position.z}";sb.Append(data) ;}}string res = sb.ToString();UnityEngine.PlayerPrefs.SetString("grid_data", res);}Dictionary<int, AstarNode> GetCacheData(){Dictionary<int, AstarNode> nodeDic = new Dictionary<int, AstarNode>(width * height);string data = UnityEngine.PlayerPrefs.GetString("grid_data", "");string[] arr = data.Split("|");for (int i = 0; i < arr.Length; i++){string itemStr = arr[i];string[] itemArr = itemStr.Split("_");if (itemArr.Length < 6){continue;}AstarNode node = new AstarNode();node.gridX = int.Parse(itemArr[0]);node.gridY = int.Parse(itemArr[1]);node.walkable = int.Parse(itemArr[2]) == 1 ? true : false;float positionX = float.Parse(itemArr[3]);float positionY = float.Parse(itemArr[4]);float positionZ = float.Parse(itemArr[5]);node.position = new Vector3(positionX, positionY, positionZ);int keyId = node.gridX * 100000 + node.gridY;nodeDic.Add(keyId, node);}return nodeDic;}public AstarNode GetNode(int x, int y){if (x < 0 || x >= width || y < 0 || y >= height){return null;}return grids[x, y];}public List<AstarNode> GetNeighborNodes(AstarNode node){if (searchType == SearchType.NeighborFour)return GetNeighborFourNodes(node);else{return GetNeighborEightNodes(node);}}public List<AstarNode> GetNeighborFourNodes(AstarNode node){List<AstarNode> nodeList = new List<AstarNode>();int nodeX = node.gridX;int nodeY = node.gridY;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){if (i == 0 && j == 0){continue;}if (i == 0 || j == 0){int itemX = nodeX + i;int itemY = nodeY + j;AstarNode itemNode = GetNode(itemX, itemY);if (itemNode != null){nodeList.Add(itemNode);}}}}return nodeList;}public List<AstarNode> GetNeighborEightNodes(AstarNode node){List<AstarNode> nodeList = new List<AstarNode>();int nodeX = node.gridX;int nodeY = node.gridY;for (int i = -1; i <= 1; i++){for (int j = -1; j <= 1; j++){if (i == 0 && j == 0){continue;}int itemX = nodeX + i;int itemY = nodeY + j;AstarNode itemNode = GetNode(itemX, itemY);if (itemNode != null){nodeList.Add(itemNode);}}}return nodeList;}public AstarNode GetByWorldPositionNode(Vector3 pos){int x = Mathf.FloorToInt(pos.x / cellSize);int y = Mathf.FloorToInt(pos.y / cellSize);if (x < 0 || y < 0 || x>=width || y >= height){return null;}return grids[x, y];}void OnDrawGizmos(){Gizmos.color = Color.gray;if (grids != null){for (int i = 0; i < width; i++){for (int j = 0; j < height; j++){AstarNode node = grids[i, j];if (manager != null){if (manager.rodeList.Contains(node)){Gizmos.color = Color.white;}else if(manager.openList.Contains(node)){Gizmos.color = Color.black;}else if (manager.closeList.Contains(node)){Gizmos.color = Color.green;}else{Gizmos.color = node.walkable?  Color.blue: Color.red;}if (manager.moveEndNode != null && manager.moveEndNode == node){Gizmos.color = Color.yellow;}if (manager.moveStartNode != null && manager.moveStartNode == node){Gizmos.color = Color.gray;}}else{Gizmos.color = node.walkable?  Color.blue: Color.red;}// Gizmos.DrawCube(node.position, Vector3.one);var rotation = Quaternion.Euler(new Vector3(90, 0, 0));Gizmos.DrawMesh(mesh, node.position, rotation, Vector3.one);}}}Gizmos.color = Color.black;var halfCellSize = cellSize / 2;for (int i = 0; i <= width; i++){Gizmos.DrawLine(new Vector3(i-halfCellSize,0,0), new Vector3(i-halfCellSize, 0 ,height-halfCellSize));}for (int i = 0; i <= height; i++){Gizmos.DrawLine(new Vector3(0,0,i-halfCellSize), new Vector3(width-halfCellSize, 0 ,i-halfCellSize));}}public void ResetNodeCost(){for (int i = 0; i < width; i++){for (int j = 0; j < height; j++){var node = grids[i, j];node.gCost = 0;node.hCost = 0;}}}public void BeginMove(List<AstarNode> rodeNodeList){moveTarget.BeginMove(rodeNodeList);}
}

尋路

using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;[Serializable]
public class ObstacleRange
{public int x;
}
public class AStartManager : MonoBehaviour
{public MapGridsManager map;public Vector3 from;public Vector3 end;public List<AstarNode> openList = new List<AstarNode>();public List<AstarNode> closeList = new List<AstarNode>();public List<AstarNode> rodeList = new List<AstarNode>();public AstarNode moveStartNode;public AstarNode moveEndNode;private bool isInFindPath = false;//是否正在訓練中void Update(){if (Input.GetKeyUp(KeyCode.A)){StartCoroutine(FindPath(from, end));}else if(Input.GetMouseButtonDown(0)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit)){var pos = new Vector3(hit.point.x, hit.point.z, 0);Debug.Log($"{pos}--{Input.mousePosition}");var node = map.GetByWorldPositionNode(pos);if (node != null){ from = end;end =  pos;StartCoroutine(FindPath(from, end));}}}}void DoMove(){map.BeginMove(rodeList);}IEnumerator FindPath(Vector3 startPos, Vector3 endPos){if (isInFindPath){yield break;}isInFindPath = true;ResetNodeData();AstarNode startNode = map.GetByWorldPositionNode(startPos);startNode.gCost = 0;startNode.hCost = 0;startNode.parent = null;AstarNode endNode = map.GetByWorldPositionNode(endPos);endNode.gCost = 0;endNode.hCost = 0;endNode.parent = null;openList.Add(startNode);moveStartNode = startNode;moveEndNode = endNode;if (!endNode.walkable){Debug.LogError("終點不可抵達");isInFindPath = false;yield break;}int index = 0;while (openList.Count > 0){var targetNode = openList[0];Debug.Log($"{index}: ({targetNode.gridX} ,{targetNode.gridY})");openList.Remove(targetNode);if (targetNode.gridX == endNode.gridX && targetNode.gridY == endNode.gridY){GenerateRote(targetNode);openList.Clear();break;}AddOpenList(targetNode, endNode);if (map.isUseNewCost){openList.Sort((a, b)=>{return a.fCostNew.CompareTo(b.fCostNew);});}else{openList.Sort((a, b)=>{return a.fCost.CompareTo(b.fCost);});}yield return new WaitForSeconds(0.1f);index++;if (index > 5000){Debug.LogError("循環超出上限");break;}}Debug.Log("尋路循環次數為:" + index);DoMove();isInFindPath = false;}void ResetNodeData(){openList.Clear();closeList.Clear();rodeList.Clear();map.ResetNodeCost();}private bool AddOpenList(AstarNode targetNode, AstarNode endNode){var neighborNodes = map.GetNeighborNodes(targetNode);closeList.Add(targetNode);foreach (var item in neighborNodes){if (item.walkable == false || closeList.Contains(item) || openList.Contains(item)){continue;}float tempGCost = targetNode.gCost + GetDestance(targetNode, item);if (item.gCost <= 0 || tempGCost < item.gCost){item.parent = targetNode;item.gCost = targetNode.gCost + GetDestance(targetNode, item);item.hCost = Mathf.Abs(endNode.gridX - item.gridX) + Mathf.Abs(endNode.gridY - item.gridY);openList.Add(item);}}return false;}List<AstarNode> GenerateRote(AstarNode curNode){rodeList = new List<AstarNode>();rodeList.Add(curNode);while (curNode.parent != null){curNode = curNode.parent;rodeList.Add(curNode);}rodeList.Reverse();return rodeList;}float GetDestance(AstarNode node1, AstarNode node2){float distance  ;if (node1.gridX != node2.gridX && node1.gridY != node2.gridY){distance = 1.4f;}else{distance = 1f;}return distance; }
}

控制對象在路徑點移動

using System.Collections.Generic;
using UnityEngine;public class AstartMoveTest : MonoBehaviour
{public Transform moveTarget;//移動對象private List<AstarNode> rodeNodeList;//行走路徑點private int moveIndex;//當前移動到第幾個點private bool isMoveing = false;//是否正在移動過程中private float miniDistance = 0.1f;private Vector3 endPosition;private Vector3 moveDir;public float speed = 1;// Start is called before the first frame updatevoid Start(){}public void BeginMove(List<AstarNode> rodeNodeList){if (rodeNodeList == null || rodeNodeList.Count < 2){Debug.LogError("路徑點異常,無法開始移動");return;}this.rodeNodeList = rodeNodeList;transform.position = rodeNodeList[0].position;isMoveing = true;moveIndex = 1;endPosition = rodeNodeList[moveIndex].position;moveDir = rodeNodeList[moveIndex].position - rodeNodeList[0].position;moveDir = moveDir.normalized;}// Update is called once per framevoid Update(){if (!isMoveing){return;}float distance = Vector3.Distance(transform.position, endPosition);// Debug.Log("distance = " + distance);if (distance < miniDistance){moveIndex++;if (moveIndex >= rodeNodeList.Count){isMoveing = false;Debug.Log("移動結束");return;}endPosition = rodeNodeList[moveIndex].position;transform.LookAt(endPosition);moveDir = endPosition - transform.position;moveDir = moveDir.normalized;}// transform.position = Vector3.Lerp(transform.position, endPosition, speed*Time.deltaTime);transform.position = transform.position + moveDir * Time.deltaTime * speed;}
}

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

網格是用Gizmos中繪制的,需要調整為top視圖
圖中黃色網格是終點,灰色網格是起始點,白色是尋路結果,紅色是障礙物點
在這里插入圖片描述

資源下載地址 demo鏈接

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/88273.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/88273.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/88273.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

十一 Javascript的按值傳遞

你將知道&#xff1a;“傳遞” 值是什么意思什么是按值傳遞傳遞物品JavaScript 中沒有傳遞引用&#xff01;介紹當需要在 JavaScript 中分配或簡單地將一個值傳遞給其他標識符時&#xff0c;我們就會看到通常所說的 按值傳遞 。嚴格來說&#xff0c;JavaScript 中傳遞值的方式只…

SpringBoot ThreadLocal 全局動態變量設置

需求說明&#xff1a; 現有一個游戲后臺管理系統&#xff0c;該系統可管理多個大區的數據&#xff0c;但是需要使用大區id實現數據隔離&#xff0c;并且提供了大區選擇功能&#xff0c;先擇大區后展示對應的數據。需要實現一下幾點&#xff1a; 1.前端請求時&#xff0c;area_i…

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘logging’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘logging’問題 摘要&#xff1a; 在使用 PyCharm 2025 控制臺通過 pip install 安裝第三方庫時&#xff0c;常會遇到諸如 ModuleNotFoundError: No module name…

打破技術債困境:從“保持現狀”到成為變革的推動者

相信許多在科技行業的同行都面臨過類似的挑戰&#xff1a;明知系統存在“技術債”&#xff0c;卻因為溝通成本、團隊壓力和短期KPI等原因&#xff0c;難以推動改進&#xff0c;最終陷入“想做卻不敢做”的矛盾心態。這不僅影響個人心情&#xff0c;更重要的是&#xff0c;它像一…

Spring Boot 整合 RabbitMQ

Spring Boot 整合 RabbitMQ 一、概述&#xff1a;RabbitMQ 是什么&#xff1f; 你可以把 RabbitMQ 想象成一個「快遞中轉站」。 比如你在網上買了一本書&#xff0c;賣家&#xff08;生產者&#xff09;把包裹&#xff08;消息&#xff09;交給快遞站&#xff08;RabbitMQ&…

Unity Demo-3DFarm詳解-其一

我們來拆解一個種田游戲&#xff0c;這個游戲種類內部的功能還是比較模板化的&#xff0c;我們來一點點說。我們大體上分為這么幾個部分&#xff1a;農場運營玩法角色與玩家互動物品與背包存檔和進度管理用戶界面系統農場運營可以大體上分為&#xff1a;種植系統&#xff1a;支…

esp8266驅動下載

問題描述&#xff1a;esp8266插上電腦&#xff0c;設備管理器無法識別&#xff0c;顯示為USB serial&#xff08;黃色感嘆號&#xff09; 首先確認你的esp8266是不是 CH340 系列的 USB 轉串口芯片 CH340驅動下載地址

大語言模型的極限:知識、推理與創造力的邊界探析

大語言模型的極限&#xff1a;知識、推理與創造力的邊界探析 人工智能領域的快速發展推動了大語言模型&#xff08;LLM&#xff09;的廣泛應用&#xff0c;這些模型在文本生成、知識問答和創意表達等方面展現出前所未有的能力。然而&#xff0c;隨著應用場景的深化&#xff0c;…

git中的fork指令解釋

在Git中&#xff0c;Fork 是指將他人的代碼倉庫&#xff08;Repository&#xff09;復制到自己的賬戶下&#xff0c;創建一個完全獨立的副本[1][2]。以下是關于Fork的詳細說明&#xff1a; Fork的定義與核心作用 定義&#xff1a;Fork是代碼托管平臺&#xff08;如GitHub&#…

iPhone 抓包工具有哪些?多工具對比分析優缺點

iOS 平臺一向以安全性著稱&#xff0c;這也使得對其進行網絡調試和抓包變得異常困難。相比安卓&#xff0c;iPhone 抓包難點主要在以下幾點&#xff1a; 系統限制代理設置的靈活性無法自由安裝根證書抓包常涉及 HTTPS 解密與雙向認證破解普通用戶設備無 root 或越獄權限 因此&a…

使用 libcu++ 庫

文章目錄使用 libcu 庫安裝與設置基本組件1. 原子操作2. 內存管理3. 類型特性4. 同步原語編譯選項注意事項使用 libcu 庫 libcu 是 NVIDIA 提供的 CUDA C 標準庫實現&#xff0c;它為 CUDA 開發者提供了類似 C 標準庫的功能和接口。以下是使用 libcu 的基本指南&#xff1a; …

[Leetcode] 預處理 | 多叉樹bfs | 格雷編碼 | static_cast | 矩陣對角線

魔術排列模擬一個特定的洗牌過程&#xff0c;并找到使得經過一系列洗牌和取牌操作后&#xff0c;能夠與給定的目標數組target相匹配的最小k值核心思想: 預處理初始排列&#xff1a;從一個按順序排列的數組&#xff08;例如&#xff0c;{1, 2, 3, ..., n}&#xff09;開始。洗牌…

【技術追蹤】SynPo:基于高質量負提示提升無訓練少樣本醫學圖像分割性能(MICCAI-2025)

SAM 新用法&#xff0c;無需訓練&#xff0c;利用高質量負提示提升分割性能~ 論文&#xff1a;SynPo: Boosting Training-Free Few-Shot Medical Segmentation via High-Quality Negative Prompts 代碼&#xff1a;https://liu-yufei.github.io/synpo-project-page/ 0、摘要 大…

深入理解機器學習

一.前言本章節開始來講解一下機器學習的知識&#xff0c;本期作為一個了解就大概介紹一下&#xff0c;我們不會從機器學習基礎開始介紹&#xff0c;但是后面會來補充&#xff0c;隨著ai的不斷發展&#xff0c;機器學習在ai的領域里面的占比越來約少&#xff0c;我們還是以應用為…

數據結構 順序表(1)

目錄 1.線性表 2.順序表 1.線性表 線性表&#xff08;linear list&#xff09;是n個具有相同特性的數據元素的有限序列。線性表是一種在實際中廣泛使用 的數據結構&#xff0c;常見的線性表&#xff1a;順序表、鏈表、棧、隊列、字符串… 線性表在邏輯上是線性結構&#…

openssl 生成國密證書

openssl生成證書生成CA私鑰 openssl ecparam -genkey -name SM2 -out ca.key.pem -noout證書請求 openssl req -new -key ca.key.pem -out ca.cert.req -subj “/CNrtems-strongswan-CA”生成證書 openssl x509 -req -days 3650 -in ca.cert.req -signkey ca.key.pem -out ca.c…

系統架構設計師論文分享-論分布式事務技術及其應用

我的軟考歷程 摘要 2023年9月&#xff0c;我所在的公司通過了研發紗線MES系統的立項&#xff0c;該系統為國內紗線工廠提供SAAS服務&#xff0c;旨在提高紗線工廠的數字化和智能化水平。我在該項目中擔任系統架構設計師一職&#xff0c;負責該項目的架構設計工作。本文結合我…

東土科技智能塔機系統亮相南京,助力智能建造高質量發展

近日&#xff0c;由南京市城鄉建設委員會、江蘇省土木建筑學會主辦的“無人駕駛智能塔機觀摩會”&#xff0c;在中建三局一公司南京揚子江智慧中心項目現場成功舉辦。作為全國首批智能建造試點城市&#xff0c;南京市已出臺20余項支持政策&#xff0c;落地93個試點項目&#xf…

3D Surface Reconstruction with Enhanced High-Frequency Details

3D Surface Reconstruction with Enhanced High-Frequency Details核心問題&#xff1a;當前基于神經隱式表示&#xff08;如 NeuS&#xff09;的 3D 表面重建方法&#xff0c;通常采用隨機采樣策略。這種隨機采樣難以充分捕捉圖像中的高頻細節區域&#xff08;如紋理、邊緣、光…

Science Robotics 耶魯大學開源視觸覺新范式,看出機器人柔性手的力感知

摘要&#xff1a;在機器人視觸覺傳感領域&#xff0c;如何兼顧成本與性能始終是一大挑戰。耶魯大學在《Science Robotics》上發表最新研究&#xff0c;提出了一種“Forces for Free”&#xff08;F3&#xff09;新范式。該研究通過觀測一個經過特殊優化的開源柔性手&#xff08…