Pathfinding NPC stuttering

basically, i have an NPC and the problem is it Stutter-steps. The stutter stepping mostly occurs after its Target gets killed, or the stutter stepping is sometimes random.

what i mean by stutterstepping is walking, then stopping, then walking, and so on

vid of stutter step:

heres the code:

local pathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
local foundtarget = nil
local targetpos = nil
local humanoid = script.Parent.Humanoid
local humanoidrootpart = script.Parent.HumanoidRootPart
local body = script.Parent:FindFirstChild("Torso")
local destination = game.Workspace.Path:GetChildren()

local IdleAnim = humanoid.Animator:LoadAnimation(script.Parent.Idle)
local WalkAnim = humanoid.Animator:LoadAnimation(script.Parent.Walk)
local RunAnim = humanoid.Animator:LoadAnimation(script.Parent.Run)

local blockedConnection
for _, desc in pairs(body:GetDescendants()) do
	if (desc:IsA("BasePart")) then
		desc:SetNetworkOwner(nil)
	end
end
humanoidrootpart:SetNetworkOwner(nil)
repeat task.wait(1) until humanoidrootpart.Anchored == false
humanoidrootpart:SetNetworkOwner(nil)
local points = {}
for _,part in ipairs(destination) do
	table.insert(points,part)
	wait()
end

foundtarget = nil

function walkRandomly()
	foundtarget = nil
	local point = points[math.random(1,#points)]
	local target = FindTarget()
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position, point.Position)
	local waypoints = path:GetWaypoints()
	for _, waypoint in ipairs(waypoints) do
		if foundtarget == nil then
			print("DEBUG: WALKING RANDOMLY TO WAYPOINT")
		humanoid:MoveTo(waypoint.Position)
		path.Blocked:Connect(function()
			print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
			main()
		end)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		end
		if foundtarget == 1 then path:Destroy() break end
		humanoid.MoveToFinished:Wait()
		FindTarget()
	end
end
--[[
local path = game:GetService("PathfindingService"):CreatePath({
	AgentRadius = 2,
	AgentHeight = 5,
	AgentCanJump = false
})
--]]



function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
				main()
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			path:Destroy()
			break
		end

		if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
			print("DEBUG: FINDPATH(TARGET) (findPath)")
			findPath(target)
			break
		end
	end
end


function FindTarget()
	local dist = 40
	local target
	local players = game.Players:GetPlayers()
	for i,v in ipairs(players) do
		if (v.Character) then
			local torso = v.Character.Torso
			local human = v.Character.Humanoid
			if (humanoidrootpart.Position - torso.Position).Magnitude < dist and human.Health > 0 then
				if checkSight(torso) and human.Health ~= 0 then
					print("DEBUG: TARGET = TORSO (FindTarget)")
					print(human.Health)
					target = torso 
				end
			end
		end
	end
	return target
end


function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			print("DEBUG: FOUND TARGET (checkSight)")
			foundtarget = 1
			return true
		end
	end
	return false
end


function main()
	local target = FindTarget()
	if target and foundtarget == 1 then
		print(target)
		local Player = game.Players:GetPlayerFromCharacter(target.Parent)
		humanoid.WalkSpeed = 25
		findPath(target)
	else
		foundtarget = nil
		humanoid.WalkSpeed = 11
		walkRandomly()
	end
end

humanoid.Running:Connect(function(speed)
	if speed > 17 then
		IdleAnim:Stop()
		WalkAnim:Stop()
		RunAnim:Play()
	elseif speed > 4 and speed < 17 then
		IdleAnim:Stop()
		WalkAnim:Play()
		RunAnim:Stop()
	elseif speed <= 4 then
		IdleAnim:Play()
		WalkAnim:Stop()
		RunAnim:Stop()
	end

end)

while RunService.Heartbeat:Wait() do
	main() 
end
6 Likes

Try replacing your findPath() function with:

local function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
					findPath(target)
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			break
		else
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
			end
			humanoid:MoveTo(waypoint.Position)
			local waitCheck = humanoid.MoveToFinished:Wait(1)
			if not waitCheck then
				findPath(target)
				break
			end
	
			if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
				print("DEBUG: FINDPATH(TARGET) (findPath)")
				findPath(target)
				break
			end
		end
	end
end
3 Likes

I know for a fact that NPCs that do pathfinding on a server can sometimes stutter when near a player because the player becomes the network owner over the NPCs physics but the server still does the walking.

And because the player owns the NPC, the NPCs current location has to replicate to the server first (introducing lag) before the server gets notified of it’s new location.

Easiest solution I can come up with for such case is to make the client/player do the NPC movement if the NPC is chasing or near that specific player.

I know the client can be unreliable because of exploiters, but if a client is the network owner of a NPC then the client must also simulate it and do it’s AI functions so it won’t stutter.

2 Likes

if i do this, wouldnt the NPC look different when chasing the target than normal players (or are you saying the the Pathfind script inside the NPC should be replaced with a local script?)

how could i make the sverer the network owner over its physics so that the server handles both the physics and the walking?

1 Like

The NPC should still behave mostly normal, only difference is that it walks/chases the player on the client, but it still exists on the server of course.

There may be a better way to do it but my solution is to just make zombies and such for example chase the player on the client for the smoothest experience rather than trying to invent something ridiculously complicated and painful to maintain.

Edit: Setting network owner to the server is tricky.
It can be done with SetNetworkOwner(nil) but setting it to the server also makes it automatic which means the server will decide the owner.

1 Like

so your saying that the server handled some of the script, and client handlers the other? can you give an example on how i should do this? @C_Corpze

here is the code im using for the pathfinding inside of a script thats in the npc

local pathfindingService = game:GetService("PathfindingService")
local RunService = game:GetService("RunService")
local foundtarget = nil
local targetpos = nil
local humanoid = script.Parent.Humanoid
local humanoidrootpart = script.Parent.HumanoidRootPart
local body = script.Parent:FindFirstChild("Torso")
local destination = game.Workspace.Path:GetChildren()

local IdleAnim = humanoid.Animator:LoadAnimation(script.Parent.Idle)
local WalkAnim = humanoid.Animator:LoadAnimation(script.Parent.Walk)
local RunAnim = humanoid.Animator:LoadAnimation(script.Parent.Run)

local blockedConnection

humanoidrootpart:SetNetworkOwner(nil)
repeat task.wait(1) until humanoidrootpart.Anchored == false
local points = {}
for _,part in ipairs(destination) do
	table.insert(points,part)
	wait()
end

foundtarget = nil

function walkRandomly()
	foundtarget = nil
	local point = points[math.random(1,#points)]
	local target = FindTarget()
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position, point.Position)
	local waypoints = path:GetWaypoints()
	for _, waypoint in ipairs(waypoints) do
		if foundtarget == nil then
			print("DEBUG: WALKING RANDOMLY TO WAYPOINT")
		humanoid:MoveTo(waypoint.Position)
		path.Blocked:Connect(function()
			print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
			main()
		end)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		end
		if foundtarget == 1 then path:Destroy() break end
		humanoid.MoveToFinished:Wait()
		FindTarget()
	end
end
--[[
local path = game:GetService("PathfindingService"):CreatePath({
	AgentRadius = 2,
	AgentHeight = 5,
	AgentCanJump = false
})
--]]
local function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
					findPath(target)
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			break
		else
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
			end
			humanoid:MoveTo(waypoint.Position)
			local waitCheck = humanoid.MoveToFinished:Wait(1)
			if not waitCheck then
				findPath(target)
				break
			end

			if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
				print("DEBUG: FINDPATH(TARGET) (findPath)")
				findPath(target)
				break
			end
		end
	end
end

--[[
function findPath(target)
	local path = game:GetService("PathfindingService"):CreatePath({
		AgentRadius = 3,
		AgentHeight = 6,
		AgentCanJump = true
	})
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()

	for _, waypoint in ipairs(waypoints) do
		humanoid:MoveTo(waypoint.Position)
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			script.Parent.Humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		end
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				path.Blocked:Connect(function()
					print("DEBUG: PATH BLOCKED (findPath")
				main()
				end)
				task.wait()
				if target == nil then
					break
				elseif target.Parent == nil then
					break
				end
			until checkSight(target) == false
			print("DEBUG: LOST TARGET, DESTROYING PATH (findPath)")
			foundtarget = nil
			path:Destroy()
			break
		end

		if (humanoidrootpart.Position - waypoints[1].Position).Magnitude > 30 then
			print("DEBUG: FINDPATH(TARGET) (findPath)")
			findPath(target)
			break
		end
	end
end
--]]

function FindTarget()
	local dist = 40
	local target
	local players = game.Players:GetPlayers()
	for i,v in ipairs(players) do
		if (v.Character) then
			local torso = v.Character.Torso
			local human = v.Character.Humanoid
			if (humanoidrootpart.Position - torso.Position).Magnitude < dist and human.Health > 0 then
				if checkSight(torso) and human.Health ~= 0 then
					print("DEBUG: TARGET = TORSO (FindTarget)")
					print(human.Health)
					target = torso 
				end
			end
		end
	end
	return target
end


function checkSight(target)
	local ray = Ray.new(humanoidrootpart.Position, (target.Position - humanoidrootpart.Position).Unit * 50)
	local hit,position = workspace:FindPartOnRayWithIgnoreList(ray, {script.Parent}) 
	if hit then
		if hit:IsDescendantOf(target.Parent) then
			print("DEBUG: FOUND TARGET (checkSight)")
			foundtarget = 1
			return true
		end
	end
	return false
end


function main()
	local target = FindTarget()
	if target and foundtarget == 1 then
		print(target)
		local Player = game.Players:GetPlayerFromCharacter(target.Parent)
		humanoid.WalkSpeed = 25
		findPath(target)
	else
		foundtarget = nil
		humanoid.WalkSpeed = 11
		walkRandomly()
	end
end

humanoid.Running:Connect(function(speed)
	if speed > 17 then
		IdleAnim:Stop()
		WalkAnim:Stop()
		RunAnim:Play()
	elseif speed > 4 and speed < 17 then
		IdleAnim:Stop()
		WalkAnim:Play()
		RunAnim:Stop()
	elseif speed <= 4 then
		IdleAnim:Play()
		WalkAnim:Stop()
		RunAnim:Stop()
	end

end)

while RunService.Heartbeat:Wait() do
	main() 
end
1 Like

Try adding this underneath your variables:

script.Parent.PrimaryPart:SetNetworkOwner(nil)
3 Likes

I already have and its not that problem, what in trying to do right now is this

1 Like

Try setting all waits to task.wait. This helped with my loops. I would also recommend changing while RunService.Heartbeat:Wait() to while task.wait() do. However, thats what I would have done.

1 Like

just tried that, didnt work. i still think its @C_Corpze solution and you can look at his post and my post on what im confused about

1 Like

I see where you went wrong! (I think)

while RunService.Heartbeat:Wait() do
	main() 
end

In this script, you are waiting until the function is over with. This is why the npc is stuttering.
Try this instead.

while RunService.Heartbeat:Wait() do
	spawn(main)
end
1 Like

this made the npc just start spasming and jumping right now im just trying to figure out

@C_Corpze solution cause i think itll work but i just dont know how to do it

Allow me to explain it very quickly and simple.

  1. On the server you check if a player was let’s say… spotted or found by your NPC.

  2. Player has been spotted, set network owner to that player and fire a remote event.

  3. A local/client script inside the player’s (StarterGui? StarterPlayerScripts?)
    detects the remote event signal and will activate it’s AI movement function.

Because the client here has power over the NPC now, you should now be able to make the humanoid walk using the local script.
Do your pathfinding on the client, make NPC follow this path on the client.

You may want to use Humanoid:MoveTo(target position) and Humanoid.MoveToFinished to check if a humanoid has finished walking.

I used to write pathfinding and wait for the humanoid to reach the target position but figured that this resulted in very choppy behavior.

The correct way to check if a humanoid has finished moving to it’s target position is through Humanoid.MoveToFinished as this signal will immediately fire when a humanoid has stopped walking and will even pass you a argument that states whether it has reached it’s goal or not (will automatically fire and return false after 5 seconds).

humanoid.MoveToFinished:Connect(function(reachedGoal)
 if not reachedGoal then TryAgain() return end
 --If statement that returns immediately to avoid deeply nestled if/then statements

 WalkToNextPathWayPoint()
end)

Also if the player is in a direct line of sight, forget Pathfinging all together and just make it walk directly at the player.
Otherwise you get this issue where the player can keep walking around the NPC in circles and avoid it entirely because Pathfinding is too slow to make the NPC chase a player in a direct line.

6 Likes

Ill mark you as the solution for now unless I find anything wrong, and if I do ill just DM you.

1 Like

If the marked solution above doesn’t work then you can try this.

Funnily enough, I was actually facing the exact same problem yesterday when I was working out a simple pathfinding system for my npc’s on dynamic targets. I found out that excess calls to computeAsync can hang the NPC and cause minor stutters ( no suprise here ).

To fix this I did a variety of things, all of which I can’t showcase here, but the main things involved:

   1) Decrasing calls to computeAsync ( using normal Humanoid.MoveTo when path of travel is a straight line ) 
   2) Letting the server take ownership of the agent
   3) Dealing with all the animation code on the client
   4) Avoiding the use of the Humanoid.MoveToFinished event
   5) When calculating a path, move the NPC forward ( ie time spend calculating can be time spent moving )

I did more than the points above ( such as code for handling when the NPC gets stuck, and optimizations to decease raycasting and pathfinding calls ).

Here i’ve quickly gathered some of the main bits of code and modularized them into a class called PathfindingAgent for you. You can use the PathfindingAgent:CreateAgent( model, agent_settings ) method. I’ll leave you to go through the code and determine what the agent_settings table needs to hold. Also note that I just wrote this yesterday ( so its very primitive ) , and the one im sending here doesn’t contain a lot of the optimizations that I wrote. But it should still finish the job nicely. Here you go :-).


local PathfindingService = game:GetService("PathfindingService")
local RunService         = game:GetService("RunService")
local ReplicatedStorage  = game:GetService("ReplicatedStorage")
local Players            = game:GetService("Players")


local PathfindingAgent = {}
PathfindingAgent.__index = PathfindingAgent


local function GetAgentPath(path, origin, destination)
	local success, errorMessage = pcall(function()
		path:ComputeAsync(origin, destination)
	end)

	if not success or path.Status ~= Enum.PathStatus.Success then
		return 
	end

	local waypoints = path:GetWaypoints()
	local next_waypoint_index = 2
	local path_not_safe = false 

	local blockedConnection = nil

	blockedConnection = path.Blocked:Connect(function(waypoint_index)
		if waypoint_index >= next_waypoint_index then
			path_not_safe = true
			blockedConnection:Disconnect()
		end
	end)

	return function()
		if path_not_safe then return false end		
		next_waypoint_index += 1

		if not waypoints[next_waypoint_index] then
			blockedConnection:Disconnect()
			return false
		end

		return waypoints[next_waypoint_index].Position
	end
end


local function FindRandomWalkVector(pos,range)
	local region_size = Vector3.new(4,4,4)
	local exhausted_count = 0
	local max_iter_count = 10

	repeat
		exhausted_count += 1
		local random_x = math.random(-range,range)	
		local random_z = math.random(-range,range)	

		local ub = pos + Vector3.new(random_x,0,random_z)
		local lb = pos - Vector3.new(random_x,0,random_z) + region_size

		local region = Region3.new(ub,lb)
		local isEmpty = game.Workspace:IsRegion3Empty(region)		
		if isEmpty then return ub end
	until
	exhausted_count >= max_iter_count
end


function PathfindingAgent:CreateAgent(agent, agent_settings)
	local self = setmetatable({}, PathfindingAgent)
	
	self.Agent = agent
	self.Settings = agent_settings
	self.is_activated = nil
	
	self.movement_states = {
		InPath = 0,
		WaitingForPath = 1,
		Idling = 2,
		Dead = 3,
		Pathfinding = 4,	
		Chasing = 5,
	}

	self.status_states = {
		Activated = 0,
		DeActivated = 1,
		Contained = 2,
	}
	
	self.curr_movement_state = self.movement_states.Idling
	self.curr_status_state = self.status_states.DeActivated
	
	self.update_connection = nil
	
	self.Humanoid = self.Agent:WaitForChild("Humanoid")
	self.HumanoidRootPart = self.Agent:WaitForChild("HumanoidRootPart")
	self.Torso = self.Agent:WaitForChild("Torso")

	 _, self.bounding_box = self.Agent:GetBoundingBox()
	
	for i, part in pairs(self.Agent:GetChildren()) do 
		if part:IsA("BasePart") then 
			part:SetNetworkOwner(nil) 
		end
	end
	
	return self
end


function PathfindingAgent:Activate()
	self.update_connection = RunService.Heartbeat:Connect(function(dt)
		self:Update(dt)
	end)
	
	self.is_activated = true
end


function PathfindingAgent:Disable()
	if not self.is_activated then return end
	
	self.update_connection:Disconnect()
	self.current_movement_state = self.Movemenet_States.Idle
end


function PathfindingAgent:FindClosestTarget()
	local player_list = Players:GetPlayers()
	local target, smallest_distance = nil, math.huge

	for _, player in ipairs(player_list) do
		local character = player.Character
		if not character then return end

		local position = character.HumanoidRootPart.Position
		local difference = position - self.HumanoidRootPart.Position
		local distance = difference:Dot(difference)

		if smallest_distance > distance then
			smallest_distance = distance
			target = player
		end
	end

	if not target then
		return warn("No Closest Target Found")
	end

	return target.Character, smallest_distance
end


function PathfindingAgent:StraightLineToTarget(target)
	if not target then return end

	local rayParams = RaycastParams.new()
	rayParams.FilterDescendantsInstances = {self.Agent}
	rayParams.FilterType = Enum.RaycastFilterType.Blacklist

	local origin = self.HumanoidRootPart.Position
	local target_pos = target.HumanoidRootPart.Position
	local target_cf = target.HumanoidRootPart.CFrame
	local direction = (target_pos - origin)

	local sign = math.sign(direction:Dot(target_cf.RightVector))
	local bounding_box = self.bounding_box
	local scp_off = self.HumanoidRootPart.CFrame * Vector3.new(-sign * bounding_box.X / 2,0, bounding_box.Z/2)

	local rayResults = workspace:Raycast(scp_off, target_pos - scp_off, rayParams)
	if not rayResults then return end	

	if rayResults.Instance.Parent == target then
		return true
	end	

	return false
end


function PathfindingAgent:PathfindToTarget(target)
	--// The difference of this function to PATHFINDTOPOSITION
	--   is that we will not interrupt the state of pathfinding here

	local reachedConnection = nil
	local path_object = PathfindingService:CreatePath({
		AgentRadius = 4,
		AgentHeight = 1,
		AgentCanJump = false,
	})

	local origin = self.HumanoidRootPart.Position
	local destination = target.HumanoidRootPart.Position
	local PATH = GetAgentPath(path_object, origin, destination)
	if not PATH then 
		self.curr_movement_state = self.movement_states.WaitingForPath
		return
	end

	while self.curr_movement_state ~= self.movement_states.Idling or 
		self.curr_movement_state ~= self.movement_states.Chasing
	do
		local next_point = PATH()		
		if not next_point then break end

		self.Humanoid:MoveTo(next_point)

		repeat	
			local org = self.HumanoidRootPart.Position
			local dist = (org - next_point):Dot(org - next_point)
			self.Humanoid:MoveTo(next_point)
			RunService.Heartbeat:Wait()
		until 
		dist <= self.Settings.Waypoint_Threshold * self.Settings.Waypoint_Threshold or 
			self.curr_movement_state == self.movement_states.Idling or 
			self.curr_movement_state == self.movement_states.Chasing
	end

	if self.curr_movement_state == self.movement_states.Pathfinding then
		self.curr_movement_state = self.movement_states.WaitingForPath
	end
end


function PathfindingAgent:PathfindToPosition()
	
end


function PathfindingAgent:ChaseTarget(target)
	--// Simply just MoveTo the target :-)

	while self.curr_movement_state ~= self.movement_states.Idling do
		if not self:StraightLineToTarget(target) then
			break	
		end

		self.Humanoid:MoveTo(target.HumanoidRootPart.Position)
		RunService.Heartbeat:Wait()
	end

	self.curr_movement_state = self.movement_states.WaitingForPath
end


function PathfindingAgent:Update()	
	if not self.status_states == self.status_states.Activated then
		return 
	end	
	
	local closest_target, distance_sq = self:FindClosestTarget()
	local target = nil
	if closest_target then
		target = distance_sq < self.Settings.SCP_RANGE * self.Settings.SCP_RANGE and closest_target or nil
	end

	if not target then 
		self.curr_movement_state = self.movement_states.Idling
		
		local random_walk_bounds = self.Settings.RandomWalkBounds
		local random_walk_vector = nil--FindRandomWalkVector(random_walk_bounds)		
		
		self:PathfindToPosition(random_walk_vector)
	
		return
	end
	
	if self.curr_movement_state == self.movement_states.Pathfinding then
		if self:StraightLineToTarget(target) then
			self.curr_movement_state = self.movement_states.Chasing
			self:ChaseTarget(target)
			
			return
		end
	elseif self.curr_movement_state == self.movement_states.WaitingForPath then
		--// Start walking in order to reduce possible frames spent stuttering
		self.Humanoid:MoveTo( self.HumanoidRootPart.CFrame * Vector3.new(0,0,-5) )
	elseif self.curr_movement_state == self.movement_states.Chasing then
		--// Else we are chasing, no need to do anything
		return
	end
	
	self.curr_movement_state = self.movement_states.Pathfinding
	self:PathfindToTarget(target)
end


return PathfindingAgent
14 Likes

thanks, ill def use this. people like u make the devforum a better place

3 Likes

Yooooo thank you!! I have been stuck on this problem for days.

2 Likes