PathfindingService : Path.Blocked not firing --- why?

Hello everyone. I’m very new to Roblox development and was testing out the Pathfinding article @ Character Pathfinding. I was wondering if someone might provide some insight as to why the Path.Blocked event isn’t getting fired in the below example. I’ve searched all over for any examples of this same issue and have come up with nothing.

Below you can see the path generated by the PathfindingService’s GetWaypoints() function. PathfindingService works great and generates efficient paths even when I place the Red target behind a complicated maze (although below it’s just a straight line). Pretty sweet.

The issue I’m having trouble getting my head around is why path.Blocked:Connect(onPathBlocked) isn’t firing when the above path gets blocked. Here’s how Path.Blocked is wired up in the script (again, this is just the script used in the Pathfinding article @ Oops!

local function onPathBlocked(blockedWaypointIndex)
	warn("onPathBlocked at blockedWaypointIndex: ", blockedWaypointIndex)
	-- Check if the obstacle is further down the path
	if blockedWaypointIndex > currentWaypointIndex then
		-- Call function to re-compute the path
		followPath(destination)
	end
end

-- Connect 'Blocked' event to the 'onPathBlocked' function
path.Blocked:Connect(onPathBlocked)

To test the above Path.Blocked event, I added a 5-second wait after the above path is generated, then Instanced a wall directly in the path, as you can see below:

As you can see in the capture above, the Path.Blocked event isn’t fired after the path is blocked. The onPathBlocked function is supposed to run, which then calls followPath() to generate a new path. But the Path.Blocked event is never fired, no new path is generated, and the Dummy running the script just runs into the wall and gets stuck. Poor dumb Dummy, I’m sorry I can’t make you smarter — be patient.

Below is the full script I used from Character Pathfinding. The only modification I’ve made is to Instance a wall to block the path after 5 seconds. Any insight into this puzzle would be greatly appreciated. Dummy wants to be smarter!

Many thanks in advance.


Full Script:

local PathfindingService = game:GetService("PathfindingService")
 
-- Variables for the zombie, its humanoid, and destination
local zombie = script.Parent
local humanoid = zombie.Humanoid
local destination = game.Workspace.Red
 
-- Create the path object
local path = PathfindingService:CreatePath()
 
-- Variables to store waypoints table and zombie's current waypoint
local waypoints
local currentWaypointIndex

function showPath(points)
	for _, waypoint in pairs(points) do
		local part = Instance.new("Part")
		part.Shape = "Ball"
		part.Material = "Neon"
		part.Size = Vector3.new(0.6, 0.6, 0.6)
		part.Position = waypoint.Position
		
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			part.Color = Color3.new(1,0,0)
		end
		
		part.Anchored = true
		part.CanCollide = false
		part.Parent = game.Workspace
	end			
end

 
local function followPath(destinationObject)
	-- Compute and check the path
	path:ComputeAsync(zombie.HumanoidRootPart.Position, destinationObject.Position)
	-- Empty waypoints table after each new path computation
	waypoints = {}
 
	if path.Status == Enum.PathStatus.Success then
		-- Get the path waypoints and start zombie walking
		waypoints = path:GetWaypoints()
		
		-- Move to first waypoint
		currentWaypointIndex = 1
		
		showPath(waypoints)
		
		if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end				
		
		humanoid:MoveTo(waypoints[currentWaypointIndex].Position)
	else
		if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end			
		
		-- Error (path not found); stop humanoid
		humanoid:MoveTo(zombie.HumanoidRootPart.Position)
	end
end
 
local function onWaypointReached(reached)
	if reached and currentWaypointIndex < #waypoints then
		currentWaypointIndex = currentWaypointIndex + 1

		if waypoints[currentWaypointIndex].Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end				
		humanoid:MoveTo(waypoints[currentWaypointIndex].Position)
			
	end
end
 
local function onPathBlocked(blockedWaypointIndex)
	warn("onPathBlocked at blockedWaypointIndex: ", blockedWaypointIndex)
	-- Check if the obstacle is further down the path
	if blockedWaypointIndex > currentWaypointIndex then
		-- Call function to re-compute the path
		followPath(destination)
	end
end
 
-- Connect 'Blocked' event to the 'onPathBlocked' function
path.Blocked:Connect(onPathBlocked)
 
-- Connect 'MoveToFinished' event to the 'onWaypointReached' function
humanoid.MoveToFinished:Connect(onWaypointReached)
 
followPath(destination)


wait(5)
local part = Instance.new("Part")
part.Shape = "Block"
part.Material = "ForceField"
part.Color = Color3.new(1,0,0)
part.Size = Vector3.new(4, 14.99, 63.15)
part.Position = Vector3.new(-48.13, 9.495, 92.035)
part.Parent = workspace

15 Likes

I have had the same question as MrReesh for quite some time now and never found an answer. Can anyone explain or offer a better working example of how to use Path.Blocked correctly so that it fires correctly?

5 Likes

Want to bump this old post because this bug is still relevant and still needs fixing. For my implementation, I have a very similar approach with a wall that changes cancollide properties, but the path.blocked event only SOMETIMES is fired. This makes it so certain NPCs will try to get stuck trying to run into the wall and others will find a new path.

3 Likes

Checking constantly if any path has been invalidated can add a slight amount of overhead to a huge number of part updates in games that would never need it. I suspect that some conditions exist so that blocking parts can be detected. My first hunch would be that the part must be controlled by the physics service, which means that it cannot be anchored or connected to any part that is welded. I’d also assume that when position / CFrame changes are made by scripts, path validation is also not performed.

If these theories don’t turn out to be true in your tests, I’d be happy to pull up Studio and do some testing myself to see if I can discover a pattern.

2 Likes

Hi MrReesh,

Replying to this first of all because Path.Blocked not firing still seems to be a problem. I am also new to development and, like you, followed the Pathfinding article step by step, and everything worked great up until the part about the Blocked event. So if there’s anyone who can provide some help with this topic, that would be great.

Secondly, though, I wanted to offer my (novice) suggestion, which you may have already thought of, as to how you might circumvent this issue for the time being: you can run a coroutine that checks if, while a humanoid is moving along its path, the magnitude of the velocity of any of its body parts is less than, say, 0.1 (meaning it’s stuck), and if so, recompute the path. This solution has worked fine for me so far.

8 Likes

I’m here 18 months later, and this is still an issue. I’m struggling with it right now, and I think this post should be bumped for a potential fix.

5 Likes

This is an issue for me too. What I do to fix it is to just not use the Path.Blocked function, and instead re-compute the path if the NPC does not reach its next waypoint in under 2 seconds. For the cooldown you will have to implement a custom function that looks like this. Event:Wait(N) where N is the cooldown, in this case 2. Unfortunately an inbuilt feature for this does not exist in Roblox at the time of this reply.

2 Likes

Same issue here… can’t get the event to fire. Not sure why. Documentation is lackluster on the developer page too.

2 Likes

I’ve had this same issue for a while now, and I don’t know how to make it immediately recompute the path if the path is blocked. I’m assuming that the Path.Blocked function is broken or doesn’t work well.

1 Like

File bug reports instead of bumping this thread, engineers won’t see it. Do some tests yourself first; if it really isn’t working and you’re able to confidently know this is a bug rather than an issue with your own code, support an existing report regarding Blocked or file a new one.

This still happens for me, can someone post this as a bug report??

1 Like

You could test the velocity of the NPC & if it is below your expected threshold then just re-compute the path from there, and/or, calculate when the NPC is expected to reach the next waypoint and call the compute again if it does not reach it by a certain time.

Like this;

humanoid.Running:Connect(function(speed)
	if speed < .1 then 
		followPath(destination)
	end
end)
2 Likes

Instead of using .Blocked, one can simply check the absolute value of the difference in positions one second apart. If the difference in position is less than one then its safe to assume the NPC is stuck.

function zombieStuck()
local position1 = zombie.HumanoidRootPart.Position.Magnitude
wait(1)
local position2 = zombie.HumanoidRootPart.Position.Magnitude
local deltaPosition = (math.abs(position1 - position2)) 
if deltaPosition < 1 then
	print("Zombie is stuck")
	computePath(destination)
else
	print("Zombie is NOT stuck")
end

end

something like this should work where computePath tells the NPC to recalculate the path to a certain objective

3 Likes

You would also need a while loop that continuously checks if the zombie is stuck. Maybe not the best solution out there but it works for sure.
while true do

wait(1)

zombieStuck()

end

Another option could be raycasting depending on the case

I found that calling Path:Destroy() after you don’t need its blocked event to fire fixes this

This is just a guess but in your script, try changing the path variable to a different name…? As you notice it appears a different color from the other variables…

(I never rlly used path finding so don’t mind meh)

Hi, Path.Blocked still isn’t working for me so I decided to create a raycasting solution to fix it, as best I can, it’s not perfect, but it should do the trick, and I’ve commented what everything does as best I can.

wait(10)
-- Variables

----Services
local PathService = game:GetService("PathfindingService")

----Constants
local destination = game.Workspace.GoToPart
local Chr = game.Workspace.NPC
local RootPart = Chr.PrimaryPart
local hum = Chr.Humanoid

----Non-Constants
local stillWalking = true 
local verifStopWalk = true
local hasMoved

-- Setup Raycast Parameters
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.FilterDescendantsInstances = {workspace.NPC}
raycastParams.IgnoreWater = true

-- Main Code
--- Setting Up Path

local function MoveThruPath(path)
	local waypoints = path:GetWaypoints()
	local chosenColour = Color3.new(math.random(0,1),math.random(0,1),math.random(0,1))
	
-- Loops thorugh waypoints
	  for i,v in pairs(waypoints) do
		
-- If player should still be walking (nothing that means it should stop 
--(e.g. walls blocking its path), continue walking)	
	
		if stillWalking == true then
			
-- Creates Parts at Each Waypoint to Indicate Where the Path Is
			local part = Instance.new("Part",workspace)
			part.Shape = Enum.PartType.Ball
			part.Size = Vector3.new(1,1,1)
			part.Color = chosenColour
			part.Position = v.Position
			part.Anchored = true
			part.CanCollide = false
			
-- Humanoid moving between waypoints
			hum:MoveTo(v.Position)
			
-- If Humanoid needs to jump, jump
			if v.Action == Enum.PathWaypointAction.Jump then
				hum.Jump = true
				part.Color = Color3.new(1, 0, 0)
			end
			
-- This wait is so humanoid has enough time to move between each waypoint, so it doesn't skip part of the path
			wait(0.3)
		else
-- If the player should not be walking, stop the walking function, and verify the player has stopped walking
			print("returned")
			verifStopWalk = true
			return
		end	
		
	end
end	





local function createPath()
-- Wait for when the player is definetely not walking, to continue with the rest of the function
	repeat
		wait()
	until verifStopWalk == true
	
-- Set it back to verify that the player might be walking again
	verifStopWalk = false
	
-- Set Up Path
	local path = PathService:CreatePath()	
	path:ComputeAsync(RootPart.Position,destination.Position)
	
-- Show that the player is allowed to walk again (no blocking walls, etc.)
	stillWalking = true
	
-- Call Function that Moves Player
	MoveThruPath(path)
end




local function rayCast()
	
-- Loop indefinetely
  while true do	
		
-- While the player should be walking (eliminates unneccesary raycasting, saving memory)
      while stillWalking == true do
			
-- Send a ray out of Players RootPart (middle of player), to check there's nothing in front of the player
		local raycastResult = workspace:Raycast(RootPart.Position,RootPart.CFrame.LookVector*2,raycastParams)
			
-- If Raycast is Nil, then it shows the player has moved out the vicinity of the blockage and can start creating new paths
-- hasMoved prevents constant creation of new paths when the player is still facing the same blockage as before, therefore stopping the player getting stuck not moving
		if raycastResult == nil then
		   hasMoved = true	
		end
			
-- If the player has moved out of sight of a blockage, but another one has come back in view, create new path
		if raycastResult and hasMoved then
-- Player should not be walking anymore and shows the player is in front of a blockage that is being sorted.	
			hasMoved = false
			stillWalking = false
			print("Object/terrain hit:", raycastResult.Instance:GetFullName())
-- Creates New Path
			local createdPath = coroutine.wrap(createPath)	
			createdPath()
		end
		wait()
    end	
   wait(0.2)	
  end
  print("over")
end

-- Starts Checking for Blockages
local testRayCoroutine = coroutine.create(rayCast)
coroutine.resume(testRayCoroutine)

-- Starts Moving Character through Path
local createdPath = coroutine.wrap(createPath)	
createdPath()


I can’t believe this hasn’t received any attention but I am here making an AI for citizen NPCs and I can’t find out why path.Blocked doesn’t work. I really hope this can be fixed or someone finds a solution. Before getting mad at me for bumping this post up, this issue has been consistent and everyone here has had the same problem so I hope I can get this more attention.

3 Likes

So the issue seems to be WaypointBlocked. If I put a part that intersects with a waypoint then event will fire. If you just have a part between 2 waypoints, the part will not fire. You can get around this issues by setting the distance between waypoints to be very small. This can cause replication issues for the :MoveTo command on server sided NPCs due to all the waypoints but it will cause the .Blocked event to fire.

2 Likes