首页 > 解决方案 > 使用 Vector2.MoveTowards 缓入和缓出

问题描述

我见过很多用Vector2.Lerp.

但我想使用Vector2.MoveTowards,因为我正在随机化我的 NPC 行进的距离,并且我希望 NPC 的速度始终保持不变,无论行进的距离如何。理想情况下,AnimationCurve如果可能的话,希望通过一个平滑的步进函数来精细地控制缓动,但也可以。这是我现在使用的简化代码(使用行为设计器,所以方法有点不同):

    private readonly float Speed = 1.5f;
    public SharedVector2 BugDirection;
    
    public override void OnStart()
    {
       BugDirection.Value = new Vector2(Random.Range(0.1f, 0.5f), transform.position.y);
    }

    public override TaskStatus OnUpdate()
    {
        if (Vector2.Distance(transform.position, BugDirection.Value) == 0)
        {
            return TaskStatus.Success;
        }

       transform.position = 
           Vector2.MoveTowards(transform.position, BugDirection.Value, Speed * Time.deltaTime);
        
       return TaskStatus.Running;
    }

标签: c#unity3d

解决方案


好的,所以有一个第一个问题:永远不要用于==比较两个float值!

即使是这样的情况也5f * 0.2f / 10f == 1f可能失败,因为由于浮点精度,它实际上可能是0.9999999or 1.0000001

相反,您通常宁愿检查某个范围,例如

if(Mathf.Abs(a - b) <= someThreshold)

Unity 为此还提供Mathf.Approximately和使用

if(Mathf.Approximately(a, b))

基本上等于使用

if(Mathf.Abs(a - b) <= Mathf.Epsilon)

Epsilon在哪里

浮点数可以不同于零的最小值。

就您当前的停止条件而言。


我现在只是假设这发生在 a 之外的某个地方,MonoBehaviour所以协程不是一个选择。

但是,您基本上想要实现的是具有一个从线性(时间)持续增长的值,0映射1到缓入和缓出的运动曲线上。

因此,假设目标位置在运动的开始和结束之间没有更新,我会这样做,例如

private readonly float Speed = 1.5f;

private Vector2 target;
private Vector2 start;
private float timePassed;
private float duration;

public override void OnStart()
{
   target = new Vector2(Random.Range(0.1f, 0.5f), transform.position.y);
   start = transform.position;

   duration = Vector2.Distance(start, target) / speed;
   timePassed = 0f;
}

public override TaskStatus OnUpdate()
{
    if (timePassed >= duration)
    {
        transform.position = target;
        return TaskStatus.Success;
    }

    var factor = timePassed / duration;

    //MAGIC

    transform.position = Vector2.Lerp(start, target, factor);

   timePassed += Time.deltaTime;
    
   return TaskStatus.Running;
}

到目前为止,这将是一个线性运动,就像以前一样;)

所以让我们填写MAGIC.

缓入和缓出的一个非常简单的选择是使用Mathf.SmoothStep例如

factor = Mathf.SmoothStep(0, 1, factor);

或者你AnimationCurve现在可以使用

factor = yourAnimationCurve.Evaluate(factor);

这使您可以完全控制运动的平滑度,但总平均速度speed就像您将其线性应用一样。


推荐阅读