如何在样条曲线上改变物体的速度?
我在尝试根据对象在样条曲线上的移动角度来微调它的速度,让它的运动看起来像沿着轨道上下落。对象的“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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。