I need help with pathfinding

I need a pathfinding system for my semi open world horror game monster. I’ve found SimplePath which saved me from dealing with roblox’s pathfinding system stuff. I’ve been trying different methods to get the monster to work but it nothing works. So far I’ve used this for detecting the player and simplepath’s functions to capture the player but I have no idea how to fix the bugs I’m encountering currently.

The NPC keeps switching waypoints every second(I tried doing this without a loop and just using events but then the npc doesn’t see the player while walking towards the waypoint. I’ve been working on this for a few days non stop and at this point I need some help from more experienced devs.

Here’s what I have so far

local rs = game:GetService("ReplicatedStorage")
local ps = game:GetService("Players")

local simplePath = require(rs:WaitForChild("SimplePath"))

local NPC = script.Parent
local nHum = NPC:WaitForChild("Humanoid")
local nHrp = NPC:WaitForChild("HumanoidRootPart")

local Range = 60
local FOV = 90

function getCharacters()
	local Characters = {}
	
	for _, b in ps:GetPlayers() do
		table.insert(Characters, b.Character)
	end
	return Characters
end

function Attack(char, dist)
	if dist <= 5 then
		char:FindFirstChild("Humanoid").Health = 0
		fovCast()
	else
		attackPath = simplePath.new(NPC)
		attackPath:Run(char:FindFirstChild("HumanoidRootPart"))
	end
end

function Patrol()
	print("Patrol")
	local waypoints = game.Workspace.Waypoints:GetChildren()
	local random = math.random(1, #waypoints)

	patrolPath = simplePath.new(NPC)
	patrolPath.Visualize = true
	
	patrolPath:Run(waypoints[random])
	patrolPath.Reached:Connect(function()
		fovCast()
	end)
	patrolPath.Blocked:Connect(function()
		fovCast()
		print("pathfinding blocked")
	end)
	patrolPath.Error:Connect(function(errorType)
		print("pathfinding error:", errorType)
		fovCast()
	end)
end

function checkLineOfSight(Start, End)
	local Params = RaycastParams.new()
	Params.FilterDescendantsInstances = {NPC, getCharacters()}
	Params.FilterType = Enum.RaycastFilterType.Exclude
	
	local Raycast = game.Workspace:Raycast(Start, End - Start, Params)
	
	if Raycast then
		return false
	else
		return true
	end
end

function fovCast()
	print("fovCast")
	for _, b in ps:GetPlayers() do
		local Character = b.Character
		
		if Character then
			local npcToCharacter = (Character.Head.Position - NPC.Head.Position).Unit
			local npcLookVector = NPC.Head.CFrame.LookVector
			
			local dotProduct = npcToCharacter:Dot(npcLookVector)
			local Angle = math.deg(math.acos(dotProduct))
			
			local Distance = (NPC.HumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude
			
			if Angle <= FOV and Distance <= Range and checkLineOfSight(NPC.Head.Position, Character.Head.Position) then
				Attack(Character, Distance)
				print("a")
			else
				Patrol()
			end
		end
	end
end

while wait(1) do fovCast() end
3 Likes

Your script calls the Patrol function inside of the FOVCast function, meanwhile you’re calling the FOVCast function in a loop with a delay of 1 second.

Everytime Patrol is called, the NPC chooses a random waypoint and walks to it. Since the function is being called every second, the NPC is going to switch every second.

Try this:

local Debounce = false -- Put this before the Patrol() function!!

local PatrolMechanic = coroutine.create(function()
     while task.wait() and not Debounce do
          Patrol()
          Debounce = true
     end
end)

coroutine.resume(PatrolMechanic)

Add this line after patrolPath.Reached:Connect(function()

Debounce = false

Remove any unneeded FOVCast() and Patrol()

If this worked, please mark it as a solution. If not, let me know the error you’ve encountered.

1 Like

I will try this once I’m home, give me a few days

the patrolling works but when the npc sees me and attacks me my game freezes for around 5 seconds and the npc stops. And my output looks like this:

I remember having this issue before and not being able to fix it.

In the parentheses of the task.wait() in the code I sent you, put 0.35.

it just makes the npc wait a bit before going to another waypoint while patrolling. However, the issue is with the attacking function being called like thousands of times in a short period of time when the npc sees the player AFTER reaching the waypoint. Another issue that I couldnt fix is that the npc won’t detect the player while on its way to a waypoint. It has to reach the waypoint to see the player which isn’t intended but I have no idea how to fix that one.

I’ve been able to implement your coroutine into the attacking method aswell. But I still have no idea how to make it so the npc will still see the player while going ot a waypoint, also when the player is moving the npc doesnt follow them but instead goes to their location at the time the function was called. Here’s the updated code.

local rs = game:GetService("ReplicatedStorage")
local ps = game:GetService("Players")

local simplePath = require(rs:WaitForChild("SimplePath"))

local NPC = script.Parent
local nHum = NPC:WaitForChild("Humanoid")
local nHrp = NPC:WaitForChild("HumanoidRootPart")

local Range = 60
local FOV = 90

function getCharacters()
	local Characters = {}

	for _, b in ps:GetPlayers() do
		table.insert(Characters, b.Character)
	end
	return Characters
end

function Patrol()
	local waypoints = game.Workspace.Waypoints:GetChildren()
	local random = math.random(1, #waypoints)
	
	local patrolPath = simplePath.new(NPC)
	patrolPath.Visualize = true
	
	patrolPath:Run(waypoints[random])
	patrolPath.Reached:Connect(function()
		Debounce = false
		fovCast()
	end)
end

function Attack(char, dist)
	if dist <= 5 then
		char:FindFirstChild("Humanoid").Health = 0
		Debounce = false

		local AttackMechanic = coroutine.create(function()
			while task.wait(.35) and not Debounce do
				Patrol()
				Debounce = true
			end
		end)

		coroutine.resume(AttackMechanic)
		fovCast()
	else
		local attackPath = simplePath.new(NPC)
		attackPath:Run(char:FindFirstChild("HumanoidRootPart"))
		Debounce1 = false
		fovCast()
	end
end

function checkLineOfSight(Start, End)
	local Params = RaycastParams.new()
	Params.FilterDescendantsInstances = {NPC, getCharacters()}
	Params.FilterType = Enum.RaycastFilterType.Exclude

	local Raycast = game.Workspace:Raycast(Start, End - Start, Params)

	if Raycast then
		return false
	else
		return true
	end
end

function fovCast()
	print("fovCast")
	for _, b in ps:GetPlayers() do
		local Character = b.Character

		if Character then
			local npcToCharacter = (Character.Head.Position - NPC.Head.Position).Unit
			local npcLookVector = NPC.Head.CFrame.LookVector

			local dotProduct = npcToCharacter:Dot(npcLookVector)
			local Angle = math.deg(math.acos(dotProduct))

			local Distance = (NPC.HumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude

			if Angle <= FOV and Distance <= Range and checkLineOfSight(NPC.Head.Position, Character.Head.Position) then
				Debounce1 = false

				local PatrolMechanic = coroutine.create(function()
					while task.wait(.35) and not Debounce1 do
						Attack(Character, Distance)
						Debounce1 = true
					end
				end)

				coroutine.resume(PatrolMechanic)
				print("a")
			else
				Debounce = false

				local PatrolMechanic = coroutine.create(function()
					while task.wait(.35) and not Debounce do
						Patrol()
						Debounce = true
					end
				end)

				coroutine.resume(PatrolMechanic)
			end
		end
	end
end

wait(5)
fovCast()

If you want the npc to stop patrolling when it sees the player, you need some way to interrupt the patrol path. I don’t think SimplePath has a feature to interrupt it so that it completely stops walking. After that, to make the npc chase the player at it’s current position you need to also interrupt the chase path every few seconds and make a new path to the target player to update the path so that it goes to the new player position. Then, when the npc gets close enough to the player, or if it can create a path with only 1 or 2 waypoints, then instead of following the path, use Humanoid:MoveTo(char.PrimaryPart.Position) so that it can reach the player quicker. Interrupting paths is pretty important.

Edit: You CAN interrupt the path by doing Path:Stop(). Hope this helps!

You have to make it detect players on a different thread.

local DetectionMechanic = coroutine.create(function()
     while task.wait(1) do
          fovCast()
     end
end)

coroutine.resume(DetectionMechanic)

Unfortunately, I’m not too sure how to solve some other aspects of your code.

this breaks the patrolling state. The npc keeps switching which waypoint it’s going to go to so it barely moves.

I have no idea how I can do that I had a similar idea but I never got my code to work properly so I gave up on it