Weird bug with my pathfinding script?

So I’ve made an NPC Following Script and it works pretty well. Thanks to the suggestions of other devforum users, I was able to speed up the NPC’s Movement and make it more lagless by using run service instead of a while wait() do loop. However, now I’ve encounted a weird bug:

For some reason my NPC just wants to wander quite a bit through the chase, and I can see this by looking at how the NPC randomly turns while following. I feel this is to do with my Character not always being detected during the loop as when I added an else to my character check and made it print if no character was detected, The script printed very ofen, so it calls the wander function. Also I am aware that my wander function isn’t the best. But I’lll post a topic linked to this later. For now here is the code:

local PathfindingService = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
local Humanoid = script.Parent.Humanoid
local HumanoidRootPart = script.Parent.HumanoidRootPart
local MaxDistance = 50
function FindClosestTarget(MaxDist, TableOfCurrentPlayers)
	local Target
	local SmallestDistance = MaxDist
	local PlayerWithSmallerDistance
	for _, player in pairs(TableOfCurrentPlayers) do
		local distance = player:DistanceFromCharacter(HumanoidRootPart.Position)
		if distance < SmallestDistance then
			SmallestDistance = distance
			PlayerWithSmallerDistance = player
		end
	end
	Target = PlayerWithSmallerDistance
	if Target ~= nil then  --Check if it isn't nil
		print(Target.Name) 
	end
	return Target
end

function Wander()
	local Character = script.Parent
	local Humanoid = Character.Humanoid
	local Base = workspace.Baseplate
	Humanoid:MoveTo(Vector3.new(math.random(-100,100),0,math.random(-100,100)))
end

function FindAndMoveToTargetPos()
	local TargetToMoveTo = FindClosestTarget(MaxDistance, game.Players:GetPlayers())
	if TargetToMoveTo ~= nil then
		
		local TargetChar = TargetToMoveTo.Character
		if TargetChar then
			local Target_HRP = TargetChar:WaitForChild("HumanoidRootPart",5)
			local path = PathfindingService:CreatePath()
			path:ComputeAsync(HumanoidRootPart.Position, Target_HRP.Position) 
			local waypoint = path:GetWaypoints()
			for i, waypoint in pairs(waypoint) do
				if waypoint.Action == Enum.PathWaypointAction.Jump then 
					Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				Humanoid:MoveTo(waypoint.Position) 
				Humanoid.MoveToFinished:Wait() 
			end
		else
			pcall(function()
				FindAndMoveToTargetPos()
			end)
		end
		return TargetToMoveTo
	else
		Wander()
	end
end


function Iterate()
	for _, v in pairs(script.Parent:GetChildren()) do
		if v:IsA("BasePart") then
			v:SetNetworkOwner(nil)
		end
	end
end

Iterate()

runservice.Heartbeat:Connect(FindAndMoveToTargetPos)

Thanks In Advance!
P.S you can paste this in an R15 Npc and see what I mean. I just want it so my character is always detected. Aa at the moment despite being in range. The NPC still fails to identify my character

You could see exactly what path it is taking and why if you plot the waypoints the NPC is using.
Add the following function:

local function drawPoints(waypoints)
-- Function to DRAW WayPoints	
	for _, waypoint in pairs(waypoints) do
		local part = Instance.new("Part")
		part.Shape = "Ball"
		part.Material = "Neon"
		part.BrickColor = BrickColor.new(0.8,0.2,0.8)
		part.Size = Vector3.new(0.6, 0.6, 0.6)
		part.Position = waypoint.Position
		part.Anchored = true
		part.CanCollide = false
		part.Parent = game.Workspace
	end
end

Then after your local waypoint = path:GetWaypoints() line, add

drawPoints(waypoint)

That will at least show you the path discovered for the NPC to take and might give you a hint as to why it wanders.

I do wonder whether your duplication of the waypoint variable may not be helping at all. One should be a table, the other a single variable. To stop confusion, try renaming:

			local waypoints = path:GetWaypoints()	-- The table of Waypoints
			for i, waypoint in pairs(waypoints) do		-- Loop thru a single waypoint from the table of waypoints
				if waypoint.Action == Enum.PathWaypointAction.Jump then 
					Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				Humanoid:MoveTo(waypoint.Position) 
				Humanoid.MoveToFinished:Wait() 
			end

Your suggestion did help. I think the problem isnt my wander function, but the fact that multiple paths are being calculated at once. Maybe because of that my NPC is finding it hard to choose which one to go for. Could simply calling the function whenever the HRP moves while constantly checking if the closest target has changed be a fix. Do you think that would work?

1 Like

I would make a raycast check every time you try to pathfind to see if the NPC has a clear line of sight with the player.

Edit: forgot to say that if it has the clear line of sight, just directly move to it, dont pathfind.

So what you mean is I should use a ray to detect if anything is in front of the NPC?

1 Like

Yeah, you would set the ignore list to both the target and the npc and make sure the ray returns no parts.

1 Like

Ok. So I use a ray to detect if anything is in front of the NPC and if it is, I can use the pathfinding to make the NPC move to the player around the obstacle, else I can just directly move to the player.

1 Like

Yes, you probably meant this but I just wanna make sure. You’re casting a ray between the player and the NPC

1 Like

Ok that’s great then. Will that fix the weird rotation of the character when following?

1 Like

I’m guessing it’s like turning backward and whatever. It should fix the problem while you’re directly moving towards the player, the jitter comes from the way you pathfind usually

1 Like

It doesn’t really fix the problem I’m experiencing. My NPC Still Randomly Rotates for a split second while following me. I redesigned the script, so this is the one I’m using now:

local PathfindingService = game:GetService("PathfindingService")
local runservice = game:GetService("RunService")
local Humanoid = script.Parent.Humanoid
local HumanoidRootPart = script.Parent.HumanoidRootPart
local MaxDistance = 50
local DebrisService = game:GetService("Debris")
local DebuggingMode = true -- Shows Path NPC Calculates
local Character = Humanoid.Parent
local RayToCheck = Ray.new(HumanoidRootPart.Position ,  Vector3.new(0,0, MaxDistance)) --Creating the ray down from the character

function FindClosestTarget(MaxDist, TableOfCurrentPlayers)
	local Target
	local SmallestDistance = MaxDist
	local PlayerWithSmallerDistance
	for _, player in pairs(TableOfCurrentPlayers) do
		local distance = player:DistanceFromCharacter(HumanoidRootPart.Position)
		if distance < SmallestDistance then
			SmallestDistance = distance
			PlayerWithSmallerDistance = player
		end
	end
	Target = PlayerWithSmallerDistance
	if Target ~= nil then  --Check if it isn't nil
		print(Target.Name) 
	end
	return Target
end

function MoveToTarget(Target)
	if Target then
		local Target_Char = Target.Character or Target.CharactrAdded:Wait()
		if Target_Char ~= nil and Target_Char:IsA("Model") then
			local HRP = Target_Char:WaitForChild("HumanoidRootPart", 5)
			if HRP then
				Humanoid:MoveTo(HRP.Position)
			end
		end
	end
end

function PathfindToTarget(Target)
	if Target then
		local Target_Char = Target.Character or Target.CharacterAdded:Wait()
		if Target_Char ~= nil and Target_Char:IsA("Model") then
			local Target_HRP = Target_Char:WaitForChild("HumanoidRootPart",5)
			local path = PathfindingService:CreatePath()
			path:ComputeAsync(HumanoidRootPart.Position, Target_HRP.Position) 
			if path.Status == Enum.PathStatus.Success and DebuggingMode then
				local WaypointsTable = path:GetWaypoints()
				for i, waypoint in pairs(WaypointsTable) do
					local part = Instance.new("Part")
					part.Shape = "Ball"
					part.Material = "Neon"
					part.Size = Vector3.new(0.6, 0.6, 0.6)
					part.Position = waypoint.Position
					part.Anchored = true
					part.CanCollide = false
					part.Parent = game.Workspace
					DebrisService:AddItem(part, 5)
					Humanoid:MoveTo(waypoint.Position) 
					Humanoid.MoveToFinished:Wait()
				end
			else
				local WaypointsTable = path:GetWaypoints()
				for i, waypoint in pairs(WaypointsTable) do
					Humanoid:MoveTo(waypoint.Position) 
					Humanoid.MoveToFinished:Wait()
				end

			end
		else
			pcall(function()
				PathfindToTarget()
			end)
		end
	end
end


function Iterate()
	for _, v in pairs(script.Parent:GetChildren()) do
		if v:IsA("BasePart") then
			v:SetNetworkOwner(nil)
		end
	end
end



function CheckRayCollisions(RayToCheck, Ray_Hit)
	local Closest_Plr
	Closest_Plr = FindClosestTarget(MaxDistance, game.Players:GetPlayers()) 
	if Closest_Plr ~= nil then
		if Ray_Hit then
			if Ray_Hit.Parent ==  Closest_Plr.Character or Closest_Plr.CharacterAdded:Wait() then
				return game.Players:GetPlayerFromCharacter(Ray_Hit.Parent)
			else
				return nil
			end
		end
	end
end

Iterate()
runservice.Heartbeat:Connect(function()
	local Ray_Hit = workspace:FindPartOnRay(RayToCheck, Character)
	local Target = FindClosestTarget(MaxDistance, game.Players:GetPlayers())
	if Target ~= nil then
	if CheckRayCollisions(RayToCheck,Ray_Hit ) ~= nil and CheckRayCollisions(RayToCheck) == Target then
		MoveToTarget(Target)
	else
			PathfindToTarget(Target)
		end
	end
end)

P.S. To See what i mean put this code in a script. Then put the script in an R15 NPC. That should show you what I’m experiencing

Do you think the .MoveToFinished:Wait() is causing the weird rotations

Possibly. I would do some benchmarking around the functions such as MoveToTarget, but I doubt that’s the cause. Everything seems right, except for these two lines (well actually 4 since you repeated it) :

Is there a chance that the humanoid is stopping between each waypoint for a brief second or so. Perhaps you may need to use magnitude calculation for a constant distance value, and call MoveTo when the conditions are met. This may sound like a dumb question, but can you try getting rid of MoveToFinished entirely?

Ok, I’ll try that and see if MoveToFinished is the cause for this

No way. MoveToFinished:Wait() was the root cause for the weird rotations. Thanks For That! Will I still have to implement your ‘magnitude calculation’ idea?

Well, I’m not sure if you want to get rid of MoveToFinished:Wait() entirely. MoveToFinished can be a bit glitchy sometimes, because the Humanoid has to wait until the waypoint is reached, resulting in a brief pause. I suggested magnitude because of issues such as this. A magnitude calculation would just mean that the transition between each waypoint would be smooth, if your updating it constantly, through a repeat wait until loop.

I’ll look into including that later. For Now Thank you