问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Unity中Dreamteck Splines插件实现沿路径移动功能详解

创作时间:
作者:
@小白创作中心

Unity中Dreamteck Splines插件实现沿路径移动功能详解

引用
1
来源
1.
https://www.cnblogs.com/ThousandPine/p/18301767

本文详细介绍了如何使用Unity引擎中的Dreamteck Splines插件实现物体沿路径移动的功能。通过绘制路径、获取坐标点以及解决移动速度不一致等问题的具体方法,为Unity开发者提供了实用的开发指南。

绘制路径线

首先,在场景中创建一个空物体,并添加SplineComputer组件。由于这是一个2D项目,因此选择在Z平面上绘制。之后,编辑器中会显示出跟随鼠标的网格线,点击左键就可以逐点绘制Spline。


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

获取坐标

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

  • GetPoint(int)
  • GetPoints()

这两个方法用于获取手动添加的坐标点,也就是上图中的那三个点。这明显不符合"沿路径移动"的需求。而下面这三个方法才是返回Spline上的所有坐标点:

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

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

移动示例(踩坑)

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所在的物体上,拖动右侧的滑动条即可移动目标物体。一切看似正常,直到又添加了一条长度不同的线段。可以明显看出,在后面这个较短的路径中,物体的移动速度明显变慢了。当我们直接将进度设为0.5后,便能发现问题所在。目标物体移动并没有移动到Spline的终点,而是移动到了设置的第二个控制点上。

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

默认情况下,样条曲线(Spline)在[0-1]的百分比范围内进行计算(evaluated),涵盖了所有坐标点。例如,一条由3个点组成的样条曲线,计算百分比为0.5的坐标点,将始终返回第二个点的位置,因为它位于中间。然而,如果第一个点和第二个点非常接近,而第三个点距离它们很远,计算百分比为0.5的坐标点不会返回样条曲线的中间位置,它仍将返回第二个点。因为某些区域的采样点比其他区域更密集

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

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

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

前面提到"采样点的不均匀分布,导致了采样点百分比和长度百分比不一致的情况"。反过来说,需要让采样点能够均匀分布,就可以解决这一问题。SplineComputer提供了三种采样模式(Sample Mode):

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

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

这样一来就能正确地匀速移动目标了。但是,这个方法还会带来一系列问题。首先,它会导致线段脱离控制点。其次,它还有个很致命的BUG:Uniform模式下,如果用CalculateLength方法获取Spline的长度,那么初始状态下将会始终返回0。此时必须对他"进行一些操作",比如移动控制点,修改其他参数等,让他响应一次变化。之后CalculateLength才能正确返回数值。

方法二:使用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");
        }
    }
}

效果如下:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号