Pathfinding breaks when obstructed

I’m trying to make very simple AI that paths around in a flat area. Every few seconds the AI moves. The position they move to in this area is randomly decided, so the path might have obstructions. I’m using code that’s provided in the official articles and I don’t know much about how it works.

When the AI can’t reach the destination, it breaks the rest of their movements. They just get stuck permanently trying to get to the spot that they can’t reach. I want the AI to just forget about a destination they can’t reach and stop moving when they can’t reach it.

local NavigableArea = {X = {-4.38, 40}, Z = {-7, 33}}
local function handlePath()
	local Players = game:GetService("Players")
	local RunService = game:GetService("RunService")

	local path = PathfindingService:CreatePath()
	
	local character = script.Parent:FindFirstChildWhichIsA("Model")
	local humanoid = character.Humanoid

	local TEST_DESTINATION = Vector3.new(math.random(NavigableArea.X[1], NavigableArea.X[2]), 9, math.random(NavigableArea.Z[1], NavigableArea.Z[2]))
	game.Workspace.DestiPart.Position = TEST_DESTINATION

	local waypoints = nil
	local nextWaypointIndex = nil
	local reachedConnection = nil
	local blockedConnection = nil

	local function followPath(destination)
		-- Compute the path
		local success, errorMessage = pcall(function()
			path:ComputeAsync(character.PrimaryPart.Position, destination)
		end)

		if success and path.Status == Enum.PathStatus.Success then
			
			local MAX_PATH_ATTEMPTS = 5
			local pathAttempts = 0
			
			while pathAttempts <= MAX_PATH_ATTEMPTS do
				pathAttempts += 1
				-- Get the path waypoints
				waypoints = path:GetWaypoints()

				-- Detect if path becomes blocked
				blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
					-- Check if the obstacle is further down the path
					if blockedWaypointIndex >= nextWaypointIndex then
						-- Stop detecting path blockage
						blockedConnection:Disconnect()
						-- Cease pathing ???????????????
						pathAttempts = MAX_PATH_ATTEMPTS + 1
					end
				end)

				-- Detect when movement to next waypoint is complete
				if not reachedConnection then
					reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
						if reached and nextWaypointIndex < #waypoints then
							-- Increase waypoint index and move to next waypoint
							nextWaypointIndex += 1
							humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
						else
							reachedConnection:Disconnect()
							blockedConnection:Disconnect()
						end
					end)
				end

				-- Initially move to second waypoint (first waypoint is path start; skip it)
				nextWaypointIndex = 2
				humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
			end
		else
			warn("Path not computed!", errorMessage)
		end
	end

	followPath(TEST_DESTINATION)
end

I haven’t been able to find a solution. Thank you for any help.

Definitly not the smartest or best solution, but you could group everything that could obstruct the NPC (or put a tag on them, just make them identifiable), shoot a raycast through the path the NPC is going to take (since I dont think it takes turns, if it does then shoot a raycast everytime it turns), and if the raycast hits any of the items identified as an obstruction, just cancel the entire operation and have the NPC pick another place to go

1 Like

I THINK I’ve figured out what was causing it, though I could be wrong

humanoid:moveto moves the character until humanoid:MoveToFinished is fired. If it isn’t fired within 8 seconds, moveto cancels. Calling moveto again will restart this 8 second timer. My AI was doing randomized movement every 5 seconds, which would continuously restart the timer so that the moveto never timed out.

You can cheat humanoid:MoveToFinished by changing the humanoid.WalkToPoint to the character’s current position. This makes the character stop moving even if they haven’t reached their destination.

I added this to the part of the function where the character moves to the next waypoint:

humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
wait(3)
humanoid.WalkToPoint = character.PrimaryPart.Position

This causes the AI to think their pathing is complete after 3 seconds regardless of whether they reached the destination