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

unity,dreamteck,splines · 浏览次数 : 15

小编点评

本文主要介绍了如何使用Dreamteck Splines插件实现物体沿固定路径移动的功能。首先,通过绘制Spline并获取其上的坐标点,然后利用这些坐标点来实现物体的移动。在实现过程中,遇到了一些问题,如采样点不均匀分布导致的错误计算。最终,通过修改采样模式和使用Travel函数,成功解决了问题,并实现了沿路径匀速移动的目标。 1. **绘制路径线**: - 在场景上创建一个空物体,并添加SplineComputer组件。 - 在编辑器中绘制Spline,并设置其类型为直线Linear。 - 使用GetPoint和GetPoints方法获取坐标点。 2. **获取坐标**: - 使用EvaluatePosition方法获取Spline上的坐标点。 - 使用Evaluate方法获取包含更多信息的SplineSample对象。 3. **移动示例(踩坑)**: - 使用EvaluatePosition方法手动模拟物体的移动过程。 - 注意到 Evaluate 参数中的 percent 并非指Spline长度的百分比,而是表示Spline采样点的百分比。 4. **修改采样模式**: - 选择Uniform模式以实现均匀分布采样点。 - 注意到 Uniform 模式下,移动控制点会使 Spline 更新受该点影响的区域中的采样点。 5. **使用Travel函数**: - 使用Travel函数计算某个长度在Spline上对应的采样点百分比。 - 通过传入Spline长度的一半,然后乘以Travel函数,得到对应的percent值。 - 使用 EvaluatePosition(percent) 得到目标点的坐标。 总的来说,通过修改采样模式和使用Travel函数,我们成功地实现了物体沿固定路径从起点移动到终点的功能。这种方法避免了采样点不均匀分布导致的问题,并且可以实现更加平滑和准确的移动效果。

正文

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 something
            Debug.Log("Done");
        }
    }
}

效果如下

img

img

参考资料

Dreamteck Splines – User Manual


本文发布于2024年7月14日

最后编辑于2024年7月14日

我的博客园

与[Unity] Dreamteck Splines实现沿路径移动功能相似的内容:

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

Dreamteck Splines实现沿路径移动功能 最近有一个“让物体沿固定路径移动”的需求,因此接触到了Dreamteck Splines插件。 Dreamteck Splines可以很方便地绘制各种插值曲线,但在实现物体移动的时候却遇到了很多坑,因此在这里记录一下。 1. 绘制路径线 首先,让

Unity 编辑器中获取选中的文件夹、文件路径

编辑器中获取选中的文件夹、文件路径 using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; public class MyEditorScript { [MenuItem("Assets/PrintSelect

扩展实现Unity协程的完整栈跟踪

现如今Unity中的协程(Coroutine)方案已显得老旧,Unitask等异步方案可以直接解决如异常捕获等各类问题, 并且Unity官方也在开发一套异步方案,但现阶段还是需要在协程这个方案上继续琢磨。 Unity协程中无法输出完整的栈跟踪,因为协程编译后会转换为IL编码的状态机,中间存在栈回到堆

Unity三维数学总结

三维向量和三角函数 三维向量 向量是指一个同时具有大小和方向,且满足平行四边形法则的几何对象。 向量的模 po点相对于世界坐标原点的距离: po.magnitude。 标准向量,归一向量,指的是将向量的模变成1,方向不变。改变后的向量: po.normalized。 向量的方向 求向量的方向(求向量

Unity 利用Cache实现边下边玩

现在手机游戏的常规更新方案都是在启动时下载所有资源更新,游戏质量高的、用户粘性大的有底气,先安装2个G,启动再更新2个G,文件小了玩家还觉得品质不行不想玩。 最近在做微信、抖音小游戏,使用他们提供的资源缓存方案,现在要转成Android APP, 也想用这种边下边玩的机制把首包做小。 其实很简单,直

[Unity] 实现AssetBundle资源加载管理器

实现Unity AssetBundle资源加载管理器 AssetBundle是实现资源热更新的重要功能,但Unity为其提供的API却十分基(jian)础(lou)。像是自动加载依赖包、重复加载缓存、解决同步/异步加载冲突,等基础功能都必须由使用者自行实现。 因此,本篇博客将会介绍如何实现一个Ass

Unity热更学习toLua使用--[1]toLua的导入和默认加载执行lua脚本

[0]toLua的导入 下载toLua资源包,访问GitHub项目地址,点击下载即可。 将文件导入工程目录中: 导入成功之后会出现Lua菜单栏,如未成功生成文件,可以点击Generate All 重新生成(注意很可能是路径问题导致的生成失败!) 之后就可以开始编写脚本执行第一个lua程序了! [1]

Unity开发Hololens2—环境配置

> 博客地址:https://www.cnblogs.com/zylyehuo/ * ![](https://img2023.cnblogs.com/blog/3071480/202304/3071480-20230405201857570-1161269618.png) > 配置如下: > win

Unity开发Hololens2—交互发布配置

> 博客地址:https://www.cnblogs.com/zylyehuo/ ## 环境配置 ```bash unity2021.3.15f visual studio 2019 pro MRTK 2.8.3 OpenXR 1.8.0 Hololens2 ``` > Hololens2 环境配置

Unity框架与.NET, Mono框架的关系

什么是C# C#是一种面向对象的编程语言。 什么是.NET .NET是一个开发框架,它遵循并采用CIL(Common Intermediate Language)和CLR(Common Language Runtime)两种约定, CIL标准为一种编译标准:将不同编程语言(C#, JS, VB等)使