unity的自动寻路之 ------ wayPoint寻路的实现方式

article/2025/10/13 9:32:47

孙广东 2015.6.28

看了看  Unity的官方案例,就顺便看了 wayPoint相关。

效果:


WaypointProgressTracker.cs  【固定】

WaypointCircuit.cs  【固定】


using System;
using System.Collections;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;#endifnamespace UnityStandardAssets.Utility
{public class WaypointCircuit : MonoBehaviour{public WaypointList waypointList = new WaypointList();[SerializeField] private bool smoothRoute = true;private int numPoints;private Vector3[] points;private float[] distances;public float editorVisualisationSubsteps = 100;public float Length { get; private set; }public Transform[] Waypoints{get { return waypointList.items; }}// 这是在这里会保存 GC 分配private int p0n;private int p1n;private int p2n;private int p3n;private float i;private Vector3 P0;private Vector3 P1;private Vector3 P2;private Vector3 P3;// Use this for initializationprivate void Awake(){if (Waypoints.Length > 1){CachePositionsAndDistances();}numPoints = Waypoints.Length;}public RoutePoint GetRoutePoint(float dist){// 位置和方向Vector3 p1 = GetRoutePosition(dist);Vector3 p2 = GetRoutePosition(dist + 0.1f);Vector3 delta = p2 - p1;return new RoutePoint(p1, delta.normalized);}public Vector3 GetRoutePosition(float dist){int point = 0;if (Length == 0){Length = distances[distances.Length - 1];}dist = Mathf.Repeat(dist, Length);while (distances[point] < dist){++point;}// get nearest two points, ensuring points wrap-around start & end of circuit// 得到最近的两个点,确保点环绕电路的开始与结束p1n = ((point - 1) + numPoints)%numPoints;p2n = point;// found point numbers, now find interpolation value between the two middle points// 发现点的数目,现在找到中间两点之间内的一个插值i = Mathf.InverseLerp(distances[p1n], distances[p2n], dist);if (smoothRoute){// 有关两点之间的光滑catmull-rom计算。// 获取周围的 2 点的指数,因为catmull-rom 函数要求四个点p0n = ((point - 2) + numPoints)%numPoints;p3n = (point + 1)%numPoints;// 第二个点可能已经是最后的 'last' 点了 - a dupe of the first,// (to give a value of max track distance instead of zero)// but now it must be wrapped back to zero if that was the case.p2n = p2n%numPoints;P0 = points[p0n];P1 = points[p1n];P2 = points[p2n];P3 = points[p3n];return CatmullRom(P0, P1, P2, P3, i);}else{// 两个点之间的简单线性插值:p1n = ((point - 1) + numPoints)%numPoints;p2n = point;return Vector3.Lerp(points[p1n], points[p2n], i);}}private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i){// 这是 catmull-rom 方程// Un-magic this, lord vector!return 0.5f*((2*p1) + (-p0 + p2)*i + (2*p0 - 5*p1 + 4*p2 - p3)*i*i +(-p0 + 3*p1 - 3*p2 + p3)*i*i*i);}private void CachePositionsAndDistances(){// transfer the position of each point and distances between points to arrays for// speed of lookup at runtime// 在运行时查找到阵列速度点之间传输的每个点和距离的位置points = new Vector3[Waypoints.Length + 1];distances = new float[Waypoints.Length + 1];float accumulateDistance = 0;for (int i = 0; i < points.Length; ++i){var t1 = Waypoints[(i)%Waypoints.Length];var t2 = Waypoints[(i + 1)%Waypoints.Length];if (t1 != null && t2 != null){Vector3 p1 = t1.position;Vector3 p2 = t2.position;points[i] = Waypoints[i%Waypoints.Length].position;distances[i] = accumulateDistance;accumulateDistance += (p1 - p2).magnitude;}}}private void OnDrawGizmos(){DrawGizmos(false);}private void OnDrawGizmosSelected(){DrawGizmos(true);}private void DrawGizmos(bool selected){waypointList.circuit = this;if (Waypoints.Length > 1){numPoints = Waypoints.Length;CachePositionsAndDistances();Length = distances[distances.Length - 1];Gizmos.color = selected ? Color.yellow : new Color(1, 1, 0, 0.5f);Vector3 prev = Waypoints[0].position;if (smoothRoute){for (float dist = 0; dist < Length; dist += Length/editorVisualisationSubsteps){Vector3 next = GetRoutePosition(dist + 1);Gizmos.DrawLine(prev, next);prev = next;}Gizmos.DrawLine(prev, Waypoints[0].position);}else{for (int n = 0; n < Waypoints.Length; ++n){Vector3 next = Waypoints[(n + 1)%Waypoints.Length].position;Gizmos.DrawLine(prev, next);prev = next;}}}}[Serializable]public class WaypointList{public WaypointCircuit circuit;public Transform[] items = new Transform[0];}public struct RoutePoint{public Vector3 position;public Vector3 direction;public RoutePoint(Vector3 position, Vector3 direction){this.position = position;this.direction = direction;}}}
}/// 编辑器对 Inspector面板的定制
namespace UnityStandardAssets.Utility.Inspector
{
#if UNITY_EDITOR[CustomPropertyDrawer(typeof (WaypointCircuit.WaypointList))]public class WaypointListDrawer : PropertyDrawer{private float lineHeight = 18;private float spacing = 4;public override void OnGUI(Rect position, SerializedProperty property, GUIContent label){EditorGUI.BeginProperty(position, label, property);float x = position.x;float y = position.y;float inspectorWidth = position.width;// 绘制 label// 不要缩进的子字段var indent = EditorGUI.indentLevel;EditorGUI.indentLevel = 0;var items = property.FindPropertyRelative("items");var titles = new string[] {"Transform", "", "", ""};var props = new string[] {"transform", "^", "v", "-"};var widths = new float[] {.7f, .1f, .1f, .1f};float lineHeight = 18;bool changedLength = false;if (items.arraySize > 0){for (int i = -1; i < items.arraySize; ++i){var item = items.GetArrayElementAtIndex(i);float rowX = x;for (int n = 0; n < props.Length; ++n){float w = widths[n]*inspectorWidth;// Calculate rects// 计算 rectsRect rect = new Rect(rowX, y, w, lineHeight);rowX += w;if (i == -1){EditorGUI.LabelField(rect, titles[n]);}else{if (n == 0){EditorGUI.ObjectField(rect, item.objectReferenceValue, typeof (Transform), true);}else{if (GUI.Button(rect, props[n])){switch (props[n]){case "-":items.DeleteArrayElementAtIndex(i);items.DeleteArrayElementAtIndex(i);changedLength = true;break;case "v":if (i > 0){items.MoveArrayElement(i, i + 1);}break;case "^":if (i < items.arraySize - 1){items.MoveArrayElement(i, i - 1);}break;}}}}}y += lineHeight + spacing;if (changedLength){break;}}}else{// add button// 添加"+"按钮var addButtonRect = new Rect((x + position.width) - widths[widths.Length - 1]*inspectorWidth, y,widths[widths.Length - 1]*inspectorWidth, lineHeight);if (GUI.Button(addButtonRect, "+")){items.InsertArrayElementAtIndex(items.arraySize);}y += lineHeight + spacing;}// add all button// 添加所有按钮var addAllButtonRect = new Rect(x, y, inspectorWidth, lineHeight);if (GUI.Button(addAllButtonRect, "Assign using all child objects")){var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;var children = new Transform[circuit.transform.childCount];int n = 0;foreach (Transform child in circuit.transform){children[n++] = child;}Array.Sort(children, new TransformNameComparer());circuit.waypointList.items = new Transform[children.Length];for (n = 0; n < children.Length; ++n){circuit.waypointList.items[n] = children[n];}}y += lineHeight + spacing;// rename all button// 重命名所有按钮var renameButtonRect = new Rect(x, y, inspectorWidth, lineHeight);if (GUI.Button(renameButtonRect, "Auto Rename numerically from this order")){var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;int n = 0;foreach (Transform child in circuit.waypointList.items){child.name = "Waypoint " + (n++).ToString("000");}}y += lineHeight + spacing;// Set indent back to what it was// 设置缩进EditorGUI.indentLevel = indent;EditorGUI.EndProperty();}public override float GetPropertyHeight(SerializedProperty property, GUIContent label){SerializedProperty items = property.FindPropertyRelative("items");float lineAndSpace = lineHeight + spacing;return 40 + (items.arraySize*lineAndSpace) + lineAndSpace;}// comparer for check distances in ray cast hits// 比较器检查射线发射命中的距离public class TransformNameComparer : IComparer{public int Compare(object x, object y){return ((Transform) x).name.CompareTo(((Transform) y).name);}}}
#endif
}



using System;
using UnityEngine;namespace UnityStandardAssets.Utility
{public class WaypointProgressTracker : MonoBehaviour{// 此脚本可以用于任何对象,支持沿着一条路线为标志的waypoints。// 此脚本管理向前看沿路线的数量,并跟踪进度和圈数。[SerializeField] private WaypointCircuit circuit; // 一个引用关于 waypoint-based 我们应该沿着的路线[SerializeField] private float lookAheadForTargetOffset = 5;// 我们将目标,沿着路线的偏移[SerializeField] private float lookAheadForTargetFactor = .1f;// 一个倍数,增加目标与沿着线路的距离,基于当前速度[SerializeField] private float lookAheadForSpeedOffset = 10;// 前面的偏移的唯一路线速度调整(作为waypoint目标的rotation变换)[SerializeField] private float lookAheadForSpeedFactor = .2f;// 一个倍数,沿着路线调整速度添加距离[SerializeField] private ProgressStyle progressStyle = ProgressStyle.SmoothAlongRoute;// 进度样式:是否smoothly更新位置沿着路线(好的曲线路径)或只是正常到达每个航点。[SerializeField] private float pointToPointThreshold = 4;// 接近waypoint的阈值,一旦达到这个值,目标将切换到下一个目标地点:只用于PointToPoint模式。public enum ProgressStyle{SmoothAlongRoute,PointToPoint,}// 这些是public,由其他对象读取。让一个AI知道在哪里头!public WaypointCircuit.RoutePoint targetPoint { get; private set; }public WaypointCircuit.RoutePoint speedPoint { get; private set; }public WaypointCircuit.RoutePoint progressPoint { get; private set; }public Transform target;private float progressDistance; // 圆形(环形)路线的进展,平滑smooth模式中使用。private int progressNum; // 当前waypoint数,点对点point-to-point模式中使用。private Vector3 lastPosition; // 用于计算当前速度(因为我们可能没有一个刚体组件)private float speed; // 此对象的当前速度(从最后一帧的delta计算) // 设置脚本属性private void Start(){
// 我们使用transform表示目标点,这个点被认为是为即将到来的变化的速度点。这允许此信息传递给 AI 而无需进一步依赖此组件。您可以手动创建transform设置该组件 *and* AI,然后此组件将更新它,和 AI 可以阅读它。if (target == null){target = new GameObject(name + " Waypoint Target").transform;}Reset();}// 对象重置为合理的值public void Reset(){progressDistance = 0;progressNum = 0;if (progressStyle == ProgressStyle.PointToPoint){target.position = circuit.Waypoints[progressNum].position;target.rotation = circuit.Waypoints[progressNum].rotation;}}private void Update(){if (progressStyle == ProgressStyle.SmoothAlongRoute){// 确定我们目前目标的位置 (这是不同于当前的进展位置,它是一个确定值沿着前方路线的) 我们使用 lerp 作为一种随着时间的推移速度进行平滑处理的简单方式。if (Time.deltaTime > 0){speed = Mathf.Lerp(speed, (lastPosition - transform.position).magnitude/Time.deltaTime,Time.deltaTime);}target.position =circuit.GetRoutePoint(progressDistance + lookAheadForTargetOffset + lookAheadForTargetFactor*speed).position;target.rotation =Quaternion.LookRotation(circuit.GetRoutePoint(progressDistance + lookAheadForSpeedOffset + lookAheadForSpeedFactor*speed).direction);// 得到我们的当前路线的进展progressPoint = circuit.GetRoutePoint(progressDistance);Vector3 progressDelta = progressPoint.position - transform.position;if (Vector3.Dot(progressDelta, progressPoint.direction) < 0){progressDistance += progressDelta.magnitude*0.5f;}lastPosition = transform.position;}else{// 点对点模式。 如果我们足够近,只是增加Waypoint:Vector3 targetDelta = target.position - transform.position;if (targetDelta.magnitude < pointToPointThreshold){progressNum = (progressNum + 1)%circuit.Waypoints.Length;}target.position = circuit.Waypoints[progressNum].position;target.rotation = circuit.Waypoints[progressNum].rotation;// 得到我们的当前路线的进展progressPoint = circuit.GetRoutePoint(progressDistance);Vector3 progressDelta = progressPoint.position - transform.position;if (Vector3.Dot(progressDelta, progressPoint.direction) < 0){progressDistance += progressDelta.magnitude;}lastPosition = transform.position;}}private void OnDrawGizmos(){if (Application.isPlaying){Gizmos.color = Color.green;Gizmos.DrawLine(transform.position, target.position);Gizmos.DrawWireSphere(circuit.GetRoutePosition(progressDistance), 1);Gizmos.color = Color.yellow;Gizmos.DrawLine(target.position, target.position + target.forward);}}}
}




最后: 看看Unity5的 sample中的 Car 和 飞机 的AI案例中:









http://chatgpt.dhexx.cn/article/4CfiaKD4.shtml

相关文章

【Unity】关于Waypoint的寻路

创建一个名为Path的C#脚本 <span style"font-size:24px;"><span style"font-size:24px;">using UnityEngine; using System.Collections; using System.Collections.Generic; public class Enemy : MonoBehaviour {public float MoveSpeed 3.…

Autoware实车测试记录(四)--全局及局部路径规划相关功能以及发送控制指令至底盘

经过前面的一系列工作&#xff0c;现在小车在地图中任意位置已经可以实现实时定位以及检测到障碍物以及追踪这些障碍物。下面的内容主要是继续完成下一步-路径规划&#xff0c;这其中包括了全局宏观上的路径规划&#xff08;全局路径规划&#xff09;以及在行走过程中对障碍物进…

ROS-使用命令发布导航目标点(publish point)

目录 手动发布rviz中的publish point思路方法实现publish point的发布的话题“/clicked_point”发布“/clicked_point”内容 使用脚本发布一系列的点疑惑补充 手动发布rviz中的publish point 我在利用move—base跑仿真时&#xff0c;是使用的rviz中的publish point按钮&#x…

大疆无人机安卓Mobile Sdk开发(三)制定航点任务WaypointMission

大疆无人机安卓Mobile Sdk开发&#xff08;一&#xff09;简单介绍 大疆无人机安卓Mobile Sdk开发&#xff08;二&#xff09;连接无人机&#xff0c;获取无人机信息 大疆无人机安卓Mobile Sdk开发&#xff08;三&#xff09;制定航点任务WaypointMission 大疆无人机安卓Mob…

ROS学习记录(四)基于ROS的A*算法仿真

代码来源:https://github.com/KailinTong/Motion-Planning-for-Mobile-Robots/tree/master/hw_2. 文章目录 前言一、获取代码二、过程演示1.启动roscore2.打开rviz3.打开rviz文件4.新建终端加载地图5.进行路径搜索 三、ROS包node.hAstar_searcher.hAstar_searcher.cppdemo_node…

利用Random Waypoint Model生成室内轨迹数据

利用Random Waypoint Model生成室内轨迹数据 1 模型简介1.1 Random Waypoint Model1.2 Random Walk Model1.3 Random Direction Model 2 生成轨迹数据链接 在做室内定位方面的实验时&#xff0c;打算利用RNN进行室内定位&#xff0c;而利用RNN做定位则需要室内行人的轨迹数据做…

pinpoint和skyWalking

首先&#xff0c;上个别人的研究成果&#xff0c;我也是踩着巨人的肩膀继续前进的。 随着pinpoint版本的迭代更新&#xff0c;这图上的结论有些已经过时了&#xff0c;比如pinpoint方面&#xff1a; 1.协议&#xff0c;最新2.1.0版本也是默认使用gRPC的&#xff1b; 2.TraceI…

航迹大师(Waypoint Master)怎么样

WayPoint Master(航迹大师)是一款针对大疆无人机倾斜摄影测量的专业级航线定制软件。 主要可分为&#xff1a;环绕航线、仿地航线、仿面航线、Lidar航线、电力航线。 1.区域环绕&#xff1a;生成区域交叉环绕航线&#xff0c;可以增强模型细节并有效减少外业照 片数量&#xff…

Autoware学习笔记waypoint_follower之pure_pursuit

1.pure_pursuit的launch文件如下。 <!-- --> <launch><arg name"is_linear_interpolation" default"True"/> <arg name"publishes_for_steering_robot" default"False"/> <!-- rosrun waypoint_fol…

寻路 Waypoint 与 NavMesh 比较

正文 1. WayPoint寻路 下图是一个典型的路点寻路 另一种方法是使用多边形来记录路径信息&#xff0c;它可以提供更多的信息给ai角色使用。下图就是一个navigation mesh。 以下列出几个WayPoint的不足之处&#xff1a; 一些复杂的游戏地图需要的WayPoint数量过于庞大有时会使角色…

CARLA 笔记(07)— 地图和导航(Landmarks、Waypoints、Lanes、Junctions、Environment Objects、路径点导航、地图导航、分层和非分层地图)

1. 地图 地图包括城镇的 3D 模型和道路定义。地图的道路定义基于 OpenDRIVE 文件&#xff0c;这是一种标准化的带注释的道路定义格式。 OpenDRIVE 定义道路、车道、路口等的方式决定了 Python API 的功能以及做出决策背后的原因。 1.1 更换地图 要改变地图&#xff0c;世界…

【Autoware】三、ROSBAG生成waypoint

1.启动Autoware cd ~/autoware.ai/ source install/setup.bash roslaunch runtime_manager runtime_manager.launch2.切换到Simulation模块 点击右侧的Ref&#xff0c;选择文件&#xff1a; /.autoware/sample_moriyama_150324.bag点击Play按钮以后&#xff0c;立马点击Paus…

第五篇:AWS deepracer student 赛道分析(Ace speedway)最佳路径,数据分析,waypoint分析(初步

文章目录 前言一,为什么需要分析赛道二&#xff0c;分析赛道需要的东西三&#xff0c;如何获得waypoint数据四&#xff0c;正式开始1.获取waypoint的数据2.处理数据 三&#xff0c;导入excel表绘图1.将txt文件导入excel表2.插入散点图3.成品图带有标识的版本最佳路径图&#xf…

unity3d WayPoint路点寻路,AI

前言 一个简单的人工智能WayPoint WayPoint: 游戏中敌人根据几个巡逻点自动巡逻&#xff0c;在巡逻过程中&#xff0c;时刻监听英雄&#xff08;敌人&#xff09;和自己距离是否达到追击范围&#xff08;不巡逻&#xff0c;追击英雄&#xff09;&#xff0c;在追击过程中&…

Unity中的AI算法和实现1-Waypoint

本文分享Unity中的AI算法和实现1-Waypoint 在Unity中, 我们有一些有趣且常见的AI算法可以研究和使用, 其中最常见的就是怪物的简单AI, 比如在RPG游戏里, 怪物在某些点定点巡逻, 当玩家进入检测区域时, 怪物会主动追击玩家, 追击到玩家后对玩家进行伤害, 或者在超过最大距离后脱…

统计中的“不相关”与“线性无关”

以上思维导图&#xff0c;看完即可理解。下述是文字介绍。 这二者是统计新手与老手都很容易混淆的两个概念&#xff0c;以下辨明一下&#xff1a; 两变量“不相关” 不相关是指二者互相独立&#xff0c;没有相关关系。注如森林里每棵树的树叶个数与村子里每个村民的体重...二…

辨析“正交”与“不相关”。

①不相关的定义是&#xff1a; ②正交的定义是&#xff1a; 若两个向量的点积为零&#xff08;即对应元素相乘之后求和为零&#xff09;&#xff0c;则称两个向量正交。 ③一对正交向量一定是不相关的&#xff0c;即正交的两个向量中&#xff0c;一个向量绝不可能用另一个向量…

【数理统计】随机变量X和Y独立一定不相关,不相关不一定独立

假设(X,Y) 均匀分布在单位元 x 2 y 2 1 x^2 y^2 1 x2y21上&#xff1a; X和Y的&#xff08;线性&#xff09;相关系数是0。为什么呢&#xff1f;直观来说&#xff0c;因为是个圆&#xff0c;如果你画一条线性回归的线&#xff0c;线的斜率是正的还是负的都不合适&#xf…

mysql的相关子查询和不相关子查询

概念介绍 嵌套在其他查询中的查询即子查询&#xff0c;子查询也叫内部查询。子查询中有相关子查询和不相关子查询&#xff1a;相关子查询是指查询结果依赖于外部查询的子查询&#xff0c;外部查询每执行一次&#xff0c;内部子查询也会执行一次&#xff1b;而不相关子查询是指…

MySQL中不相关子查询和相关子查询

嵌套在其它查询中的查询称之为子查询或内部查询。 包含子查询的查询称之为主查询或外部查询 student表 course表 score表 不相关子查询 内部查询的执行独立于外部查询&#xff0c;内部查询仅执行一次&#xff0c;执行完毕后将结果作为外部查询的条件使用 select * from sco…