Help with enemy NPC

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.

1 Like

I just tried this and it’s still very similar, albeit a little slower now. Maybe this section has something to do with it?

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 (WorkspacePathfindingUseImprovedSearch), 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).

1 Like

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.

1 Like

I added these checks and the stutters are definitely happening less often! Thank you!

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?

Post the script you’re using now so we can see what’s going on.

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)

Try taking out the MoveToFinished line. You may just get smoother movement because there won’t be a stop sent to the NPC until they get to the player.

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.