Hello, I’m currently working on a project where I need to create NPC’s such as SCP-173, however I’m struggling a little. First of all, I’m aware that this is not how SCP-173 is supposed to chase people according to the SCP lore itself, however this is the way the other SCP’s are supposed to move (walk) so I’m kind of working on it for now. In order to achieve a consistent chasing system for the SCP’s, I keep updating the path every single time the NPC reaches a waypoint to prevent it from trying to move to the previous position of the player. The following is my script:
local players = game:GetService("Players")
local module = require(script.Parent)
local noob = script.Parent.Parent
local pathfinding = game:GetService("PathfindingService")
for i, part in pairs(script.Parent:GetChildren()) do
if part:IsA("BasePart") then
part:SetNetworkOwner(nil)
end
end
while task.wait(0.1) do
local closestChar = module.findNearestNotLooking()
if(not closestChar) then --[[print("closestChar is nil")]] continue end
local pathUnsuccessful = false
local path = pathfinding:CreatePath({
AgentRadius = 3,
AgentHeight = 6,
AgentCanJump = false,
})
local previousPosition = nil
local function reCalculatePath()
path:ComputeAsync(noob.Torso.Position, workspace[closestChar.Name].HumanoidRootPart.Position)
if(path.Status == Enum.PathStatus.Success) then
print("Successfully created the path.")
else
print("Path was unsuccessful.")
pathUnsuccessful = true
end
end
repeat task.wait(0.03) do
reCalculatePath()
if(path.Status == Enum.PathStatus.NoPath) then
break
end
local waypoints = path:GetWaypoints()
local waypointToMove = waypoints[2] -- The reason why I'm using the second waypoint is that the first waypoint is mostly the same.
local alternative = waypoints[1]
if(waypointToMove) then
noob.Humanoid:MoveTo(waypointToMove.Position)
else
noob.Humanoid:MoveTo(alternative.Position)
end
noob.Humanoid.MoveToFinished:Wait()
print("MoveTo finished.")
end
until (not closestChar) or pathUnsuccessful or (module.isLooking(closestChar.Head, noob.Head.Position)) --(not module.isLooking(closestChar.Head, noob.Head.Position))
end
I’ve also tried using SimplePath which seems to be more consistent with the chasing system I’m working on, it doesn’t have the functionality required for the system.
i watched the video. It seemed like the npc stopped chasing you at some point. Do you know why this is? Could you add in visualization so I can see how it’s pathfinding? Try having it move to the third waypoint instead. Additionally, make some kind of recursive function incase there is no third waypoint.
Maybe you’re recomputing the path too often? Consider ROBLOX’s example script on their character pathfinding page: Character Pathfinding | Documentation - Roblox Creator Hub
A short snippet of their script shows how they use MoveToFinished to walk to the next waypoint if available, rather than recomputing an entire path everytime:
reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
if reached and nextWaypointIndex < #waypoints then
-- Increase waypoint index and move to next waypoint
nextWaypointIndex += 1
humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
else
reachedConnection:Disconnect()
blockedConnection:Disconnect()
end
end)
Basically, computing a path isn’t an instant operation. It yields a bit.
To fix this, don’t use MoveToFinished:Wait(). Just calculate the new path directly after telling the NPC to move. Since computing the path takes a bit of time, the NPC will be moving and calculating the path at the same time creating seamless movement.
Hello and thank you for your response. Why I am updating it way too often is that the player might have moved onto a spot which could be far away from the previous position (aka the position of the player when the NPC started chasing them) so I’m updating it regularly in order to prevent it from being useless.
Thanks to @hellohennessy, the system seems to be a lot more consistent and the NPC is now able to catch the player without any doubts. I will update the topic if any further issues seem to appear.
I see that I fixed the jiterring issue, but I think that we can add more optimizations.
First, we don’t need to pathfind if the NPC can just move in a straight path. I recommend that you raycast from the NPC to the player and check if there is an obstacle. If there is an obstacle, then you should pathfind. If there are no obstacles, then just use MoveTo()
Next, rather than compute the path every time just in case the player moves, why not only compute the path when the player moves? To do this, check if the distance between the player and the final waypoint is large or not. If the distance is large then it means that the player moved so you should recompute a new path. If the player hasn’t moved, then no need to compute a path.