PathFindingService inefficiency [HELP]

In the clip shown above, the movement by the npc is clearly inefficient.
Script:

local NPC = script.Parent

while true do wait()
	local Check = workspace:GetPartsInPart(NPC.HumanoidRootPart)
	for i, v in ipairs(Check) do
		if v.Name == "HumanoidRootPart" or v.Parent:FindFirstChild("HumanoidRootPart") then
			local Humanoid = v.Parent:FindFirstChild("Humanoid")
			if Humanoid then
				Humanoid:TakeDamage(33)
			end
		end
	end
	for i, v in ipairs(NPC:GetChildren()) do
		if v:IsA("BasePart") or v:IsA("MeshPart") then
			v:SetNetworkOwner()
			v.CanCollide = false
		end
	end
	local Players = game.Players:GetPlayers()
	local Target, Distance = nil, math.huge
	for i, v in ipairs(Players) do
		if v.Character then
			local Character = v.Character
			local Magnitude = (NPC.HumanoidRootPart.Position - Character.HumanoidRootPart.Position).Magnitude
			if Magnitude < Distance then
				Distance = Magnitude
				Target = Character.HumanoidRootPart
			end
		end
	end
	
	if Target then
		local PathFindingService = game:GetService("PathfindingService")
		local Path = PathFindingService:CreatePath()
		Path:ComputeAsync(NPC.HumanoidRootPart.Position, Target.Position)
		
		local Part
		for i, v in ipairs(Path:GetWaypoints()) do
			Part = Instance.new("Part")
			Part.Position = v.Position
			Part.Anchored = true
			Part.Shape = Enum.PartType.Ball
			Part.CanCollide = false
			Part.Parent = workspace
		end
		for i, v in ipairs(Path:GetWaypoints()) do
			NPC.Humanoid:MoveTo(v.Position)
			NPC.Humanoid.MoveToFinished:Wait()
			Part:Destroy()
		end
	else
		warn("Target is nil")
	end
end

I’m completely out of ideas and can’t think of anything else. I’ve tried searching for similar problems on the devforum but I can’t find any.
I’m pretty sure I can just make light adjustments to my movement and the NPC wouldn’t be able to catch up to me, but I think I could just do Humanoid:MoveTo(Target.Position) if the Target is only a couple of studs away or under a certain magnitude. But the main problem is much more complicated than that.

plz help :roll_eyes: :cry: :cold_sweat: :point_right: :point_left: :people_hugging: :flushed:

2 Likes

I can tell you exactly why It is behaving this way. You are setting new target positions only after the NPC has moved to its current target. This results in the choppy pathfinding you are seeing. Additionally, the target that is eventually selected is quickly outdated because you do not account for the movement of the player. Both of these factors combined is your result, I have done similar stuff in the past

a quick suggestion is to comment out this line:

NPC.Humanoid.MoveToFinished:Wait()

and then add a predefined wait at the top like this:

while task.wait(0.1) do

instead of your current while header

This resulted in the NPC going through walls. Without the line:

The NPC would instead be stuck by a wall:

Simple idea, if the player is in the NPC’s line of sight then don’t use pathfinding service.

Run a raycast check at 1st or 5th pathfinding point to see if the player is in the line of sight. If the player is then just directly move to the player.

4 Likes

I am confused by what you’re saying. I didn’t tell you to modify that, you only had to remove the line with MoveToFinished:Wait()

This is a good suggestion. It would also be easier to implement a system that accounts for player movement this way. You could just raycast to the player every time the NPC tries to pick a target position, and if they are visible then cancel targets and move to intercept

I did that and the NPC is ignoring the walls

This solved my problem but now I’m facing another problem which is if the player resets their character before the NPC reaches the destination of the v, the npc will still go there.
Thanks for the solution though

For that, just detect if the player’s health is zero. For that, you need to constantly run the direct movement function.

1 Like

Following what @azavier123 said, here’s what is missing:

Delete or comment all this part

local Part

for i, v in ipairs(Path:GetWaypoints()) do
	Part = Instance.new("Part")
	Part.Position = v.Position
	Part.Anchored = true
	Part.Shape = Enum.PartType.Ball
	Part.CanCollide = false
	Part.Parent = workspace
end
for i, v in ipairs(Path:GetWaypoints()) do
	NPC.Humanoid:MoveTo(v.Position)
	NPC.Humanoid.MoveToFinished:Wait()
	Part:Destroy()
end

then replace it by this

local Part = nil --outside of the loop
local function UpdatePart(Position) --outside of the loop
    if Part then
       Part:Destroy()
    end

    Part = Instance.new("Part")
	Part.Position = Position
	Part.Anchored = true
	Part.Shape = Enum.PartType.Ball
	Part.CanCollide = false
	Part.Parent = workspace
end)

-----------------------------------------

if Target then
	local PathFindingService = game:GetService("PathfindingService")
	local Path = PathFindingService:CreatePath()
	Path:ComputeAsync(NPC.HumanoidRootPart.Position, Target.Position)
	
    UpdatePart(Path:GetWaypoints()[2])
    NPC.Humanoid:MoveTo(Path:GetWaypoints()[2])
else
	warn("Target is nil")
end

The objective here is that the NPC have to move only by a single Waypoint on each 0.1s loop instead of the whole path. This way the path is actively updated to constantly move to the player character. You need to increase weapoints distance a little bit for it to be more effective though.

Also, this line should be at the top of the script, outside of the loop, because it is slowing down the script/code running speed over time.

local PathFindingService = game:GetService("PathfindingService")
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.