应用运动

Vector3 结构包含一些静态函数,当我们希望将运动应用于 Vector3 时,这些函数可以提供效用。

LerpLerpUnclamped

lerp 函数基于提供的分数提供两个坐标之间的移动。如果 Lerp 只允许两个坐标之间的移动,则 LerpUnclamped 允许分数移动到两个坐标之间的边界之外。

我们提供运动的一部分作为 float。使用 0.5 的值,我们找到两个 Vector3 坐标之间的中点。01 的值将返回第一个或第二个 Vector3,因为这些值或者与没有移动(因此返回第一个 Vector3)或完成移动(这返回第二个 Vector3)相关联。重要的是要注意,这两个函数都不适应运动分数的变化。这是我们需要手动解决的问题。

使用 Lerp,所有值都夹在 01 之间。当我们想要向一个方向提供移动而不想超越目的地时,这非常有用。LerpUnclamped 可以取任何值,可用于提供远离目的地或经过目的地的移动。

以下脚本使用 LerpLerpUnclamped 以一致的速度移动对象。

using UnityEngine;

public class Lerping : MonoBehaviour
{
    /// <summary>The red box will use Lerp to move. We will link
    /// this object in via the inspector.</summary>
    public GameObject lerpObject;
    /// <summary>The starting position for our red box.</summary>
    public Vector3 lerpStart = new Vector3(0, 0, 0);
    /// <summary>The end position for our red box.</summary>
    public Vector3 lerpTarget = new Vector3(5, 0, 0);

    /// <summary>The blue box will use LerpUnclamped to move. We will 
    /// link this object in via the inspector.</summary>
    public GameObject lerpUnclampedObject;
    /// <summary>The starting position for our blue box.</summary>
    public Vector3 lerpUnclampedStart = new Vector3(0, 3, 0);
    /// <summary>The end position for our blue box.</summary>
    public Vector3 lerpUnclampedTarget = new Vector3(5, 3, 0);

    /// <summary>The current fraction to increment our lerp functions by.</summary>
    public float lerpFraction = 0;

    private void Update()
    {
        // First, I increment the lerp fraction. 
        // delaTime * 0.25 should give me a value of +1 every second.
        lerpFraction += (Time.deltaTime * 0.25f);

        // Next, we apply the new lerp values to the target transform position.
        lerpObject.transform.position 
            = Vector3.Lerp(lerpStart, lerpTarget, lerpFraction);
        lerpUnclampedObject.transform.position 
            = Vector3.LerpUnclamped(lerpUnclampedStart, lerpUnclampedTarget, lerpFraction);
    }
}

红色框移动到目标位置,然后停止。蓝框继续无限移动。

MoveTowards

MoveTowards 的表现 Lerp 非常相似 ; 核心不同的是,我们提供了一个实际的距离来移动,而不是一个,分数的两个点之间。值得注意的是,MoveTowards 不会超过目标 Vector3

LerpUnclamped 非常相似,我们可以提供一个距离值来远离目标 Vector3。在这种情况下,我们永远不会超越目标 Vector3,因此运动是无限的。在这些情况下,我们可以将目标 Vector3 视为相反的方向; 只要 Vector3 指向同一方向,相对于起始 Vector3,负向运动应该表现正常。

以下脚本使用 MoveTowards 使用平滑距离将一组对象移向一组位置。

using UnityEngine;
    
public class MoveTowardsExample : MonoBehaviour
{
    /// <summary>The red cube will move up, the blue cube will move down, 
    /// the green cube will move left and the yellow cube will move right.
    /// These objects will be linked via the inspector.</summary>
    public GameObject upCube, downCube, leftCube, rightCube;
    /// <summary>The cubes should move at 1 unit per second.</summary>
    float speed = 1f;

    void Update()
    {
        // We determine our distance by applying a deltaTime scale to our speed.
        float distance = speed * Time.deltaTime;

        // The up cube will move upwards, until it reaches the 
        //position of (Vector3.up * 2), or (0, 2, 0).
        upCube.transform.position 
            = Vector3.MoveTowards(upCube.transform.position, (Vector3.up * 2f), distance);

        // The down cube will move downwards, as it enforces a negative distance..
        downCube.transform.position
            = Vector3.MoveTowards(downCube.transform.position, Vector3.up * 2f, -distance);

        // The right cube will move to the right, indefinetly, as it is constantly updating
        // its target position with a direction based off the current position.
        rightCube.transform.position = Vector3.MoveTowards(rightCube.transform.position, 
            rightCube.transform.position + Vector3.right, distance);

        // The left cube does not need to account for updating its target position, 
        // as it is moving away from the target position, and will never reach it.
        leftCube.transform.position
            = Vector3.MoveTowards(leftCube.transform.position, Vector3.right, -distance);
    }
}

所有立方体从中心向外移动,红色立方体停在目标目的地。

SmoothDamp

SmoothDamp 视为 MoveTowards 的变体,内置平滑。根据官方文档,此功能最常用于执行平滑的相机跟踪。

除了起始和目标 Vector3 坐标,我们还必须提供一个表示速度的 Vector3,以及表示完成运动所需的大致时间的 float 。与前面的例子不同,我们提供速度作为参考,在内部递增。重要的是要注意这一点,因为在我们仍然执行该功能时改变功能之外的速度会产生不希望的结果。

除了所需的变量之外,我们还可以提供一个 float 来表示我们对象的最大速度,还有一个 float 来表示自上一次 SmoothDamp 调用该对象以来的时间间隔。我们不需要提供这些值; 默认情况下,没有最大速度,时间间隔将被解释为 Time.deltaTime。更重要的是,如果你调用一个函数 MonoBehaviour.Update() 内每个对象的功能,你应该不会需要声明的时间间隔。

using UnityEngine;
    
public class SmoothDampMovement : MonoBehaviour
{
    /// <summary>The red cube will imitate the default SmoothDamp function. 
    /// The blue cube will move faster by manipulating the "time gap", while 
    /// the green cube will have an enforced maximum speed. Note that these 
    /// objects have been linked via the inspector.</summary>
    public GameObject smoothObject, fastSmoothObject, cappedSmoothObject;

    /// <summary>We must instantiate the velocities, externally, so they may 
    /// be manipulated from within the function. Note that by making these 
    /// vectors public, they will be automatically instantiated as Vector3.Zero 
    /// through the inspector. This also allows us to view the velocities, 
    /// from the inspector, to observe how they change.</summary>
    public Vector3 regularVelocity, fastVelocity, cappedVelocity;

    /// <summary>Each object should move 10 units along the X-axis.</summary>
    Vector3 regularTarget = new Vector3(10f, 0f);
    Vector3 fastTarget = new Vector3(10f, 1.5f);
    Vector3 cappedTarget = new Vector3(10f, 3f);

    /// <summary>We will give a target time of 5 seconds.</summary>
    float targetTime = 5f;

    void Update()
    {
        // The default SmoothDamp function will give us a general smooth movement.
        smoothObject.transform.position = Vector3.SmoothDamp(smoothObject.transform.position,
            regularTarget, ref regularVelocity, targetTime);

        // Note that a "maxSpeed" outside of reasonable limitations should not have any 
        // effect, while providing a "deltaTime" of 0 tells the function that no time has 
        // passed since the last SmoothDamp call, resulting in no movement, the second time.
        smoothObject.transform.position = Vector3.SmoothDamp(smoothObject.transform.position,
            regularTarget, ref regularVelocity, targetTime, 10f, 0f);

        // Note that "deltaTime" defaults to Time.deltaTime due to an assumption that this 
        // function will be called once per update function. We can call the function 
        // multiple times during an update function, but the function will assume that enough
        // time has passed to continue the same approximate movement. As a result, 
        // this object should reach the target, quicker.
        fastSmoothObject.transform.position = Vector3.SmoothDamp(
            fastSmoothObject.transform.position, fastTarget, ref fastVelocity, targetTime);
        fastSmoothObject.transform.position = Vector3.SmoothDamp(
            fastSmoothObject.transform.position, fastTarget, ref fastVelocity, targetTime);

        // Lastly, note that a "maxSpeed" becomes irrelevant, if the object does not 
        // realistically reach such speeds. Linear speed can be determined as 
        // (Distance / Time), but given the simple fact that we start and end slow, we can 
        // infer that speed will actually be higher, during the middle. As such, we can
        // infer that a value of (Distance / Time) or (10/5) will affect the 
        // function. We will half the "maxSpeed", again, to make it more noticeable.
        cappedSmoothObject.transform.position = Vector3.SmoothDamp(
            cappedSmoothObject.transform.position, 
            cappedTarget, ref cappedVelocity, targetTime, 1f);
    }
}

我们可以通过调整 maxSpeed 和 deltaTime 参数来改变对象到达目标的速度。