Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
✓ ANSWER ACCEPTED

Pathfinding makes NPC go back and forth instead of straight for the target, why?

So I've asked this before, but after messing around a little bit I think I found why, but I don't know what to do about it, so here is the part of the script that seems to fail:

-- Chase
	
	local plr_HRP = plr_character.HumanoidRootPart
	
	local destination = plr_HRP.Position
	local beginning = HRP.Position
	
	local path = PathfindingS:CreatePath(PathInfo)
	path:ComputeAsync(beginning, destination)
	
	if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()
		
		for cry, waypoint in pairs(waypoints) do
			if waypoints.Action == Enum.PathWaypointAction.Jump then
				Hum.Jump = true
			end
			
			Hum:MoveTo(waypoint.Position)
			Hum.MoveToFinished:Wait()
		end
		
	else
		Hum:MoveTo(HRP.Position)
	end

So this is what it looks like when i say it glitches: (gif) https://gyazo.com/cfd6fd7e9b350809dd352f2ed6cac2e9

Now, this doesn't happen if i remove Hum.MoveToFinished:Wait()

But I mean, that is a necessary part of a chase script no? for the enemy to wait before going to the next waypoint

What could i do?

If for some reason you need to see the full script, here it is:

--- Services ---

local Plrs = game:GetService("Players")
local RunS = game:GetService("RunService")
local PathfindingS = game:GetService("PathfindingService")

--- Variables ---

local char = script.Parent.Parent
local Hum = char.Humanoid
local HRP = char.HumanoidRootPart

local CanJump = false

local FOV = 50
local CharSize = char:GetExtentsSize()

local PathInfo = {
	ModelRadius = (CharSize.X + CharSize.Z)/4,
	ModelHeight = CharSize.Y,
	ModelCanJump = CanJump
}

--- Initialization --- 

HRP:SetNetworkOwner(nil)

--- Functions ---

local function FindClosestPlr()
	local players = {}
	local ClosestDistance = math.huge
	local target
	
	for index, targetplr in pairs(Plrs:GetPlayers()) do
		local TargetChar = targetplr.Character
		
		if TargetChar == nil then
			continue
		end
		
		local distance = (TargetChar.HumanoidRootPart.Position - HRP.Position)
		if distance.Magnitude <= FOV then
			table.insert(players, {
				Magnitude = distance.Magnitude,
				Player = targetplr
			})
		end
	end
	
	for i, entry in pairs(players) do
		local magnitude = entry.Magnitude
		local player = entry.Player
		
		if magnitude < ClosestDistance then
			ClosestDistance = magnitude
			target = player
		end
	end
	
	return target
end


function Chase()
	local plr = FindClosestPlr()
	
	if plr == nil then
		return
	end
	
	if plr.Character.Humanoid.Health <= 0 then
		return
	end
	
	local plr_character = plr.Character or plr.CharacterAdded:Wait()
	
	local plr_humanoid = plr_character.Humanoid
	
	-- Chase
	
	local plr_HRP = plr_character.HumanoidRootPart
	
	local destination = plr_HRP.Position
	local beginning = HRP.Position
	
	local path = PathfindingS:CreatePath(PathInfo)
	path:ComputeAsync(beginning, destination)
	
	if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()
		
		for cry, waypoint in pairs(waypoints) do
			if waypoints.Action == Enum.PathWaypointAction.Jump then
				Hum.Jump = true
			end
			
			Hum:MoveTo(waypoint.Position)
			Hum.MoveToFinished:Wait()
		end
		
	else
		Hum:MoveTo(HRP.Position)
	end
	
end

--- Connections ---

RunS.Heartbeat:Connect(Chase)

1 answer

✓ Accepted answer

The problem is because you're using Run Service's heartbeat signal which isn't ideal as signals create new threads meaning it won't yield for it to complete, which means it will try to get to a new waypoint, but it is also trying to get to an old waypoint.

So instead of using heartbeat signal you can make the function call itself when it ends as well as before it returns (add a wait to prevent script from timing out) and add the chase function at the end of the script. Example:

function Chase()
	if plr == nil then
		wait(1)
		Chase()
		return
	end
	-- Code here
	Chase()
end

Chase()

Also, this will cause the NPC to stop which I don't know how fix. It will also yield the thread but to fix this you can just wrap it in coroutine.

Full script after fix (not wrapped in coroutine):

--- Services ---

local Plrs = game:GetService("Players")
local RunS = game:GetService("RunService")
local PathfindingS = game:GetService("PathfindingService")

--- Variables ---

local char = script.Parent.Parent
local Hum = char.Humanoid
local HRP = char.HumanoidRootPart

local CanJump = false

local FOV = 50
local CharSize = char:GetExtentsSize()

local PathInfo = {
	ModelRadius = (CharSize.X + CharSize.Z)/4,
	ModelHeight = CharSize.Y,
	ModelCanJump = CanJump
}

--- Initialization --- 

HRP:SetNetworkOwner(nil)

--- Functions ---

local function FindClosestPlr()
	local players = {}
	local ClosestDistance = math.huge
	local target

	for index, targetplr in pairs(Plrs:GetPlayers()) do
		local TargetChar = targetplr.Character

		if TargetChar == nil then
			continue
		end

		local distance = (TargetChar.HumanoidRootPart.Position - HRP.Position)
		if distance.Magnitude <= FOV then
			table.insert(players, {
				Magnitude = distance.Magnitude,
				Player = targetplr
			})
		end
	end

	for i, entry in pairs(players) do
		local magnitude = entry.Magnitude
		local player = entry.Player

		if magnitude < ClosestDistance then
			ClosestDistance = magnitude
			target = player
		end
	end

	return target
end


function Chase()
	local plr = FindClosestPlr()
	
	if plr == nil then
		wait(1)
		Chase()
		return
	end

	local plr_character = plr.Character or plr.CharacterAdded:Wait()

	local plr_humanoid = plr_character.Humanoid

	if plr_humanoid.Health <= 0 then
		wait(1)
		Chase()
		return
	end

	-- Chase

	local plr_HRP = plr_character.HumanoidRootPart

	local destination = plr_HRP.Position
	local beginning = HRP.Position

	local path = PathfindingS:CreatePath(PathInfo)
	path:ComputeAsync(beginning, destination)

	if path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()

		for cry, waypoint in pairs(waypoints) do
			if waypoints.Action == Enum.PathWaypointAction.Jump then
				Hum.Jump = true
			end
			

			Hum:MoveTo(waypoint.Position)
			Hum.MoveToFinished:Wait()
		end

	else
		Hum:MoveTo(HRP.Position)
	end
	
	Chase()
end

--- Call Functions ---

Chase()

1 comment

0

I've read about coroutines before but i've honestly got no clue what is it, and what is it used for, the zombie stopping is really sad since it happened to me before and i didn't know how to fix it either, nonetheless, you did answer WHY is it happening so thank you alot!

This discussion is preserved as it was. New answers, comments, and votes are closed.