Pathfinding Inaccurate During Recomputation

So, I have created my own pathfinding system that uses Roblox’s pathfinding service. I have noticed that when the path is computed once the agent’s path is correct with respect to the agent’s radius; however, if the path is computed again a few seconds later, the agent has a tendency to run into walls. I am assuming that the geometry of the agent isn’t able to be compared the environment accurately when it is moving.

This is very obvious when the target is a player’s position. A snippet of my code is below to show the underlying logic.

function EasyPath:run(destination)
	assert(typeof(destination) == "Vector3", "Destination must be a Vector3.")
	-- Do not recompute the path if the destination is the same.
	if destination ~= self._previousDestination then
		local timeSinceComputed = DateTime.now().UnixTimestampMillis - self._lastComputed
		-- Do not recompute the path if the path is currently being computed or it was recently computed.
		if not self._isComputing and timeSinceComputed > EasyPath.COMPUTE_DELAY then
			self._isComputing = true
			local computationSuccess, errorMessage = pcall(function()
				local characterPosition = self._character.PrimaryPart.Position
				self._path:ComputeAsync(characterPosition, destination)
			end)

			local pathSuccess = self._path.Status == Enum.PathStatus.Success
			if not computationSuccess or not pathSuccess then
				self._nextWaypointIndex = -1
				self._events.PathErrored:Fire()
			else
				if self._connections["BlockedConnection"] then
					self._connections["BlockedConnection"]:Disconnect()
					self._connections["BlockedConnection"] = nil
				end
				
				self._previousDestination = destination
				self._connections["BlockedConnection"] = self._path.Blocked:Connect(function(blockedWaypointIndex)
					-- Check if the obstacle is further down the path.
					if blockedWaypointIndex >= self._nextWaypointIndex then
						self._connections["BlockedConnection"]:Disconnect()
						self._connections["BlockedConnection"] = nil
						self._events.PathBlocked:Fire()
						self._previousDestination = nil -- Allow for recomputations for this destination.
						-- The destination may not have changed, but the path has been blocked so removing
						-- the previous destination will allow for a path recomputation.
					end
				end)
				
				local waypoints = self._path:GetWaypoints()
				if waypoints and #waypoints > 2 then
					self._waypoints = waypoints
					self._nextWaypointIndex = 2
					self._lastWaypointTimestamp = os.time()

					if self._visualize then
						for i = #self._visibleWaypoints, 1, -1 do
							self._visibleWaypoints[i]:Destroy()
							self._visibleWaypoints[i] = nil
						end
						
						for i = 2, #self._waypoints do
							local visibleWaypoint = Instance.new("Part")
							visibleWaypoint.Size = Vector3.one
							visibleWaypoint.Position = self._waypoints[i].Position
							visibleWaypoint.Anchored = true
							visibleWaypoint.Material = Enum.Material.SmoothPlastic
							visibleWaypoint.Shape = Enum.PartType.Ball
							visibleWaypoint.BrickColor = BrickColor.White()
							visibleWaypoint.CanCollide = false
							visibleWaypoint.Parent = workspace
							table.insert(self._visibleWaypoints, visibleWaypoint)
						end
					end
				end	
			end

			self._lastComputed = DateTime.now().UnixTimestampMillis
			self._isComputing = false
		end
	end
	
	local waypoint = self._waypoints[self._nextWaypointIndex]
	if waypoint then
		-- If the agent is not on the ground reset the waypoint timestamp.
		-- The agent is most likely climbing and the next waypoint is far away.
		if self._humanoid.FloorMaterial == Enum.Material.Air then
			self._lastWaypointTimestamp = os.time()
		end
		
		local timeSinceLastWaypoint = os.time() - self._lastWaypointTimestamp
		if timeSinceLastWaypoint > EasyPath.STUCK_TIME_LIMIT then
			self._nextWaypointIndex = -1
			self._events.AgentStuck:Fire()
		else
			self._humanoid:MoveTo(waypoint.Position)
		end
	end
end

If the destination is changed and an obstacle is in front of the AI, the AI bumps into the obstacle ignoring its preset radius. I was wondering if anyone else has run into this problem and has designed a solution.

I am aware that I can generate raycasts to ensure that the next waypoint isn’t hitting an obstacle within the agent’s radius, but that defeats the entire purpose of pathfinding.

I did not find a fix to this yet, but increasing the computation delay does appear to make it work better.

As you said, you can perform raycasting to check for obstacles.
But doing this for every location of your path is not very efficient. This will also increase your CPU usage.
What you can do is :slight_smile:
Create a StartPoint and an EndPoint
Compute the path as usual
Check if there is an obstacle in the path (if yes, set the new EndPoint to this obstacle, and start over at step 1)

This way, you can check if there is an obstacle in the path, and if it is, you can stop computing it.
You can also use the BreadthFirstSearch algorithm instead of the A*, it is more efficient for finding shortes paths in many cases :slight_smile: Coding Challenge #68: Breadth-First Search Part 1 - YouTube

1 Like