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
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.
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!