OnTargetReached() wrong


#1

Using AIPath.cs

I’m running into an issue where the AI stops just outside of range of being able to swing at the player. After debugging I found these numbers:

AIPath.destination

  •   destination	"(150.4, 0.0, 333.3)"	UnityEngine.Vector3
    

AIPath.endReachedDistance
0.202

Inside OnTargetReached() from MovementUpdateInternal

  •   simulatedPosition	"(149.7, 0.0, 333.3)"	UnityEngine.Vector3
    
  •   tr.position	"(149.7, 0.0, 333.3)"	UnityEngine.Vector3
      updatePosition	true	System.Boolean
    

AIPath.interpolator.path

  •   path	Count=2	System.Collections.Generic.List`1[[UnityEngine.Vector3, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
    
  •   [0]	"(148.4, 0.0, 333.3)"	UnityEngine.Vector3
    
  •   [1]	"(149.9, 0.0, 333.3)"	UnityEngine.Vector3
    

If it isn’t clear from the numbers, the end of the path is about .6 meters from the requested destination, well outside the error margin returned by endReachDistance

You are returning OnTargetReached() based on the end of the path, which is an internal implementation feature the game shouldn’t have to care about
“float distanceToEnd = dir.magnitude + Mathf.Max(0, interpolator.remainingDistance);”

In Seeker I am using NodeConnection for start and end point snapping. I didn’t have this issue before when I used Original for the end point I think but that ends up walking through impassible areas.

In AIPath for the setting “WhenCloseToDestination” I have “Continue to Exact Destination”

Semi-related, another problem with OnTargetReached() is it is called back over and over even when the AI is not moving, once per path update. I have worked around it by checking the last value vs. the current though and not calling the callback again if nothing changed.


#2

Hi, I was wondering if this was going to be fixed in the next version? It’s fine if not, as it’s easy to put in my own OnTargetReached() equivalent.


#3

Hi

Yes OnTargetReached was indeed a bad name for that function when it was added to the package, and it does some weird things like being called over and over (once per path recalculation) which is usually not what you want. In later versions I have tried to replace it using the reachedEndOfPath property. That property will return true when the AI has reached the end of the path (defined as within ‘reachedEndDistance’ units of the end of the path) the ai is currently following, which is the best thing that I can do without getting in to game specific things.
Also note this one only claims to tell you if the agent has reached the end of the path, not if it has reached the destination. This is because it is pretty much impossible to know if an agent has reached its destination without some game specific knowledge. Would a position say 0.5 meters above the ground be considered reached ever for example? Many games on the other hand just want to know when the agent has reached wherever it is heading, not necessarily if that is exactly where the destination was.
If you want to know if the agent has reached the destination, a good proxy that may or may not work for you game is this check:

var distanceToDestination = Mathf.Max(ai.remainingDistance, Vector3.Distance(ai.position, ai.destination));
if (distanceToDestination < someThreshold) {
    // Do something
}

#4

Thanks for the quick answer.

As a feature suggestion I’d like to suggest adding the following or similar as a default. Knowing when the destination was reached is something nearly every game would need.

public float GetDistanceToDestination()
{
	if (float.IsPositiveInfinity(destination.x))
		return 0.0f;

	if (hasPath)
	{
		Vector3 distanceToEndNoHeight = (path.vectorPath[path.vectorPath.Count - 1] - destination);
		distanceToEndNoHeight.y = 0.0f;
		return remainingDistance + distanceToEndNoHeight.magnitude;
	}
	else
	{
		Vector3 distanceToEndNoHeight = (simulatedPosition - destination);
		distanceToEndNoHeight.y = 0.0f;
		return distanceToEndNoHeight.magnitude;;
	}
}
public bool DestinationReached()
{
	if (float.IsPositiveInfinity(destination.x))
		return true;

	Vector3 distanceToEndNoHeight = (simulatedPosition - destination);
	distanceToEndNoHeight.y = 0.0f;

	if (hasPath)
	{
		return remainingDistance < endReachedDistance * endReachedDistance &&
			distanceToEndNoHeight.sqrMagnitude < endReachedDistance * endReachedDistance;
	}
	else
	{
		return distanceToEndNoHeight.sqrMagnitude < endReachedDistance * endReachedDistance;
	}
}

RichAI properly check when a destination is reached
#5

As far as I know you can only check if the AI has reached the end of path, even if that means this:

The AI will not enter the red square, but you will have to write some code to see if the AI has reached the player ot it’s stuck somewhere and the player’s position is not reached.


#6

I got it working. I override AIPath Update() and use the code I pasted above

bool wasTargetReached = DestinationReached();
base.Update()
bool isTargetReached = DestinationReached();
if (wasTargetReached == false && isTargetReached == true)
{
	//Debug.Log("DestinationReached()");
}

#7

Yeah this is something I have debated a lot with myself. I am not sure if it is better to provide something approximate that works most of the time, but may fail in some situations or if I should only provide the things that the script can guarantee (e.g that is has reached the end of the path it is currently following, which is well defined). Is it better that users have to learn the trade-offs quickly or that they can get started easier but which may cause weird edge case behavior later in development. I’d love to hear your input on this.

Also note that your example code assumes that the y coordinate is not important, which will fail horribly if the game is for example set in a building with multiple floors.


#8

I consider the Y component by also checking remainingDistance, since there’s no way without additional info to know what Y different is safe to ignore.

“Is it better that users have to learn the trade-offs quickly or that they can get started easier but which may cause weird edge case behavior later in development. I’d love to hear your input on this.”

I think right now it’s not taking either option but presenting options that look like it is, so either way would be better. As far as which I would go with, I would present the easier start but document the limitations. The code has enough edge cases a new user wouldn’t be able to just write something correct from the start. I had to modify the code I posted multiple times to get it right, for example.