I have a physical rowing device that sends the remaining distance of its rowing race to Unity constantly (i.e. 350m remaining to complete the race). When I try to update the position of my player accordingly (Z-Axis only), the movement is choppy. How can I interpolate this kind of movement so that it's smoother?

My first try looked like this:

void Update()
{
   player.position = new Vector3(player.position.x, player.position.y, 
   pm5.Pm5_distance);
}

This resulted in choppy movement, probably because the player is simply teleported to the next position.

Next I tried to lerp to next position, but I'm not sure if I did it correctly or if this works at all since my player receives a new position every frame:

private float startTime;
private float journeyLength;
public float speed = 5.0F;

void Start()
{
    startTime = Time.time;
}

void Update()
{
    if (player.position != new Vector3(player.position.x, 
    player.position.y, pm5.Pm5_distance))
    {
        journeyLength = Vector3.Distance(player.position, new 
        Vector3(player.position.x, player.position.y, pm5.Pm5_distance));

        float distCovered = (Time.time - startTime) * speed;

        float fracJourney = distCovered / journeyLength;

        transform.position = Vector3.Lerp(player.position, new 
        Vector3(player.position.x, player.position.y, pm5.Pm5_distance), 
        fracJourney);
    }

}

Even though this does not throw errors, it doesn't seem to fix my problem either. Maybe the whole concept is wrong? I'd be glad if somebody could help me out here.

EDIT 25.04.19: From trying around with my David's code I landed here:

    void Update()
    {

        if (useLerpMovementModel)
        {
            float newDistance = pm5.Pm5_distance;

            if (player.position.z != newDistance)
            {
                // Distance moved
                float distCovered = journeyLength - pm5.Pm5_distance;

                // Fraction of journey completed = current distance divided by total distance.
                float fracJourney = distCovered / journeyLength;

                // Set our position as a fraction of the distance between the markers.
                transform.position = Vector3.Lerp(player.position, new Vector3(player.position.x, player.position.y, newDistance), fracJourney);
            }
        }
        else
        {
            player.position = new Vector3(player.position.x, player.position.y, journeyLength - pm5.Pm5_distance);
        }

    }

I'm not sure if this does anything at all though in terms of smoothing the movement compared to player.position = new Vector3(player.position.x, player.position.y, journeyLength - pm5.Pm5_distance);

1 Answers

1
Darren Ruane On Best Solutions

The following line is actually an incorrect calculation:

journeyLength = Vector3.Distance(player.position, new 
        Vector3(player.position.x, player.position.y, pm5.Pm5_distance));

You'll need to store the position at the start and at the end of the journey so that you can interpolate the player's position between them.

I will try to explain what is necessary by taking your code and adding comments. I have also included the changes that I recommended as well as others:

private float journeyLength;
private Vector3 startPosition;
private Vector3 endPosition;

void Start()
{
    // The total length of the journey, let's imagine it's 100
    // This assumes that when this Start() method is called that
    // pm5.Pm5_distance is equal to the journey length
    journeyLength = pm5.Pm5_distance;
    // Store the player's starting position
    // This will be used to interpolate between start/end
    startPosition = player.position;
    // Store the position that the player will be at when the journey is complete
    // This will be used to interpolate between start/end
    endPosition = startPosition + Vector3.forward * journeyLength;
}

void Update()
{
    // I am assuming that 'pm5.Pm5_distance' is the remaining distance
    if (pm5.Pm5_distance > 0f) 
    {
        // Take the remaining distance from the total journey length to get
        // the current distance travelled. Let's imagine the remaining distance is 50:
        // This will end up being 100 - 50 = 50 units travelled into the journey
        float distCovered = journeyLength - pm5.Pm5_distance;

        // We now get the fraction of the journey that that distance equates to, 
        // which in this case will be 0.5
        float fracJourney = distCovered / journeyLength;

        // Interpolate the players position from where it originally started
        // to the position at the end of the journey (the one we calculated in Start())
        transform.position = Vector3.Lerp(startPosition, endPosition, fracJourney);
    }

}

As a final note, it is important to understand how the Vector3.Lerp() function works: It takes two vectors and interpolates between them by a value that is between 0 and 1.

If the value is 0, then the first vector is returned.

If the value is 1, then the second vector is returned.

If the value is 0.5 then a vector that lies between both is returned.

So you can see that in the example above, fracJourney is 0.5, which means that for this frame, the player's position will be exactly half way through the race. But in the next frame, fracJourney will be recalculated and let's say something like 0.55. In that frame, the new position is going to be calculated just slightly forward from where it was in the previous frame.

This continues on and on until eventually the distance to the end of the race is 0.