Pathfinding agent suddenly ignoring Pathfinding Modifiers

My game development has been working smoothly until recently when the pathfinding monster for my horror game suddenly started walking through parts that I have the labels for the pathfinding modifier set to a cost of math.huge().

I can’t figure out what is happening, as I’ve tried the same script in a fresh test place, and the script worked perfectly. The NPC refused to walk into the part with the pathfinding modifier active. I even checked a previous version of my game (Which I can confirm used to work perfectly with the pathfinding system), which now also ignores pathfinding modifiers.

I thought that maybe the issue is that there are too many pathfinding modifiers because my game does have quite a bit of those to make sure it doesn’t get stuck, or maybe some game scripts causing problems within the pathfinding service, but then why would old versions of my game cease to function as intended now with these modifiers?

I can’t pinpoint exactly when this started happening, but I noticed it when one of the playtesters mentioned that the monster wasn’t reacting to the flare item, which is meant to scare the monster away by using a part with a modifier with the “FlareZone” label.

Here is a simplified script for my pathfinding service (I cut out all of the parts that link to specific things in my game so it can be used in a fresh test place):

local Players = game:GetService("Players")
local Pathfind = game:GetService("PathfindingService")

local main = script.Parent.Parent
local startPos = main.HumanoidRootPart.Position

local nearestPlayer
local nearestDistance
local nearestSneakingPlayer
local nearestSneakingDistance

local activeTask

local climbing = false

local path = Pathfind:CreatePath({
	AgentCanJump = true,
	AgentCanClimb = true,
	AgentRadius = 2,
	Costs = {
		Climb = 1,
		Water = 20,
		FlareZone = math.huge,
		StuckZone = 200
	}
})

local maxDistance = 1500

function checkRaycast(character)
	local raycastResult = nil
	if character then
		local rayOrigin = main.HumanoidRootPart.Position
		local rayDirection = character.HumanoidRootPart.Position - rayOrigin

		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {main, character}
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
	end
	return raycastResult
end

function update()
	main.HumanoidRootPart:SetNetworkOwner(nil)

	local nearestPlayer = nil
	local nearestDistance = nil
	local nearestSneakingPlayer = nil
	local nearestSneakingPlayer = nil

	for _, player in pairs(Players:GetPlayers()) do
		if player.Character and player.Character:FindFirstChild("Humanoid") and player.Character.Humanoid.Health > 0 then
			local character = player.Character
			local distance = (character.HumanoidRootPart.Position - main.HumanoidRootPart.Position).Magnitude

			if not character or distance > maxDistance or (nearestDistance and distance >= nearestDistance) then
				continue
			end

			nearestDistance = distance
			nearestPlayer = player
		end
	end

	if nearestPlayer then
		if checkRaycast(nearestPlayer.Character) ~= nil then
			script.Parent.CanSeePlayer.Value = false
		else
			script.Parent.CanSeePlayer.Value = true
		end
	else
		script.Parent.CanSeePlayer.Value = false
	end

	if script.Parent.WalkSpeed > 0 then
		if nearestPlayer ~= nil then
			script.Parent.TargetPlayer.Value = nearestPlayer.Character
		else
			script.Parent.TargetPlayer.Value = nil
		end

		if nearestDistance and nearestDistance > 65 then
			script.Parent.WalkSpeed = 9
		elseif nearestDistance and nearestDistance <= 65 then
			script.Parent.WalkSpeed = 21
		else
			script.Parent.WalkSpeed = 9
		end

		if nearestPlayer and nearestPlayer.Character:FindFirstChild('HumanoidRootPart') then
			path:ComputeAsync(main.HumanoidRootPart.Position, nearestPlayer.Character.HumanoidRootPart.Position)
		else
			path:ComputeAsync(main.HumanoidRootPart.Position, startPos)
		end

		local Waypoints = path:GetWaypoints()
		table.remove(Waypoints, 1)

		if activeTask ~= nil then
			coroutine.close(activeTask)
		end

		activeTask = coroutine.running()
		followPath(Waypoints)
	end
end

function followPath(waypoints)
	for _, point in ipairs(waypoints) do
		main.Humanoid:MoveTo(point.Position)
		local moveFinished = main.Humanoid.MoveToFinished:Wait()

		if not moveFinished then
			warn("Monster stuck; Skipping pathfind waypoint")
			main.HumanoidRootPart.CFrame = CFrame.new(point.Position)
		end

		if point.Action == Enum.PathWaypointAction.Jump then
			main.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
	end
end

while true do
	task.wait(.25)

	if script.Parent.TravelingToSeekLastSeen.Value == false and script.Parent:GetState() ~= Enum.HumanoidStateType.Climbing then
		coroutine.wrap(function()
			update()
		end)()
	end
end

I’ve tried with PassThrough both enabled and disabled.

I’m here to ask if anyone else has encountered this same issue, and if you can, please inform me of what you did to solve the problem. I’ve been at this for a while now and can’t figure out how to replicate the issue. I’m getting tired of trying to search for the issue myself and trying to find similar forums online.

If anyone would like further details on my situation, please ask! I’m not going to be by my computer for the next couple of days after posting this, so I might not be able to go into too much detail for a while. I might also have some very late responses during this time period.

Thank you so much for your time and help!

repro file for others
reprofilenpc.rbxl (1.8 MB)

anyway, you are right that it does work in a new file… i also tried to see if setting the part as canquery false would allow the npc to go through but thats not what happens, setting the modifer to passthrough true also doesnt allow him to go to me…

perhaps the npc was chasing the player already and he dropped a flare mid chase–that would make the most sense under the circumstances you are telling–I don’t see any path recomputes while monster is currently pathing its current waypoints.

The old version of my game is extremely bare-bones and I can also confirm this version used to work perfectly with the modifier, so there is no reason it shouldn’t be working…

I don’t know if that’s the issue. I simply can’t get the monster to stop going into the area it’s not supposed to. I uploaded a video to make the situation easier to understand.

The way I replicated what youve just shown–within the repro file–is if I am inside the zonepart & modifier passthrough = true

setting modifier passthrough = false should prevent computation of path to that player then

after looking at the video in its entirety… it is very strange indeed…

I think as a final resort I would try to visualize each way point, like in the first 2 they all waypoint into the sphere, I notice the last one stops abruptly right outside, and if inside it even traces backwards–i guess what im trying to get at is I don’t truly know whats going on in the background in terms of how you are stopping the npc from entering–from repro file, if a player is inside the flare or inside the castle the npc cant compute a path to you and stops.

the bellow is code to visualize waypoints, taken from SimplePath module

--Used to visualize waypoints
local visualWaypoint = Instance.new("Part")
visualWaypoint.Size = Vector3.new(0.3, 0.3, 0.3)
visualWaypoint.Anchored = true
visualWaypoint.CanCollide = false
visualWaypoint.Material = Enum.Material.Neon
visualWaypoint.Shape = Enum.PartType.Ball
--Create visual waypoints
local function createVisualWaypoints(waypoints)
	local visualWaypoints = {}
	for _, waypoint in ipairs(waypoints) do
		local visualWaypointClone = visualWaypoint:Clone()
		visualWaypointClone.Position = waypoint.Position
		visualWaypointClone.Parent = workspace
		visualWaypointClone.Color =
			(waypoint == waypoints[#waypoints] and Color3.fromRGB(0, 255, 0))
			or (waypoint.Action == Enum.PathWaypointAction.Jump and Color3.fromRGB(255, 0, 0))
			or Color3.fromRGB(255, 139, 0)
		table.insert(visualWaypoints, visualWaypointClone)
	end
	return visualWaypoints
end

Is the CanQuery property of the Part set to Enabled? This requirement is sadly lacking from the Roblox documentation on Pathfinding.

4 Likes

I used the exact same part within all of the test scripts. All of them have passthrogh set to false.

I believe I’ve already tried visualizing the path using the path visualization setting in Roblox Studio. It’s like it doesn’t even realize that the modifier is there and thinks it can walk right through it, as CanCollide is set to false. This used to work in old versions of my game, but just stopped working all of a sudden. That’s why I’m so confused.

Yes, CanQuery is set to true. Apologies for not showing the part’s properties in the first post.

ROBLOX pathfinding service has issues with large or a place with too many clustered parts in it

Ok, makes sense, but I’ve never had this issue with old versions of my game. Plus, I’m not sure that would correlate to causing it to ignore modifiers. The agent works well with my current sized map anyways.

Still an issue. Has nobody else had this problem or something similar before?

Since we’ve deduced that the issue lies in your original place file–the problem being place-reliant–I think you should delete stuff in parts and determine what is causing it to begin with, and then report back.

(inregards to the successful video parts) based on what you’ve shown I still find it strange that your NPC is able to even pathfind/moveto to the target given that the cost is Math.Huge and that in my own testing place I had the same Math.Huge and it would immediately stop and not even come to me after entering past the zone so I don’t really know what to say