[Unity] Dreamteck Splines实现沿路径移动功能

news/2024/9/20 9:42:12/文章来源:https://www.cnblogs.com/ThousandPine/p/18301767

Dreamteck Splines实现沿路径移动功能

最近有一个“让物体沿固定路径移动”的需求,因此接触到了Dreamteck Splines插件。

Dreamteck Splines可以很方便地绘制各种插值曲线,但在实现物体移动的时候却遇到了很多坑,因此在这里记录一下。

1. 绘制路径线

首先,让我们在场景上创建一个空物体,并添加SplineComputer组件。

img

由于我这是个2D项目,所以选择在Z平面上绘制。

img

之后编辑器中就会显示出跟随鼠标的网格线,点击左键就可以逐点绘制Spline了。

img

img

在右侧选项中可以修改Spline的类型。默认类型是Catmull Rom,我们可以把它改成直线Linear

img

img

2. 获取坐标

SplineComputer类有两类获取坐标点的方法:

  • GetPoint(int)
  • GetPoints()

这两个方法用于获取我们手动添加的坐标点,也就是我们上图中的那三个点。这明显不符合我们“沿路径移动”的需求。

而下面这三个方法才是返回Spline上的所有坐标点

  • Evaluate(double)
  • EvaluatePosition(double)
  • EvaluatePositions()

其中,EvaluateEvaluatePosition的区别在于,Evaluate返回的是SplineSample对象,包括了坐标、朝向、颜色、百分比等信息,而EvaluatePosition则是简单地返回一个Vector3的坐标。在只需要坐标的情况下,推荐使用更加轻量化的EvaluatePosition

3. 移动示例(踩坑)

EvaluatePosition(double)方法传入一个0~1的值,就会返回Spline上对应的坐标,因此我们可以用这个脚本手动模拟物体的移动过程。

using Dreamteck.Splines;
using UnityEngine;public class Move : MonoBehaviour
{[Range(0, 1)]public float Percent;public Transform Target;private SplineComputer spline;void Start(){spline = GetComponent<SplineComputer>();}void Update(){Target.position = spline.EvaluatePosition(Percent);}
}

将脚本挂载到SplineComputer所在的物体上,拖动右侧的滑动条即可移动目标物体。

img

一切看似十分正常,直到我们又添加了一条长度不同的线段。

img

可以明显地看出,在后面这个较短的路径中,物体的移动速度明显变慢了。

当我们直接将进度设为0.5后,便能发现问题所在。

img

目标物体移动并没有移动到Spline的终点,而是移动到了我们设置的第二个控制点上。

这个问题在官方文档的3.3. Sample Mode中有对应的解答:

默认情况下,样条曲线(Spline)在 [0-1] 的百分比范围内进行计算(evaluated),涵盖了所有坐标点。

例如,一条由 3 个点组成的样条曲线,计算百分比为 0.5 的坐标点,将始终返回第二个点的位置,因为它位于中间。

img

然而,如果第一个点和第二个点非常接近,而第三个点距离它们很远,计算百分比为 0.5 的坐标点不会返回样条曲线的中间位置,它仍将返回第二个点。因为某些区域的采样点比其他区域更密集

img

为了说明这一点,以下是显示了采样点密度的样条曲线:每条垂直线表示一个采样点(spline sample)。在这种情况下,点 1 和点 2 之间有 10 个采样点,但点 2 和点 3 之间也只有 10 个采样点。

img

从中我们可以看出,问题的根源在于,Evaluate参数中的percent并不是指Spline长度的百分比,而是表示Spline采样点的百分比。而采样点的不均匀分布,导致了采样点百分比和长度百分比不一致的情况。

5 方法一:修改采样模式(Sample Mode)

前面我们提到"采样点的不均匀分布,导致了采样点百分比和长度百分比不一致的情况"。

反过来说,我们只需要让采样点能够均匀分布,就可以解决这一问题。

SplineComputer提供了三种采样模式(Sample Mode):

  • Default(默认):两点间的采样点数量固定
    img
  • Uniform(均匀):根据Spline长度,均匀分布采样点。但在Spline较长时会有更大的性能开销。
    img
  • Optimized(优化):与默认模式相同,但会执行优化操作删除不必要的采样点
    img

所以我们需要选择Uniform模式,以实现均匀分布采样点的需求。

请注意,在Default和Optimized模式下,当移动控制点时,Spline仅更新受该点影响的区域中的采样点。而在Uniform模式下,将重新计算整个Spline。在Optimized模式下,还提供了一个额外的滑块来控制优化的角度阈值。

img

这样一来就能正确地匀速移动目标了

img

但是!

除了性能开销外,这个方法还会带来一系列问题。

首先,它会导致线段脱离控制点:

img

其次,它还有个很致命的BUG

Uniform模式下,如果你用CalculateLength方法获取Spline的长度,那么初始状态下将会始终返回0。此时必须对他"进行一些操作",比如移动控制点,修改其他参数等,让他响应一次变化。之后CalculateLength才能正确返回数值。

img

6 方法二:使用Travel函数(推荐)

为了避免上述问题,我们可以使用Travel函数计算某个长度在Spline上对应的采样点百分比。

它的使用方法在官方文档的20.4. Converting World Units to Spline Percentages中有所提及。

假如我们要获取Spline中心点的坐标,只需要传入Spline长度的一半,也就是spline.CalculateLength() / 2,然后Travel函数就会返回对应的percent。这时再调用EvaluatePosition(percent)即可得到中心点的坐标。其他位置的坐标也是同理,我们只需要给出对应的长度即可获取坐标。

这样一来,我们就可以方便地实现沿路径匀速移动的功能了。

using Dreamteck.Splines;
using UnityEngine;public class Move : MonoBehaviour
{[Range(0, 1)]public double Percent;public float Speed;public Transform Target;private float distance;private SplineComputer spline;void Start(){spline = GetComponent<SplineComputer>();distance = 0;}void Update(){distance += Speed * Time.deltaTime;// 有需要的话可以用这个限制上限// distance = Math.Min(distance, spline.CalculateLength()); Percent = spline.Travel(0, distance);Target.position = spline.EvaluatePosition(Percent);if (Percent == 1){// do somethingDebug.Log("Done");}}
}

效果如下

img

img

参考资料

Dreamteck Splines – User Manual


本文发布于2024年7月14日

最后编辑于2024年7月14日

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/743601.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

银河麒麟V10SP1搭建oracle19c(单库)

遇到的坑:1.PRVG-0282问题解决:在先决条件检查步骤,PRVG-0282:无法检索操作系统分发ID的报错,该问题是由于字符集和环境变量问题,只需在执行安装前:使用oracle用户登录,不要root跳到oracle用户下 export CV_ASSUME_DISTID=RHEL7.6 export LANG=en_US 然后刷新环境变量 …

2021杭电多校10 D.Pty hates prime numbers题解

前言 暑期第三次组队赛是选的21年杭电多校10,遗憾爆0,被对面队打爆,赛后狠狠补题。这道题的题解,以及网上搜到的其他题解看了好久没看懂,在问了队里大腿多次后,总算磨出来了,这里讲一下我的理解。 题意 多次询问,每次给定 \(n\) 和 \(k\), 如果一个数的质因数里包括前…

DOS命令行

DOS命令行 CMD的方式开始 + 系统 + 命令提示符 Win键 + R 输入CMD打开控制台适用 任意文件夹下面 按住Shift + 鼠标右击点击 + 在此处打开命令行窗口 在资源管理器地址栏加上CMD路径 管理员方式运行:选择以管理员方式运行常用的DOS命令盘符切换:D: 查看文件所有目录:dir 切换…

总表拆分

问题:总表拆分 将以下总表按A列拆分为若干工作表函数法: 新建五个工作表,筛选标题后粘贴到这五个表中 同时选取五个工作表,使用以下公式:=FILTER(总表!A:B,总表!A:A=INDEX(UNIQUE(总表!A:A),SHEET()))数据透视表法: 复制标题1列(条件列),插入数据透视表,复制的标题1列…

基于uniapp+springboot的记账小程序

小程序视频链接:https://www.bilibili.com/video/BV1hi421Y7BE/?vd_source=cd3ceb58125e43fa5f95caf874aec5ef 1.登录 2.注册 3.我的 4.账单 5.记账6.图表 7.明细

计算工作日

问题:当前日期是当月最后2个工作日,则显示次月第一个工作日,否则显示当前日期 函数公式解决:=IF(SUM(N(WORKDAY.INTL(EOMONTH(A2,0)+1,{-2,-1},"0000000",F:F)=A2)),WORKDAY.INTL(EOMONTH(A2,0),1,"0000000",F:F),A2)WorkDay.intl第三参数使用“000000…

推荐几个好用的开源工作流引擎系统

目前基于Java语言开发的主流开源工作流引擎有osworkflow、jbpm、activiti、flowable、camunda。其中osworkflow、jbpm技术较老已经过时,activiti包括activiti5、activiti6、activiti7三个版本,flowable分开源版和商业版,camunda包括camunda7和camunda8两个系列的版本。这么多…

添加友情链接~

欢迎添加友链本文介绍下友情链接 ‍ 什么是友情链接 引自百度百科:友情链接也称为友链、交换链接、互惠链接、互换链接等,是具有一定互补优势的网站之间的简单合作形式,即分别在自己的网站上放置对方网站的 LOGO 或网站名称并设置对方网站的超级链接,使得用户可以从合作网站…

查找的问题

问题:根据片区名称和项目名称找出右表中对应的数据函数公式解决:油腻套路 =VLOOKUP($B$2,$G$5:$S$7,ROW(A1)*4+COLUMN(A1)-3,) 清新套路 =WRAPROWS(XLOOKUP(B2,G5:G7,H5:S7),4)ROW(A1)*4+COLUMN(A1)-3右拉下拉生成第一行2、3、4、5第二行6、7、8、9第三行10、11、12、13的序列…

重复的第二行前插入一行

问题:在重复序列数的第二行前插入一行Countif+查找法: 使用以下公式返回每个值出现的次数=COUNTIF(A$3:A3,A3)选取B列》查找》查找内容:2》查找范围:值》查找全部Ctrl+A选取所有查找结果后关闭查找对话框 在单元格上方插入一行 纯公式法:=LET(x,A3:A13,y,SORT(VSTACK(UNIQ…

OpenFeign 服务调用与负载

需要建两个工程,一个是服务提供者,一个是服务调用者 服务提供者 一个普通的 nacos 服务,增加一个 controller 方法即可,上一篇文章刚说了,这里简单说下 <!-- 服务提供者不调用其他服务,所以只需要注册到 nacos 的依赖 --> <dependency><groupId>com.al…

STMF4串口通信使用

目录STMF4串口通信使用USART的使用流程注意 STMF4串口通信使用前提回顾: 串口通信概念介绍1 STM32串口通信概念介绍2 针对特定stm32F4板子的串口接线折叠文本 USART指的是通用同步异步收发器,是STM32中的串行通信设备,STM32F407ZET6一共提供了6个串行接口供用户使用,其中4个…