NPC Pathfinding tries to go through invisible barriers

path goes through invisible barriers that have CanCollide on, sometimes it doesnt but alot of time it does, i want the npcs to jump over it, i know its possible since they already make the jump but rarely

I tried making them jump a bit early which worked sort of, but still this happens often.

   function Movement.Move(root, humanoid, data, target, npc, path, distance, state)
	if state.stunned then return end

	--  Target validation
	if not target or not target:IsA("BasePart") or not target.Parent or not target.Parent:FindFirstChild("Humanoid") then
		data.strafeAngle = nil
		local align = root:FindFirstChild("AlignOrientation")
		if align then align.Enabled = false end
		return
	end

	--  Helper: get next valid waypoint
	local function getNextWaypoint(path, idx)
		if not path then return nil end
		local waypoints = path:GetWaypoints()
		return waypoints[idx], waypoints[idx + 1], #waypoints
	end

	--  Special case: fallback "Target"
	if target.Parent.Name == "Target" then
		local moveTarget = target.Position

		local currentWaypoint, nextWaypoint, maxIdx = getNextWaypoint(path, data.waypointIndex or 2)
		if currentWaypoint then
			-- Advance if close enough
			if (root.Position - currentWaypoint.Position).Magnitude < 3 then
				data.waypointIndex = math.min((data.waypointIndex or 2) + 1, maxIdx)
				currentWaypoint, nextWaypoint = getNextWaypoint(path, data.waypointIndex)
			end

			if nextWaypoint and nextWaypoint.Action == Enum.PathWaypointAction.Jump then
				local verticalDelta = nextWaypoint.Position.Y - root.Position.Y
				if verticalDelta < 10 then
					humanoid.Jump = true
					moveTarget = nextWaypoint.Position
				else
					moveTarget = currentWaypoint.Position
				end
				data.waypointIndex = math.min((data.waypointIndex or 2) + 2, maxIdx)
			else
				moveTarget = currentWaypoint.Position
			end
		end

		--  Only face if within 20 studs
		if (root.Position - target.Position).Magnitude <= 20 then
			local lookCFrame = CFrame.lookAt(root.Position, Vector3.new(target.Position.X, root.Position.Y, target.Position.Z))
			local align = root:FindFirstChild("AlignOrientation") or Instance.new("AlignOrientation", root)
			local attachment = root:FindFirstChild("AlignAttachment") or Instance.new("Attachment", root)
			align.Name = "AlignOrientation"
			align.Mode = Enum.OrientationAlignmentMode.OneAttachment
			align.RigidityEnabled = false
			align.Responsiveness = 100
			align.MaxTorque = math.huge
			align.Attachment0 = attachment
			align.CFrame = lookCFrame.Rotation
			align.Enabled = true
		else
			local align = root:FindFirstChild("AlignOrientation")
			if align then align.Enabled = false end
		end

		if moveTarget then
			humanoid:MoveTo(moveTarget)
		end
		return
	end

	--  Normal case: real player / humanoid target
	local npcsTargeting = TargetingSystem.getNPCsTargetingPlayer(target)
	local mainAttacker = npcsTargeting[1]

	if #npcsTargeting > 1 then
		table.sort(npcsTargeting, function(a, b)
			local aRoot = a:FindFirstChild("HumanoidRootPart")
			local bRoot = b:FindFirstChild("HumanoidRootPart")
			if not aRoot or not bRoot then return false end
			return (aRoot.Position - target.Position).Magnitude < (bRoot.Position - target.Position).Magnitude
		end)
		mainAttacker = npcsTargeting[1]
	end

	local shouldStrafe = (#npcsTargeting > 1 and npc ~= mainAttacker)

	local moveTarget
	if shouldStrafe then
		local radius = 12
		data.strafeAngle = (data.strafeAngle or math.random() * math.pi * 2) + 0.03
		moveTarget = Vector3.new(
			target.Position.X + math.cos(data.strafeAngle) * radius,
			root.Position.Y,
			target.Position.Z + math.sin(data.strafeAngle) * radius
		)
	else
		local currentWaypoint, nextWaypoint, maxIdx = getNextWaypoint(path, data.waypointIndex or 2)
		if currentWaypoint then
			-- Advance if close enough
			if (root.Position - currentWaypoint.Position).Magnitude < 3 then
				data.waypointIndex = math.min((data.waypointIndex or 2) + 1, maxIdx)
				currentWaypoint, nextWaypoint = getNextWaypoint(path, data.waypointIndex)
			end

			if nextWaypoint and nextWaypoint.Action == Enum.PathWaypointAction.Jump then
				local verticalDelta = nextWaypoint.Position.Y - root.Position.Y
				if verticalDelta < 10 then
					humanoid.Jump = true
					moveTarget = nextWaypoint.Position
				else
					moveTarget = currentWaypoint.Position
				end
				data.waypointIndex = math.min((data.waypointIndex or 2) + 2, maxIdx)
			else
				moveTarget = currentWaypoint.Position
			end
		else
			moveTarget = target.Position
		end
	end

	--  Only face if within 20 studs
	if (root.Position - target.Position).Magnitude <= 20 then
		local lookCFrame = CFrame.lookAt(root.Position, Vector3.new(target.Position.X, root.Position.Y, target.Position.Z))
		local align = root:FindFirstChild("AlignOrientation") or Instance.new("AlignOrientation", root)
		local attachment = root:FindFirstChild("AlignAttachment") or Instance.new("Attachment", root)
		align.Name = "AlignOrientation"
		align.Mode = Enum.OrientationAlignmentMode.OneAttachment
		align.RigidityEnabled = false
		align.Responsiveness = 100
		align.MaxTorque = math.huge
		align.Attachment0 = attachment
		align.CFrame = lookCFrame.Rotation
		align.Enabled = true
	else
		local align = root:FindFirstChild("AlignOrientation")
		if align then align.Enabled = false end
	end

	--  Always Move
	if moveTarget then
		humanoid:MoveTo(moveTarget)
	else
		humanoid:MoveTo(target.Position)
	end

	-- Extra safety: if MoveTo fails (blocked), try to jump or reset path
	humanoid.MoveToFinished:Once(function(reached)
		if not reached then
			humanoid.Jump = true
			data.waypointIndex = 2 -- reset path index
		end
	end)

	if path and not data._pathBlocked then
		data._pathBlocked = true
		path.Blocked:Connect(function(blockedIdx)
			data.waypointIndex = blockedIdx
			humanoid.Jump = true
		end)
	end
end

Any help is appreciated

1 Like

Can I see your pathfinding params?

1 Like

Morning, you can use this plugin to visualise paths and amend your different properties. As @Minhseu123 said, share your pathfinding params.

1 Like

Please look into Pathfinding Modifiers.

Basically, you make parts which act as no zone areas.

1 Like

Did you make sure that the obstacle and the NPC are collidable via Collissiongroups?

Also, it could be the Agent Params cause this, when the NPC is high enough, canJump is set to true, and the obstacle is small enough for the Pathfinding to think there´s a way.

1 Like

i have already been doing that, but i do want them to jump over it and i know its possible.

	local path = PathfindingService:CreatePath({
		AgentHeight = 6,
		AgentRadius = 4,
		AgentCanJump = true,
		Costs = {
			pathblock = 999999999, 
		}
	})

yes, they are all Default, i made invisible obstacle in the fence so they dont get stuck in it.

these are my params

	local path = PathfindingService:CreatePath({
		AgentHeight = 6,
		AgentRadius = 4,
		AgentCanJump = true,
		Costs = {
			pathblock = 999999999, 
		}
	})

This may not be a fix but your agent variables are too large.

The standard r6 height is 6, however more likely than not this is recorded at the root position so AgentHeight = 3 or 1.5 .

Secondly, the width of a standard r6 rig is Four but it clearly states “Radius” you’ve given the diameter so AgentRadius = 2 furthermore, the arms are usually non collidable so it should be 1 and as a safe area/compensation we can add 0.5 so AgentRadius = 1.5

1 Like

Thanks, it does look like the npcs now jump over it more often, but they often get stuck at corners even tho their waypoints seem to point correctly, for some reason it happens more often when setting smaller variables

1 Like

Way to go “roblox’s pathfinding”,

from here i can only say so much, as

  1. Thickening the modifiers
  2. Possibly doing a in-depth re-read of pathfinding.
  3. Make some platforms have a collision group for AI’s only which would prevent wrong doing and make their paths possibly simpler.

nevermind im just ignorant, i think its obviously because they are in the state of strafing or circling around player for combat, they kept strafing while hugging the wall sort of.

The solution for their bad path is changing the variables, thanks for telling me that, the green waypoint path now seems to be almost always correct, its up to me to fix the rest.

1 Like