用于平滑 3D 运动的 Unity 线性插值

时间:2021-06-12 14:29:03

标签: c# unity3d linear-interpolation

所以我正在尝试创建一个向前奔跑的游戏(类似于 Temple Run),但我希望我的角色能够平滑地左右移动,而不仅仅是传送。我尝试使用 Vector3.Lerp(),但我无法理解它是如何工作的。

这是我尝试过的,但角色只是传送

    void Update()
    {
        if(Input.GetKeyDown("a")&&lane-1>0)
        {
            lane-=1;
            shouldMove = true;
            Vector3 newpos= new Vector3(transform.position.x -2.5f, transform.position.y, transform.position.z);
            while (shouldMove)
            {
                transform.position = Vector3.Lerp(transform.position, newpos, 0.1f * Time.deltaTime);
                if(transform.position.x>=newpos.x)
                {
                    shouldMove = false;
                    transform.position = newpos;
                }
            }

此示例仅用于向左移动。

1 个答案:

答案 0 :(得分:0)

您认为使用 Vector3.Lerp 在开始值和结束值之间逐渐插入是正确的。但是,实现和用例不太适合您尝试执行的操作。

由于您只更改移动的单个轴,因此我不会使用 Vector3.Lerp,而是使用 Mathf.Lerp,因为您只更改了位置的 x 轴。

您使用的是 while 循环而不是 Update 意味着程序将停留在该循环内,直到该过程完成。它立即移动到下一点的原因是由于您的 if 条件。您正在从当前位置减去 2.5 的值,然后不断检查您当前的位置 x 是否大于新位置,这总是正确的。它会立即将您的当前位置设置为新位置,然后跳出 while 循环。另一个问题是您使用了 GetKeyDown,它仅在用户单击键的一帧中发生。 Lerps 应该随着时间的推移完成,而不是立即完成。

我不会将您的逻辑放在 Update 中,而是考虑将实际运动移至名为 Coroutine 的特殊函数中。将 Courtines 视为在多个帧上完成的过程,然后跳回到前一帧中停止的位置。 Coroutines 非常强大,但请在过度使用它们之前仔细阅读文档以深入了解它们的作用。

现在开始实施。我不完全确定您的车道设置,但会假设您有 3 条车道,类似于其他无限跑道游戏。我会将输入检测保留在 Update 内,并允许玩家从中间从一侧跳到另一侧,但不会偏离轨道,也不会在跳跃时跳跃。我还将使用时间而不是速度来控制 Lerp 的跳跃,如果您愿意,可以轻松地将其更改为速度。

[SerializeField] private float TimeForJump = 0.25f;
[SerializeField] private float JumpOffset = 2.5f;

// I am assuming your setup for lanes is -1 | 0 | 1
private int lane = 0;
private Coroutine PlayerJumping = null;

void Update()
{
    // no need to detect input if we are already jumping
    if(PlayerJumping == null)
    {
        // instead of 'a' and 'd' you can use GetAxis to allow for arrow key and wasd support
        // it is also an easy way to determine direction when adding or subtracting for a goal location
        float horzInput = Input.GetAxis("Horizontal");

        if (horzInput < 0 && lane > -1)
        {
            PlayerJumping = StartCoroutine(Jump(-1));
            lane--;
        }
        else if(horzInput > 0 && lane < 1)
        {
            PlayerJumping = StartCoroutine(Jump(1));
            lane++;
        }
    }

    // simulating your movement
    transform.position = new Vector3(transform.position.x, transform.position.y + 0.1f, transform.position.z);
}

/// <summary>
/// Horizontally have the player jump
/// </summary>
private IEnumerator Jump(float direction)
{
    // store our current position
    float currentXPos = transform.position.x;

    // our goal position
    float goalXPos = currentXPos + (direction * JumpOffset);

    // timer for how long it has passed since we started the jump
    float jumpTimer = 0.0f;

    // continue the lerp for the time we have left
    while(jumpTimer <= TimeForJump)
    {
        transform.position = new Vector3(Mathf.Lerp(currentXPos, goalXPos, jumpTimer / TimeForJump), transform.position.y, transform.position.z);

        // increment the time that has passed
        jumpTimer += Time.deltaTime;
        yield return null;
    }

    // set our position directly in case of floating point errors
    transform.position = new Vector3(goalXPos, transform.position.y, transform.position.z);

    // set the coroutine to null so we can jump again
    PlayerJumping = null;
}

由于我不确定您的实施是如何完成的,因此我对车道和其他一般设置有一些假设。答案非常通用,因此可以根据您的需要进行调整。如果您愿意,您也可以将输入切换回 GetKeyDown,我使用它的唯一原因是您可以获得箭头键以及 wasd 输入。