Hey There, Im using AIPath and a simple state machine to move an agent that has a simple job: Move to a position, grab an item, move to a different position, drop that item. During the first update loop, where the agent is required to move to the item, everything works perfect. But as soon as the agent picks up the item, I check this:
if (aIPath.remainingDistance <= aIPath.endReachedDistance)
Which results in ‘true’ but that should not be the case. The new destination is far away. I’m guessing the seeker component didn’t calculate the new path properly?. Due to this, the agent never moves to the second destination but instead it drops the item right from the first destination. Right now, I’m adding a timer of 3 seconds after change destination and setting can move to true again to avoid this bug and that solves the issue. So right after setting the target, wait 3 seconds and then check if AIPath.remainingDistance <= AiPath.endReachedDistance. Here’s my complete script. Refer to the update loop.
using Pathfinding;
using System;
using UnityEngine;
using DG.Tweening;
using System.Linq;
public class LoadingAreaForkLiftAINew : Resource
{
private PutAwaySuggestion currentPutAwaySuggestionItem;
[SerializeField] AIPath aIPath;
[SerializeField] AIDestinationSetter destinationSetter;
[SerializeField] Seeker seeker;
[SerializeField] Transform cratePositionTransform;
private float timer;
private string currentAction = "";
bool foundItem = false;
public static event Action OnAnyForkLiftjobComplete;
public Transform destinationRack = null;
public DashboardRackData currentRack = null;
private bool isMoving = false;
//Delete Later
public string itemToSearch;
protected override void Start()
{
base.Start();
SimulationManager.Instance.OnItemArrangedOnTruck += SetTarget;
}
private void SetTarget()
{
if (isMoving || SimulationManager.Instance.putAwaySuggestionsGameObjectList.Count == 0)
return;
currentPutAwaySuggestionItem = SimulationManager.Instance.putAwaySuggestionsGameObjectList.First();
bool found = false;
foreach (var rack in SimulationManager.Instance.createdRackList)
{
foreach (var locator in rack.locators)
{
if (string.Equals(locator.locationName.Trim(), currentPutAwaySuggestionItem.suggested_location.Trim(), StringComparison.OrdinalIgnoreCase))
{
SimulationManager.Instance.putAwaySuggestionsGameObjectList.Remove(currentPutAwaySuggestionItem);
foundItem = true;
destinationRack = rack.binTransforms[UnityEngine.Random.Range(0, rack.binTransforms.Count)];
currentRack = rack;
isMoving = true; // Now we're busy moving!
//seeker.StartPath(transform.position, currentPutAwaySuggestionItem.transform.position, OnPathComplete);
destinationSetter.target = currentPutAwaySuggestionItem.transform;
currentState = ResourceState.PackingRequest;
found = true;
break;
}
}
if (found) break;
}
if (!found)
{
Debug.Log($"No locator found for suggested location: {currentPutAwaySuggestionItem.suggested_location}");
SimulationManager.Instance.putAwaySuggestionsGameObjectList.Remove(currentPutAwaySuggestionItem);
Destroy(currentPutAwaySuggestionItem);
if (SimulationManager.Instance.putAwaySuggestionsGameObjectList.Count > 0)
{
SetTarget();
}
}
}
private void OnPathComplete(Path p)
{
isPathPending = false; // Path calculation complete
if (!p.error)
{
// Path is valid; proceed to Move state
if (currentAction == "")
{
destinationSetter.target = currentPutAwaySuggestionItem.transform;
currentState = ResourceState.PackingRequest;
}
if (currentAction == "DropItem")
{
destinationSetter.target = destinationRack;
currentState = ResourceState.Move;
}
}
else
{
Debug.LogError("Pathfinding failed: " + p.errorLog);
// Handle failure: Either retry or find another target
SetTarget();
}
}
protected override void Update()
{
SimulateBatteryBehaviour();
if (isCharging) { return; }
base.Update();
if (!canOperate) return;
//if (isPathPending) return;
CheckAgentPosition();
Vector3 pos = transform.position;
pos.y = 0;
transform.position = pos;
if (currentState == ResourceState.Idle)
{
if (SimulationManager.Instance.putAwaySuggestionsGameObjectList.Count > 0)
{
SetTarget();
}
}
if (currentState == ResourceState.PackingRequest)
{
currentAction = "GrabItem";
//currentState = ResourceState.Move;
float maxTime = 3f;
timer += Time.deltaTime;
if (timer > maxTime)
{
currentState = ResourceState.Move;
Debug.Log("timer over");
timer = 0f;
}
}
if (currentState == ResourceState.Move)
{
aIPath.canMove = true;
if (aIPath.reachedEndOfPath && aIPath.remainingDistance <= aIPath.endReachedDistance)
{
Debug.Log("Reached end distance");
aIPath.canMove = false;
SetState(currentAction);
}
}
if (currentState == ResourceState.GrabItem)
{
currentPutAwaySuggestionItem.transform.parent = cratePositionTransform;
currentPutAwaySuggestionItem.transform.DOLocalMove(Vector3.zero, .5f);
//currentPutAwaySuggestionItem.GetComponent<DynamicGridObstacle>().enabled = false;
currentAction = "DropItem";
//isPathPending = true;
//foundItem = false;
//seeker.StartPath(transform.position, destinationRack.position, OnPathComplete);
destinationSetter.target = destinationRack.transform;
float maxTime = 3f;
timer += Time.deltaTime;
if (timer > maxTime)
{
currentState = ResourceState.Move;
Debug.Log("timer over");
timer = 0f;
}
}
if (currentState == ResourceState.DropItem)
{
currentPutAwaySuggestionItem.transform.parent = destinationRack;
currentPutAwaySuggestionItem.transform.DOLocalMove(Vector3.zero + new Vector3(0, .1f, 0), .5f);
currentPutAwaySuggestionItem.transform.DOScale(new Vector3(.4f, .4f, .4f), .5f);
currentPutAwaySuggestionItem.transform.DORotate(Vector3.zero, .5f);
currentRack.stockedItems.Add(currentPutAwaySuggestionItem);
currentPutAwaySuggestionItem = null;
currentRack = null;
currentAction = "";
destinationRack = null;
isPathPending = true;
currentState = ResourceState.Idle;
foundItem = false;
//SimulationManager.Instance.TotalItems++;
FinishPutAway();
if (SimulationManager.Instance.putAwaySuggestionsGameObjectList.Count > 0)
{
SetTarget();
}
else
{
OnAnyForkLiftjobComplete?.Invoke();
}
}
}
private void FinishPutAway()
{
isMoving = false;
if (SimulationManager.Instance.putAwaySuggestionsGameObjectList.Count > 0)
{
SetTarget();
}
}
private void SetState(string currentAction)
{
if (currentAction == "GrabItem")
{
Debug.Log("Setting state to grab item");
currentState = ResourceState.GrabItem;
}
if (currentAction == "DropItem")
{
Debug.Log("Setting state to drop item");
currentState = ResourceState.DropItem;
}
}
}
The newer FollowerEntity movement script was written partly to remedy this. It uses a much more sophisticated method of repairing the path to try as hard as possible to ensure this value is up to date.
Best tip is to use the FollowerEntity movement script instead
You can also call ai.SearchPath() after setting the destination, and then repeatedly check ai.pathPending, it will be true while the path is being calculated.
But my agent is stuck forever searching for a path and this debug log never gets printed. Checked the inspector and found that the path pending field remains true forever
The snippet I provided was after removing a lot of unnecessary code, sorry for the confusion. In the class, in the update loop, if (currentState == ResourceState.PackingRequest), if (currentState == ResourceState.GrabItem), if(currentState == ResourceState.WaitingForPath)
These deal with the concerned code