Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smooth Lerp movement?

Tags:

c#

unity3d

Currently it is my script for movement the player to the around the scene. How can i make it smoothly move?

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

}
like image 902
d d doe Avatar asked Sep 27 '18 02:09

d d doe


People also ask

What does Mathf Lerp do?

The Mathf. Lerp function takes 3 float parameters: one representing the value to interpolate from; another representing the value to interpolate to and a final float representing how far to interpolate. In this case, the interpolation value is 0.5 which means 50%.


2 Answers

  1. Create a velocity vector:

    Vector3 velocity = Vector3.zero;

  2. Add your movement vector to velocity:

    velocity += movement;

  3. Add velocity to the actual position:

    transform.position += velocity;

  4. Smooth out velocity by reducing it over time:

    velocity *= 0.975f;

like image 148
Iggy Avatar answered Sep 30 '22 23:09

Iggy


The FixedUpdate() function is ran every fixed interval as defined by Time.fixedDeltaTime (which you can either set via the TimeManager (Fixed Timestep) or at runtime by directly setting Time.fixedDeltaTime to a new value.

Because you are moving the character position and rotation on a fixed interval, depending on framerate the character will either move a lot with a lower framerate or only move every few frames with a higher framerate.

For example with a fixed timescale of 0.02 seconds and the game running at a framerate of 30 frames per second (aka rendering every 0.033 seconds) your game will be doing this:

 - [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

So in this example you can see how the character would jump forward at different amounts per frame and you can't guarantee what framerate the game will be running at either so it could end up being worse.

There are a few ways to make your character move smoothly though.

  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)

A direct code block answer to the question though could be to just try this, which is a combination of explanation 1 and 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);    
}

If you want to read up in more detail about smoothly moving objects, learning about lerps or just want more examples then check out this guide on how to fix movement stutter in Unity.

like image 38
Sean McManus Avatar answered Sep 30 '22 23:09

Sean McManus