Vector3.Lerp never arrives at 'Vector3 to' position.

When I run the following code the “Translating…” message never stops appearing in the console so the check for

if(_token.transform.position != transform.position)

is always evaluating to true.

The object does successfully move to the destination position the first time, but after that it only seems to happen randomly. I am sure this is probably occurring because of the problem I mentioned above.

Is there a different check I should use?

Here is my code:

void OnMouseOver()
{

     // If terrain tile receives left click then move the previously selected token to this terrain tile.
     if(Input.GetButtonDown("LeftClick"))
     {
          // Movement distance between terrain tiles.
          _journeyLength = Vector3.Distance(_token.transform.position, transform.position);

          // Movement start time.
          _transformStartTime = Time.time;
     }

}


// Update is called once per frame
	void Update () {
		if(_token != null)
		{
			if(_token.transform.position != transform.position)
			{
				float distanceCovered = (Time.time - _transformStartTime) * 3.0F;
				float amountOfJourneyCompleted = distanceCovered / _journeyLength;
				_token.transform.position = Vector3.Lerp(_token.transform.position, transform.position, amountOfJourneyCompleted);
				Debug.Log("Translating...");
			}
		}
	}

You should check based on the distance from your current position to your destination rather than if your object has arrived at that exact destination. This is because your object may never arrive at the destination due to floating point accuracy. For example, if let’s say your destination is Vector3(1, 1, 1). If let’s say your object stops at Vector3(0.99999, 0.99999, 0.9999), it still hasn’t arrived at its destination.

You can use Vector3.Distance to check. Unity - Scripting API: Vector3.Distance

Also another thing to note is that if you’re using Vector3.Lerp, it actually makes the object slow down as it nears its destination. So although you can’t actually see it moving and you assume it has arrived at its destination, it’s actually still moving.

So, to continue with Wolfhunter’s (very good) advice…

Instead of this:

if (_token.transform.position != transform.position)

You should use something like this:

float closeEnough = 0.001f;
if (Vector3.Distance(_token.transform.position, transform.position) > closeEnough)
{
   // not there yet...
}

Jeff

Lerp is also fine for constant speed linear movement if you pass in a constant value for the first argument, rather than passing in the transform which you’re progressively changing over time. So, in addition to tracking _transformStartTime, add another variable for _transformStartPosition, and pass that into the lerp as the first argument. That way you’ll get a constant speed movement and you can stop lerping when your amountOfJourneyCompleted is greater than 1.

Unity also has a Vector3.MoveTowards function which provides this behaviour more concisely, if you do want a constant movement speed.

This is not correct. Lerp does a linear interpolation — there is no smoothing or deceleration involved.

(Of course you could make it do that by passing in values of t that get progressively closer to 1, without ever actually reaching it — but that’s not what this code is doing.)

I think the actual problem the OP is having here is the opposite: it’s overshooting. On one frame, amountOfJourneyCompleted is 0.98; and on the next frame, it’s 1.02 (for example). If that parameter never equals exactly 1.0, then the position will never equal exactly the target. This is easily fixed by clamping it to the range 0-1:

    float amountOfJourneyCompleted = Mathf.Clamp01(distanceCovered / _journeyLength);

Cheers,

  • Joe

The OP’s code is doing exactly that, check line 25. You could be right about the lerp factor overshooting though.

Elaborating on what I said before, per-frame constant-speed movement is much easier to do using MoveTowards. All you need is this:

transform.position = Vector3.MoveTowards(transform.position, _targetPosition, Speed * Time.deltaTime);

Then your code elsewhere just needs to set _targetPosition to whatever you like, and every frame the object will move towards it with speed Speed, and stop when it gets there without overshooting.

Whoops, you’re right. I should have caught that; I think this is a fairly common misunderstanding of how to use Lerp. Line 25 should be written as:

_token.transform.position = Vector3.Lerp(startPosition, transform.position, amountOfJourneyCompleted);

where startPosition is the position at the start of the journey. (I know you know this, gfoot, but perhaps Magian was still unclear on this point.)

Absolutely agree with that. I tend to be too quick to reach for Lerp myself, when often MoveTowards (or RotateTowards, etc.) would do the job in a much simpler way.

Thanks for all of the responses! Definitely some very good information.

After running this project this evening, I notice that the movement of the token is not occurring randomly at all. Instead, the token is translating only when the direction is northwest, north, or northeast.

In addition, I have changed my code to check distance in the conditional statement, but “Translating…” is still appearing in the Console repeatedly and never stopping.

void Update () {
		if(_token != null)
		{
			if(Vector3.Distance(_token.transform.position, transform.position) > 0.1F)
			{
				// Movement is only occuring in N, NE, NW directions.
				float distanceCovered = (Time.time - _transformStartTime) * 3.0F;
				float amountOfJourneyCompleted = distanceCovered / _journeyLength;
				_token.transform.position = Vector3.Lerp(_token.transform.position, transform.position, amountOfJourneyCompleted);
				//Debug.Log("distanceRemaining: " + distanceRemaining.ToString());
				Debug.Log("Translating...");
			}
		}
	}

I just tried this exactly as you have it entered and, while I think it is excellent information that I will note for future reference, it does not change anything about my problem. :frowning:

Just tried this but I have not wrapped my head around what exactly is happening yet. “Translating…” is only appearing once in the Console per click as it should, however, the token is not translating and instead a blue cube seems to be appearing at the clicked destination. No idea on this one yet and my daughter is basically howling in my ear, so I will have to come back to this shortly…

By the way, I replaced the code with:

	void Update () {
		if(_token != null)
		{
			if(Vector3.Distance(_token.transform.position, transform.position) > 0.1F)
			{				
				transform.position = Vector3.MoveTowards(_token.transform.position, transform.position, 3.0F * Time.deltaTime);
				Debug.Log("Translating...");
			}
		}
	}

Your latest code is adjusting “transform.position”. I assume you meant to adjust “_token.transform.position” instead?

In addition, it might be interesting to output the distance between the transforms within the loop…

Yes, correct. That is what rushing will do for you. I have corrected the code and now the token is moving… part of the way.

I have output the distance and and I am using Vector3.MoveTowards as suggested. The token moves part of the way and then jerks repeatedly.

Outputting the distance on each call to Update() shows that, after the token travels a bit of distance, it ping-pongs back and forth between two values (4.337266 and 0.084…). It appears that something else in the code is acting on the token, but I cannot figure out what it could be. The only other time a token appears to be transformed is during the call to Start() when the token is first instantiated.

Sounds like you’re right - that something else must be changing the position of the _token object. That 0.084 value though would definitely stop the posted translation loop…

If I remove all of the code from the Update method and enter the following code:

_token.transform.position = transform.position;

everything works perfectly. Every single time. When I click on a terrain tile after selecting a token, the token moves to the clicked terrain tile.

So I can’t imagine that something elsewhere in the code is transforming the token. The problem has to be in my MoveToward or Lerp logic I would think.

Ok, this problem is driving me insane. Is there anyone who might be willing to take a look at this project?

Are you still stuck at this? I don’t want to get a whole project — Unity projects are large and a pain to pass around.

So my suggestion is this: try to reproduce the problem in a trivial project… nothing in the scene but, say, a cube and a sphere. Call one of these the token, attach this script to the other one, assign the first to its _token property in the editor, and run. There are only two possibilities at this point:

  1. It works — now see how your real game differs from this trivial version. Rip stuff out of your real game piece by piece until that starts working, too, and then study the last piece ripped out to see why it was causing failure.

…or…

  1. It doesn’t work — but now you have a trivial case; post the code here, with a description of how your scene is set up, and any of us should be able to reproduce it. (But I doubt this will be the case, as the code by this point should be pretty fool-proof.)

Good luck,

  • Joe

… or you could go with this solution

public static Vector3 PerfectTransfer(Vector3 curPos , Vector3 distination , float MaxDelt)
    {
        Vector3 c = curPos;
        Vector3 d = distination;

        float step = MaxDelt * Time.deltaTime;

        float x = Mathf.MoveTowards( c.x , d.x , step);
        float y = Mathf.MoveTowards( c.y , d.y , step);
        float z = Mathf.MoveTowards( c.z , d.z , step);
               
        return new Vector3( x , y , z );
    }

i went with this solution to get less shaky transition.

There is a Vector3.MoveTowards that does in one line, exactly what your code above is doing. In other words, you can simplify your function to:

    public static Vector3 PerfectTransfer(Vector3 curPos , Vector3 distination , float MaxDelt)
        {
            float step = MaxDelt * Time.deltaTime;
            return Vector3.MoveTowards(curPos, distination, step);
        }

I’d also note that the “MaxDelt” as it’s being used here is a speed, not a max delta… step is the max delta. If you actually want to pass in a max delta, then the whole method above boils down to exactly Vector3.MoveTowards!

(Still not sure how this relates to the OP’s problem, though.)