I’m trying to make an enemy NPC that constantly follows a smart path towards the closest player until they are at a close distance. I’ve got somewhat far but the NPC stutters sometimes, and this is not due to network ownership. I think it’s because of the way I’m using for loops through the waypoints to move the humanoid. I know I’m probably doing something really dumb here like making tons of loops that counteract each other causing the stutter, but I don’t really know what the right way is.
rs.Heartbeat:Connect(function(dt)
local success, errorMessage = pcall(function()
path:ComputeAsync(NPCRoot.Position, closestChar.HumanoidRootPart.Position)
end)
if success then
local waypoints = path:GetWaypoints()
for index, waypoint in pairs(waypoints) do
activeNPC.Humanoid.WalkSpeed = 16
activeNPC.Humanoid:MoveTo(waypoint.Position)
if (NPCRoot.Position - closestChar.HumanoidRootPart.Position).Magnitude <= 5 then
activeNPC.Humanoid.WalkSpeed = 0 -- to stop the npc from getting obnoxiously close
break
else
activeNPC.Humanoid.MoveToFinished:Wait()
end
end
end
end)
A new path is being computed every heartbeat and then a for loop for those new waypoints is run. This is surely bad and is the cause of my problem, but I don’t know what else to do, since Humanoid:MoveTo(closestChar.HumanoidRootPart.Position) is really dumb and doesn’t pathfind around obstacles, while the closestChar root part is constantly moving around and changing position. Thanks for any help!!
The problem most likely stems from how you use Heartbeat, since that fires every frame and doesn’t halt for anything. You could wrap your whole waypoint system in a while task.wait() do loop instead, so it doesn’t fire until after all of the code executes. That way you don’t have multiple loops firing at once and causing that stutter.
The if statement is fine (not sure if it’s necessarily needed depending on what you are trying to accomplish), the reason it might seem slower is because now you aren’t firing every frame, rather going off of an infinite loop. I know that there is a setting to use the new Pathfinding system roblox is currently working on to help improve the wall issues (Workspace → PathfindingUseImprovedSearch), but as for not constantly moving by the player, you’ll need to create a function to check if a player has moved a certain distance from the path goal.
If you want to keep RunService for that extra speed, then you’ll need to run checks and make sure that you aren’t running multiple loops at once (however I think this method might waste resources if I’m not mistaken).
How about save the player’s Position once every .1 second (1.6 studs if the player is walking 16 studs per second) and check their Position again. If their magnitude is greater than (just for rounding sake) 1.8 studs then recalculate the NPC’s waypoint. 1.8 studs should track the NPC pretty closely to the player’s new Position and only happen 10 times per second instead of every frame.
That way the NPC isn’t getting new waypoints when it doesn’t need to, you’re just doing a calculation which should save you some issues.
If the player isn’t moving then no new waypoint needs to be set.
1.8 studs per distance check should get you within the 5 stud range as well.
I am now checking the distance between the path goal and player, and it has helped the issue somewhat. It’s not perfect at the moment but I’ll keep at it. Thanks for the help!
Is there anyway I can prevent the humanoid from coming to a brief full stop each time a new path is created?
I’ve got a few versions that I’m messing around with but this is the primary one.
local pathGoal = closestChar.HumanoidRootPart.Position
if prevPathGoal == nil then
canCreateNewPath = true
elseif (prevPathGoal - pathGoal).Magnitude > 2 then
canCreateNewPath = true
end
if canCreateNewPath and not loopRunning then
loopRunning = true
path:ComputeAsync(NPCRoot.Position, pathGoal)
prevPathGoal = pathGoal
local waypoints = path:GetWaypoints()
for index, waypoint in pairs(waypoints) do
if (activeNPC.HumanoidRootPart.Position - waypoints[#waypoints].Position).Magnitude <= 3 then
break
else
activeNPC.Humanoid.WalkSpeed = 16
activeNPC.Humanoid:MoveTo(waypoint.Position)
if (NPCRoot.Position - pathGoal).Magnitude <= 3 then
activeNPC.Humanoid.WalkSpeed = 0
break
else
lastWaypointPosition = waypoint.Position
activeNPC.Humanoid.MoveToFinished:Wait()
end
end
end
loopRunning = false
end
My next idea is to figure out a way to keep the humanoid moving, because I believe the stuttering is caused when the humanoid changes path. I think there might be some benefit if I figured out how to keep the humanoid moving to the last position of the previous path, before following the new one. Or maybe combining the points somehow.
Potentially the origin of the path in path:ComputeAsync(NPCRoot.Position, pathGoal) is too outdated and is causing these issues?
(Let me know if you need more of the script but this version is still wrapped in runservice)
In this case it’s smoother but does not follow a smart path and instead tries to just walk straight to the end position, even if there are walls in the way.