Hi everyone. I’m trying to build custom graph based on point graph that dynamicly updates in runtime based on entities as obstacles. Everything works fine untill I try to attach DynamicGridObstacle to the agent. Sometimes my agents just getting stuck permanently while trying to repath with this message:
Path Failed : Computation Time 0.000 ms Searched Nodes 0
Error: Couldn’t find a close node to the end point
There’s definately close node to the end point (since it works w/o DynamicGridObstacle), and settings is:
Graph UpdateArea, Connections and Collisions code:
public void UpdateArea(GraphUpdateObject o) {
if (nodes == null) {
Debug.LogWarning("Graph is not scanned, cannot update area ");
return;
}
//Copy the bounds
Bounds b = o.bounds;
bool willChangeWalkability = o.updatePhysics || o.modifyWalkability;
// Expanding bounds to fit more points
// Either the collision data is used a.k.a. capsule raycast or node size specified
if (collision.collisionCheck) {
Vector3 margin = new Vector3(collision.diameter, 0, collision.diameter) * 0.5F;
b.Expand(margin);
} else {
b.Expand(new Vector3(nodeSize, nodeSize, nodeSize));
}
if (maxDistance <= 0) {
b.Expand(b.size * 2);
} else {
// Increasing size of bounds to fit points that are left out
b.Expand(b.size * maxDistance);
}
// Initialization
if (_intersectedNodes == null) {
_intersectedNodes = new List<PointNode>();
} else {
_intersectedNodes.Clear();
}
// Creating list of nodes that intersect with these bounds
for (int i = 0; i < nodes.Length; i++) {
if (b.Contains((Vector3) nodes[i].position)) {
_intersectedNodes.Add(nodes[i]);
// Mark nodes that might be changed
o.WillUpdateNode(nodes[i]);
}
}
// Update Physics first
if (o.updatePhysics && !o.modifyWalkability) {
collision.Initialize(matrix, nodeSize);
foreach (PointNode node in _intersectedNodes) {
UpdateNodePositionCollision(node, o.resetPenaltyOnPhysics);
}
}
//Apply GUO
foreach (PointNode node in _intersectedNodes) {
if (b.Contains((Vector3) node.position)) o.Apply(node);
}
//To avoid too many allocations, these lists are reused for each node in CalculateConnections
if (_connections == null) {
_connections = new List<PointNode>(3);
}
if (_costs == null) {
_costs = new List<uint>(3);
}
// Recalculate connections
if (willChangeWalkability) {
for (int i = 0; i<_intersectedNodes.Count; i++){
CalculateConnections(_intersectedNodes[i], i);
}
}
}
/// <summary>
/// Calculates connections for a single node.
/// </summary>
/// <param name="node">Graph node</param>
/// <param name="index">Index of node in graph</param>
public virtual void CalculateConnections(PointNode node, int index) {
// All connections are disabled if the node is not walkable
if (!node.Walkable) {
// Reset all connections
// This makes the node have NO connections to any neighbour nodes
// Also removes reverted connections
node.ClearConnections(true);
return;
}
if (maxDistance >= 0) {
//Loop through all nodes and add connections to other nodes
for (int j = 0; j < _intersectedNodes.Count; j++) {
if (index == j) continue;
PointNode other = _intersectedNodes[j];
float dist;
if (IsValidConnection(node, other, out dist)) {
_cost = (uint) Mathf.RoundToInt(dist * Int3.FloatPrecision);
node.AddConnection(other, _cost);
other.AddConnection(node, _cost);
}
}
}
}
public virtual void UpdateNodePositionCollision(PointNode node, bool resetPenalty = true) {
RaycastHit hit;
bool walkable;
// Calculate the actual position using physics raycasting (if enabled)
// walkable will be set to false if no ground was found (unless that setting has been disabled)
Vector3 position = collision.CheckHeight((Vector3) node.position, out hit, out walkable);
node.position = (Int3) position;
if (resetPenalty) {
node.Penalty = initialPenalty;
// Calculate a penalty based on the y coordinate of the node
if (penaltyPosition) {
node.Penalty += (uint) Mathf.RoundToInt(
(node.position.y - penaltyPositionOffset) * penaltyPositionFactor);
}
}
// Check if the node is on a slope steeper than permitted
if (walkable && useRaycastNormal && collision.heightCheck) {
if (hit.normal != Vector3.zero) {
// Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
float angle = Vector3.Dot(hit.normal.normalized, collision.up);
// Add penalty based on normal
if (penaltyAngle && resetPenalty) {
node.Penalty +=
(uint) Mathf.RoundToInt((1F - Mathf.Pow(angle, penaltyAnglePower)) * penaltyAngleFactor);
}
// Cosinus of the max slope
float cosAngle = Mathf.Cos(maxSlope * Mathf.Deg2Rad);
// Check if the ground is flat enough to stand on
if (angle < cosAngle) {
walkable = false;
}
}
}
// If the walkable flag has already been set to false, there is no point in checking for it again
// Check for obstacles
node.Walkable = walkable && collision.Check((Vector3) node.position);
}
In DynamicGridObstacle I use GUO with settings:
_graphUpdateObject = new GraphUpdateObject(bounds) {
resetPenaltyOnPhysics = false
};
Edit: If I disable Constrain.Suitable check I’m getting this:
There is no valid path to the target (start area: 54, target area: 1)
First I thought the issue was in start point being unwalkable, it seems it’s finding start point just fine, I’ve checked that.
Any idea what I’m doing wrong?