平滑的Lerp运动?

7

目前这是我的脚本,用于使玩家在场景中移动。我如何使它平滑移动?

void FixedUpdate()
{
    bool running = Input.GetKey(KeyCode.LeftShift);

    float h = Input.GetAxisRaw("Horizontal");
    float v = Input.GetAxisRaw("Vertical");

    bool isWalking = Mathf.Abs(h) + Mathf.Abs(v) > 0;

    movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;
    if (isWalking)
    {
        transform.position += movement * Time.fixedDeltaTime;
        transform.LookAt(transform.position + movement);
    }

}

4
你看过Vector3.MoveTowards或者Vector3.Lerp吗?这两个函数都有平滑移动的示例... - Ron Beyer
它的移动有什么不流畅的地方吗?与您期望的结果相比,您当前的问题是什么?我只是想确保我理解哪一部分的移动不“流畅”。 - Alox
5个回答

11
  1. 创建速度向量:

    Vector3 velocity = Vector3.zero;

  2. 将你的移动向量添加到速度中:

    velocity += movement;

  3. 将速度添加到实际位置中:

    transform.position += velocity;

  4. 通过逐渐减小速度来平滑速度:

    velocity *= 0.975f;


3
代码非常简单,哪一部分不太清楚? - Iggy

4
The FixedUpdate()功能每隔一段固定时间运行一次,这个时间由Time.fixedDeltaTime定义(可以通过TimeManager (Fixed Timestep)设置或在运行时直接设置Time.fixedDeltaTime的值来进行设置)。因为你是按照固定间隔移动角色的位置和旋转,所以根据帧速率,角色要么会在低帧速率下移动很多,要么要等几帧才移动一次。例如,如果将固定时间尺度设置为0.02秒,并且游戏以30帧每秒的帧速率运行(即每0.033秒渲染一次),那么你的游戏将执行以下内容:
 - [Time: 0.020s] Character position += 0.02
 - [Time: 0.033s] Render frame with character at position 0.02
 - [Time: 0.040s] Character position += 0.02
 - [Time: 0.060s] Character position += 0.02
 - [Time: 0.066s] Render frame with character at position 0.06
 - [Time: 0.080s] Character position += 0.02
 - [Time: 0.099s] Render frame with character at position 0.08
 - [Time: 0.100s] Character position += 0.02
 - [Time: 0.120s] Character position += 0.02
 - [Time: 0.133s] Render frame with character at position 0.12

在这个示例中,您可以看到角色每帧向前跳跃的不同量,而且您无法保证游戏运行的帧率,因此可能会更糟。但是有几种方法可以使您的角色移动平稳。
  1. Put your code in an Update() loop instead of a FixedUpdate() loop, this will move the position of the character each rendered frame. Along with this you can multiply the movement speed by Time.deltaTime which is the time since the previous frame was rendered (aka time since Update() was last ran and the character was moved)

  2. Use Vector3.Lerp(..)/Quaterion.Lerp(..) or Vector3.MoveTowards(..)/Quaternion.RotateToward(..) using a time/step value multiplied by Time.deltaTime to interpolate the character movement keeping it moving smoothly in relation to the game framerate.

  3. If your character has a rigidbody component then you can simply just set the rigidbody interpolation to interpolate then move your character by calling:

    characterRigidbody.MovePosition(wantedPosition);
    characterRigidbody.MoveRotation(wantedRotation);
    

    As a replacement to your existing transform movements (keeping your code inside of the FixedUpdate() loop)

    But note that continuing to have your Input.* calls inside a FixedUpdate() is polling them more than needed so you might want to move them over into an Update() splitting the movement code and input listening code if you decide to do this. (I develop android games so maybe on PC this isn't worth worrying about as much, but probably still worth changing)

对于这个问题,直接的代码块回答可能是尝试这个,它是解释1和2的结合:

// How long in seconds will it take the lerps to finish moving into position
public float lerpSmoothingTime = 0.1f;

private Vector3 targetPosition;
private Quaternion targetRotation;

void Update()
{
    bool running = Input.GetKey(KeyCode.LeftShift);

    float h = Input.GetAxisRaw("Horizontal");
    float v = Input.GetAxisRaw("Vertical");

    bool isWalking = Mathf.Abs(h) + Mathf.Abs(v) > 0;

    movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;
    if (isWalking)
    {
        targetPosition += movement * Time.deltaTime;
        targetRotation = Quaternion.LookRotation(targetPosition - transform.position);
    }

    // Always lerping as if we suddenly stopped the lerp as isWalking became false the stop would be sudden rather than smoothly moving into position/rotation
    transform.position = Vector3.Lerp(transform.position, targetPosition, Time.deltaTime / lerpSmoothingTime);
    transform.rotation = Quaternion.Lerp(transform.rotation, targetRotation, Time.deltaTime / lerpSmoothingTime);    
}

如果你想更详细地了解如何平滑移动物体,学习有关lerps的知识或者只是需要更多的示例,请查看这份指南,了解如何在Unity中修复运动卡顿问题。请访问how to fix movement stutter in Unity

0
你可以使用 Vector3.Lerp(...)
尝试这段代码:
float smoothTime = 0.125f;
Vector3 newPos;

...

void FixedUpdate()
{
    bool running = Input.GetKey(KeyCode.LeftShift);
    float h = Input.GetAxisRaw("Horizontal");
    float v = Input.GetAxisRaw("Vertical");

    movement = ((running) ? runSpeed : walkSpeed) * new Vector3(h, 0.0f, v).normalized;

    //Set the new position
    if(movement.magnitude > 0)
        newPos = transform.position + movement;

    // Use Vector3.Lerp(...)
    transform.position = Vector3.Lerp(transform.position, newPos, smoothTime);
    transform.LookAt(transform.position);

}

希望它能对你有所帮助。


0
首先,如果您希望代码在每个帧执行,则需要将其放入Update()方法中。FixedUpdate是根据项目设置以固定间隔调用的(您可以在编辑->项目设置->时间->固定时间步长中更改它)。通常FixedUpdate()用于物理相关的内容。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接