Mathf.SmoothDamp 在协程中花费的时间比它应该花费的时间长
Mathf.SmoothDamp takes longer than it should inside a coroutine
我正在尝试在协同程序中移动和旋转游戏对象以顺利到达目标位置。为此,我尝试使用 Mathf.SmoothDamp()
来计算一个因子,我在 lerping 函数中使用它。这是我的方法:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
while (lerpFactor < 0.99f)
{
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f);
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
根据 Mathf.SmoothDamp() 的文档,它应该在一秒钟内将我的 lerpFactor
从 0
更改为 1
,这反过来应该将我的对象移动并旋转到一秒钟内到达目标位置。然而,这根本不会发生, lerpFactor
需要更长的时间(大约 3s)才能达到 1
(我使用 0.99
因为它永远不会真正达到 1
, 离得很近)。
我认为这可能是 Mathf.SmoothDamp()
默认使用 Time.deltaTime
的原因,这可能在协同程序中不起作用。所以我尝试提供自己的价值:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
var prevTime = Time.realtimeSinceStartup;
while (lerpFactor < 0.99f)
{
var time = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f, maxSpeed: 1000f, time); // using 1000f for max speed
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
这并没有改变任何东西,lerpFactor
达到 1
需要相同的时间。
我怎样才能让它按预期工作?
您不必使用平滑阻尼来计算 lerpFactor。 Lerp 因子就是经过的时间和持续时间的比率。所以如果你想要你的动画在一秒钟内;
private IEnumerator ReachTarget()
{
float timeElapsed = 0;
float lerpDuration = 1f;
float lerpFactor;
while (timeElapsed < lerpDuration)
{
lerpFactor = timeElapsed / lerpDuration;
// Use lerpFactor in your lerp functions
timeElapsed += Time.deltaTime;
yield return null;
}
}
SmoothDamp 的 smoothTime 只是一个近似值,并不精确,因为要付出更多的努力才不会超过目标。
我建议您切换到 SmoothStep 或 Animation 曲线,您可以根据需要自定义插值(例如添加过冲和弹跳效果)。
这是一个 smoothTime 更精确的例子
using System.Collections;
using UnityEngine;
public class LerpTest : MonoBehaviour
{
[SerializeField] private Transform _targetTransform;
[SerializeField] private AnimationCurve _animationCurve;
private Vector3 startingPosition;
private Quaternion startingRotation;
void Start()
{
startingPosition = transform.position;
startingRotation = transform.rotation;
}
private void Update()
{
if (Input.anyKeyDown)
{
StopAllCoroutines();
transform.position = startingPosition;
transform.rotation = startingRotation;
StartCoroutine(ReachTarget(1f));
}
}
private IEnumerator ReachTarget(float smoothTime)
{
var elapsedTime = 0f;
while (elapsedTime < smoothTime)
{
float lerpFactor = Mathf.SmoothStep(0f, 1f, elapsedTime / smoothTime);
// Or use an animation curve to customize the smoothing curve:
// float lerpFactor = _animationCurve.Evaluate(elapsedTime / smoothTime);
elapsedTime += Time.deltaTime;
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Slerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
Debug.Log($"Elapsed time: {elapsedTime} seconds");
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
}
作为旁注,我建议您使用 Quaternion.Slerp 而不是 lerp 以获得更好看的插值,并随意在 corountines 中使用 Time.deltaTime - 它工作得很好。
我正在尝试在协同程序中移动和旋转游戏对象以顺利到达目标位置。为此,我尝试使用 Mathf.SmoothDamp()
来计算一个因子,我在 lerping 函数中使用它。这是我的方法:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
while (lerpFactor < 0.99f)
{
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f);
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
根据 Mathf.SmoothDamp() 的文档,它应该在一秒钟内将我的 lerpFactor
从 0
更改为 1
,这反过来应该将我的对象移动并旋转到一秒钟内到达目标位置。然而,这根本不会发生, lerpFactor
需要更长的时间(大约 3s)才能达到 1
(我使用 0.99
因为它永远不会真正达到 1
, 离得很近)。
我认为这可能是 Mathf.SmoothDamp()
默认使用 Time.deltaTime
的原因,这可能在协同程序中不起作用。所以我尝试提供自己的价值:
private IEnumerator ReachTarget()
{
_speed = 0f;
var lerpFactor = 0f;
var startingPosition = transform.position;
var startingRotation = transform.rotation;
var prevTime = Time.realtimeSinceStartup;
while (lerpFactor < 0.99f)
{
var time = Time.realtimeSinceStartup - prevTime;
prevTime = Time.realtimeSinceStartup;
lerpFactor = Mathf.SmoothDamp(lerpFactor, 1f, ref _speed, 1f, maxSpeed: 1000f, time); // using 1000f for max speed
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Lerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
这并没有改变任何东西,lerpFactor
达到 1
需要相同的时间。
我怎样才能让它按预期工作?
您不必使用平滑阻尼来计算 lerpFactor。 Lerp 因子就是经过的时间和持续时间的比率。所以如果你想要你的动画在一秒钟内;
private IEnumerator ReachTarget()
{
float timeElapsed = 0;
float lerpDuration = 1f;
float lerpFactor;
while (timeElapsed < lerpDuration)
{
lerpFactor = timeElapsed / lerpDuration;
// Use lerpFactor in your lerp functions
timeElapsed += Time.deltaTime;
yield return null;
}
}
SmoothDamp 的 smoothTime 只是一个近似值,并不精确,因为要付出更多的努力才不会超过目标。
我建议您切换到 SmoothStep 或 Animation 曲线,您可以根据需要自定义插值(例如添加过冲和弹跳效果)。
这是一个 smoothTime 更精确的例子
using System.Collections;
using UnityEngine;
public class LerpTest : MonoBehaviour
{
[SerializeField] private Transform _targetTransform;
[SerializeField] private AnimationCurve _animationCurve;
private Vector3 startingPosition;
private Quaternion startingRotation;
void Start()
{
startingPosition = transform.position;
startingRotation = transform.rotation;
}
private void Update()
{
if (Input.anyKeyDown)
{
StopAllCoroutines();
transform.position = startingPosition;
transform.rotation = startingRotation;
StartCoroutine(ReachTarget(1f));
}
}
private IEnumerator ReachTarget(float smoothTime)
{
var elapsedTime = 0f;
while (elapsedTime < smoothTime)
{
float lerpFactor = Mathf.SmoothStep(0f, 1f, elapsedTime / smoothTime);
// Or use an animation curve to customize the smoothing curve:
// float lerpFactor = _animationCurve.Evaluate(elapsedTime / smoothTime);
elapsedTime += Time.deltaTime;
transform.position = Vector3.Lerp(startingPosition, _targetTransform.position, lerpFactor);
transform.rotation = Quaternion.Slerp(startingRotation, _targetTransform.rotation, lerpFactor);
yield return null;
}
Debug.Log($"Elapsed time: {elapsedTime} seconds");
transform.position = _targetTransform.position;
transform.rotation = _targetTransform.rotation;
}
}
作为旁注,我建议您使用 Quaternion.Slerp 而不是 lerp 以获得更好看的插值,并随意在 corountines 中使用 Time.deltaTime - 它工作得很好。