如何在样条曲线上改变物体的速度?

编程语言 2026-07-08

我在尝试根据对象在样条曲线上的移动角度来微调它的速度,让它的运动看起来像沿着轨道上下落。对象的“Align To”设为“Spline Element”,因此它会随着样条一起旋转;它的Movement Mode设置为“Speed”,并使用Spline包 v2.8.4。该样条全为贝塞尔节点,切线进入和离开值都设为0,出于简化考虑。

在实际运行中,当改变速度时,对象会非常抖动,尤其是在速度变得极慢,甚至会往回走的时候(如果这个功能可用,我其实挺喜欢的),它会达到最小速度下限,在下坡时有时又会加速得过快。此外,沿着样条移动时,游戏对象还会持续绕Z 轴旋转,几乎就像沿着被人扭动的绳子在移动。我不确定这是为什么,或者它是否与前面的另一个问题有关。

以下是错误实际表现的视频链接(请忽略背景噪音,哈哈):https://youtu.be/EJkOubG4PHU

using UnityEngine;
using UnityEngine.Splines;

public class CoasterMove : MonoBehaviour {

    public float angle; 
    public SplineAnimate animator;
    public SplineContainer currentSpline;

    public float acceleration;
    public float decceleration;

    private Rigidbody rb;

    void Start() {
        if (animator == null) animator = GetComponent<SplineAnimate>();        

        rb = GetComponent<Rigidbody>();
        if (rb != null) rb.isKinematic = true;

        animator.AnimationMethod = SplineAnimate.Method.Speed;
    }

    private void Update() {

        //ApplySlopeSpeed();

        //Vector3 targetDir = target.position - transform.position;
        angle = Vector3.Angle(Vector3.up, transform.forward);
        if (angle < 80f) {

            animator.MaxSpeed -= decceleration * Time.deltaTime;
            Debug.Log("Angle Up: " + angle + ", MaxSpeed: " + animator.MaxSpeed);
        } else if (angle >= 120f && angle < 180f) {
            Debug.Log("Angle Down: " + angle + ", MaxSpeed: " + animator.MaxSpeed);
            animator.MaxSpeed += acceleration * Time.deltaTime;
        }

        if (animator.MaxSpeed < 2) {
            animator.MaxSpeed = 2;
        }
        if (animator.MaxSpeed > 20) {
            animator.MaxSpeed = 20;
        }

        if (animator.NormalizedTime >= 1f && animator.isActiveAndEnabled) {
            //Debug.Log("Finished current spline");
            OnSplineEnd();
        }
    }

    private void OnTriggerEnter(Collider other) {
        //Debug.Log("Collided with Trigger");
        var placer = other.GetComponentInParent<SplinePlacer>();
        if (placer != null) {
            //Debug.Log("Trigger " + placer.gameObject.name + " was spline entrance: " + placer.container);
            currentSpline = placer.container;
            if (rb != null) {
                if (rb.isKinematic == false && animator.enabled == false) {
                    rb.isKinematic = true;
                    animator.enabled = true;
                }
            }
        }
    }

    private void OnTriggerExit(Collider other) {
        //Debug.Log("Left Trigger");
        var placer = other.GetComponentInParent<SplinePlacer>();
        if (placer != null && placer.container == currentSpline) {
            //Debug.Log("Left spline Trigger");
            currentSpline = null;
        }
    }

    private void OnSplineEnd() {
        if (currentSpline != null && currentSpline != animator.Container) {
            //Debug.Log("Reached end. Switching to next spline");
            animator.Container = currentSpline;
            animator.NormalizedTime = 0f;
            animator.Play();
            return;
        }

        //Debug.Log("Spline animation finished. Detaching from splines now");
        animator.enabled = false;
        //transform.Rotate(new Vector3(0,180,0));
        Vector3 vel = transform.forward * animator.MaxSpeed;

        if (rb != null) {
            rb.isKinematic = false;
            rb.linearVelocity = vel;
        }
    }
}

解决方案

它是基于经过的时间来计算位置。当你改变速度时,位置也会改变。请在MaxSpeed中添加一个setter。计算在新速度下当前位置信息应对应的经过时间,然后把经过时间改为那个值。经过时间 × 旧速度 = 距离。距离 / 新速度 = 新的经过时间。

或者不使用splineAnimate。这是我按速度设置位置的做法。你可以使用Evaluate函数来替代EvaluatePosition,以获得旋转和位置。

_totalDistance = splineContainer.Splines[0].CalculateLength(splineContainer.transform.localToWorldMatrix);
private void SetPosition()
{
    _distanceTravelled += Time.deltaTime * _speed;
    var t = _distanceTravelled / _totalDistance;

    if (t > 1)
    {
        _distanceTravelled = 0;
        _index++;
        if (_index >= splineContainer.Splines.Count)
        {
            _isFinished =  true;
            Destroy(gameObject);
            return;
        }
    }

    Vector3 pos = splineContainer.Splines[_index].EvaluatePosition(t);
    transform.position = pos;
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章