简体   繁体   English

Unity线性插值平滑3D运动

[英]Unity linear Interpolation for smooth 3D movement

So I am trying to create a forward running game(something like Temple Run) but I want my character to move from side-to-side smoothly and not just teleport.所以我正在尝试创建一个向前运行的游戏(类似于 Temple Run),但我希望我的角色能够顺利地左右移动,而不仅仅是传送。 I tried to use Vector3.Lerp() but I just can't wrap my head around how it works.我尝试使用Vector3.Lerp()但我无法理解它是如何工作的。

Here is what I tried but the character just teleports这是我尝试过的,但角色只是瞬移

    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;
                }
            }

This example is just for moving to the left.此示例仅用于向左移动。

You are right in thinking to use a Vector3.Lerp to gradually interpolate between a start and end value.您认为使用Vector3.Lerp在开始值和结束值之间逐渐插值是正确的。 However, the implementation and use case does not quite fit what you are attempting to do.但是,实现和用例并不完全适合您尝试做的事情。

As you are only changing a single axis of the movement, I would not use Vector3.Lerp , instead use Mathf.Lerp as you are only changing the x-axis of your position.由于您仅更改运动的单个轴,因此我不会使用Vector3.Lerp ,而是使用Mathf.Lerp因为您仅更改 position 的 x 轴。

You are using a while loop instead of Update meaning the program will stick inside of that loop until the process has finished.您使用的是while循环而不是Update ,这意味着程序将停留在该循环内,直到该过程完成。 The reason it is immediately moving to the next point is due to your if conditional.它立即移动到下一点的原因是由于您的if条件。 You are subtracting a value of 2.5 from your current then continually checking if your current position x is greater than the new, which is always true.您从当前值中减去2.5 ,然后不断检查当前 position x 是否大于新值,这始终是正确的。 It will immediately set your current position to your new position, then break out of your while loop.它会立即将您当前的 position 设置为您的新 position,然后跳出您的 while 循环。 One other issue is you are using GetKeyDown , which only occurs the one frame the user clicks the key.另一个问题是您正在使用GetKeyDown ,它只发生在用户单击键的一帧。 Lerps are meant to be completed over time, not immediately. Lerps意味着随着时间的推移完成,而不是立即完成。

Instead of placing your logic inside of Update , I would consider moving the actual movement to a special function called a Coroutine .与其将您的逻辑放在Update中,我会考虑将实际运动移至称为 Coroutine 的特殊Coroutine Think of Courtines as a process that is completed over multiple frames, jumping back to where it left off in the previous frame.Courtines想象为一个在多个帧上完成的过程,然后跳回到上一帧中断的地方。 Coroutines are very powerful but please read into the documentation before overusing them to get a firm understanding of what they do. Coroutines非常强大,但请在过度使用它们之前仔细阅读文档,以深入了解它们的作用。

Now for the implementation.现在进行实施。 I am not entirely sure of your lane setup but will assume you have 3 lanes similar to the other infinite run track games.我不完全确定你的车道设置,但会假设你有 3 条车道,类似于其他无限跑道游戏。 I will keep the input detection inside of Update , and will allow the player to jump from side to side from the middle, but not off the track nor jump while jumping already.我会将输入检测保留在Update内部,并允许玩家从中间从一侧跳到另一侧,但不会偏离轨道,也不会在跳跃时跳跃。 I will also be using time not speed to control the jump of the Lerp , you can easily change it to speed if you like.我还将使用时间而不是速度来控制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;
}

As I was not exactly sure how your implementation was done, I had a few assumptions about lanes and other general setups.由于我不确定你的实现是如何完成的,所以我对车道和其他一般设置有一些假设。 The answer is quite generic so it can be tweaked to what you need.答案非常通用,因此可以根据您的需要进行调整。 You can also swap the input back to GetKeyDown if you like, the only reason I used it is so you get arrow keys as well as wasd input.如果您愿意,您也可以将输入交换回GetKeyDown ,我使用它的唯一原因是您可以获得箭头键和 wasd 输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM