The Pathfinding AI Stuttering problem

First of all, the problem is that while attacking the player or patrolling, it suddenly starts to move forward by hanging. I can’t find the reason. I just noticed something that when I open the console, the script works at the level it should when the rate is 3 and 3>x is large, but the problem starts when the rate is 2.

-- ModuleScript: ServerScriptService/EnemyNPCAI

local module = {}

function module.Run(EnemyNPC)
	local humanoid  = EnemyNPC:WaitForChild("Humanoid")
	local rootPart  = EnemyNPC:WaitForChild("HumanoidRootPart")
	EnemyNPC.PrimaryPart:SetNetworkOwner(nil)

	local mapClone = workspace
	local waypoints = mapClone:FindFirstChild("waypoint"):GetChildren()
	local enemyHealthThreshold  = 200
	local normalSpeed = 12
	local chaseSpeed = 20
	local lastAttackTime = 0
	local attackCooldown = 3 
	local lastRockCheck = 0
	local rockCooldown = 5
	local lastSeenPosition = nil
	local lostTargetTimer = 0
	local maxLostTime = 5
	local angerValue = EnemyNPC:FindFirstChild("Anger")

	local animationsFolder = game.ReplicatedStorage:WaitForChild("Animations")
	local attackAnimation = humanoid:LoadAnimation(animationsFolder:WaitForChild("Punch"))
	local hideAnimation = humanoid:LoadAnimation(animationsFolder:WaitForChild("Hide"))
	local grabRockAnim = humanoid:LoadAnimation(animationsFolder:WaitForChild("GrabRock"))
	local FitalityAnimation = humanoid:LoadAnimation(animationsFolder:WaitForChild("Grab"))
	local throwRockAnim = humanoid:LoadAnimation(animationsFolder:WaitForChild("ThrowRock"))

	local originalMaxHealth = humanoid.MaxHealth
	local originalHealth = humanoid.Health


	local function createRock()
		local rock = Instance.new("Part")
		rock.Name  = "Rock"
		rock.Size  = Vector3.new(1.5, 1.5, 1.5)
		rock.Shape = Enum.PartType.Block
		rock.Material = Enum.Material.Slate
		rock.BrickColor = BrickColor.new("Dark stone grey")
		rock.Anchored = false
		rock.CanCollide = false
		rock.Massless = false
		rock.Parent  = EnemyNPC

		local weld = Instance.new("Motor6D")
		weld.Name  = "RockWeld"
		weld.Part0 = EnemyNPC:FindFirstChild("Right Arm")
		weld.Part1 = rock
		weld.C0 = CFrame.new(0, -1.2, -0.5)
		weld.Parent = rock

		rock.Position = EnemyNPC["Right Arm"].Position - Vector3.new(0, 1.2, 0.5)
		return rock, weld
	end

	local function findNearbyThrowableNpc(position, radius)
		for _, npc in pairs(workspace:GetChildren()) do
			if npc.Name == "ThrowableNpc" and npc:FindFirstChild("HumanoidRootPart") then
				if (npc.HumanoidRootPart.Position - position).Magnitude <= radius then
					return npc
				end
			end
		end
		return nil
	end

	local function throwRockAtTarget(target)
		if not target or not target:FindFirstChild("HumanoidRootPart") then return end
		humanoid.WalkSpeed = 0
		grabRockAnim:Play()

		local ThrowableNpc = findNearbyThrowableNpc(EnemyNPC.HumanoidRootPart.Position, 20)
		local thrownPart, weld

		if ThrowableNpc then
			local humanoidThrowableNpc = ThrowableNpc:FindFirstChildOfClass("Humanoid")
			if humanoidThrowableNpc then
				humanoidThrowableNpc:ChangeState(Enum.HumanoidStateType.Physics)
				humanoidThrowableNpc:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
				humanoidThrowableNpc:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
				humanoidThrowableNpc:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
				humanoidThrowableNpc:SetStateEnabled(Enum.HumanoidStateType.Seated, false)
			end

			if ThrowableNpc:FindFirstChild("Humanoid") and ThrowableNpc.Humanoid:FindFirstChild("EvaluateStateMachine") then
				ThrowableNpc.Humanoid.EvaluateStateMachine = false
			end

			for _, part in pairs(ThrowableNpc:GetDescendants()) do
				if part:IsA("BasePart") then
					part.CanCollide = true
					part.Anchored = false
				elseif part:IsA("Script") or part:IsA("LocalScript") then
					part:Destroy()
				end
			end

			weld = Instance.new("Motor6D")
			weld.Name  = "ThrowableNpcWeld"
			weld.Part0 = EnemyNPC:FindFirstChild("Right Arm")
			weld.Part1 = ThrowableNpc.HumanoidRootPart
			weld.C0 = CFrame.new(0, -1.2, -0.5)
			weld.Parent = ThrowableNpc.HumanoidRootPart

			thrownPart = ThrowableNpc.HumanoidRootPart
		else
			thrownPart, weld = createRock()
		end

		task.wait(0.5)
		throwRockAnim:Play()
		task.wait(0.6)

		humanoid.WalkSpeed = chaseSpeed
		if weld then weld:Destroy() end
		thrownPart.CanCollide = false

		local direction = (target.HumanoidRootPart.Position - thrownPart.Position).Unit
		local bodyVelocity = Instance.new("BodyVelocity")
		bodyVelocity.Velocity  = direction * 100
		bodyVelocity.MaxForce  = Vector3.new(1e5, 1e5, 1e5)
		bodyVelocity.Parent = thrownPart

		game.Debris:AddItem(bodyVelocity, 0.5)

		local function cleanup(hit)
			if hit and hit.Parent == target then
				if target:FindFirstChild("Humanoid") then
					target.Humanoid:TakeDamage(40)
				else
					if ThrowableNpc then
						ThrowableNpc:Destroy()
					else
						thrownPart:Destroy()
					end
				end
			end
		end

		thrownPart.Touched:Connect(cleanup)

		delay(3, function()
			if thrownPart and thrownPart.Parent then
				if ThrowableNpc then
					ThrowableNpc:Destroy()
				else
					thrownPart:Destroy()
				end
			end
		end)
	end

	local function canSeeTarget(target)
		local origin = EnemyNPC.HumanoidRootPart.Position
		local direction = (target.HumanoidRootPart.Position - origin).unit * 100
		local ray  = Ray.new(origin, direction)
		local hit, pos  = workspace:FindPartOnRay(ray, EnemyNPC)

		if hit and hit:IsDescendantOf(target) then
			local npcForwardVector  = EnemyNPC.HumanoidRootPart.CFrame.LookVector
			local directionToTarget = (target.HumanoidRootPart.Position - origin).unit
			local dotProduct = npcForwardVector:Dot(directionToTarget)

			if dotProduct > math.cos(math.rad(150)) then
				return true
			end
		end
		return false
	end

	local function findTarget()
		local maxDistance = math.huge
		local nearestTarget = nil

		for _, player in ipairs(game.Players:GetPlayers()) do
			local character = player.Character
			if character and character:FindFirstChild("HumanoidRootPart") then
				local dist = (EnemyNPC.HumanoidRootPart.Position - character.HumanoidRootPart.Position).Magnitude
				if dist < maxDistance then
					if canSeeTarget(character) or dist <= 10 then
						nearestTarget = character
						maxDistance = dist
					end
				end
			end
		end

		local npcFolder = workspace:FindFirstChild("NPCs")
		if npcFolder then
			for _, npc in ipairs(npcFolder:GetChildren()) do
				if npc:IsA("Model") and npc:FindFirstChild("HumanoidRootPart") then
					local dist = (EnemyNPC.HumanoidRootPart.Position - npc.HumanoidRootPart.Position).Magnitude
					if dist < maxDistance then
						if canSeeTarget(npc) or dist <= 10 then
							nearestTarget = npc
							maxDistance = dist
						end
					end
				end
			end
		end

		return nearestTarget
	end

	local function getPath(destination)
		local PathfindingService = game:GetService("PathfindingService")
		local pathParams = {
			AgentHeight = 15,
			AgentRadius = 4,
			AgentCanJump  = false,
		}
		local path = PathfindingService:CreatePath(pathParams)
		path:ComputeAsync(EnemyNPC.HumanoidRootPart.Position, destination.Position)
		return path
	end

	local function turnAndPlayAnimation(target)
		EnemyNPC:SetPrimaryPartCFrame(
			CFrame.lookAt(
				EnemyNPC.HumanoidRootPart.Position,
				target.HumanoidRootPart.Position
			)
		)
	end

	local function isPathClearOrJump(targetPos)
		local origin = EnemyNPC.HumanoidRootPart.Position
		local direction = (targetPos - origin).Unit * 6
		local raycastParams = RaycastParams.new()
		raycastParams.FilterDescendantsInstances = {EnemyNPC}
		raycastParams.FilterType = Enum.RaycastFilterType.Exclude

		local forwardResult = workspace:Raycast(origin, direction, raycastParams)

		local downOrigin  = origin + Vector3.new(0, -2, 0)
		local downDirection = direction + Vector3.new(0, -6, 0)
		local downResult  = workspace:Raycast(downOrigin, downDirection, raycastParams)

		if forwardResult then
			local hitPos = forwardResult.Position
			local heightAboveGround = hitPos.Y - origin.Y
			if heightAboveGround <= 6 then
				if humanoid:GetState() ~= Enum.HumanoidStateType.Jumping then
					humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
				end
				return true
			else
				return false
			end
		elseif not downResult then
			return false
		else
			return true
		end
	end

	local function chaseTarget(target)
		while target and target.Parent and target:FindFirstChild("Humanoid") and target.Humanoid.Health > 0 do
			local distance = (target.HumanoidRootPart.Position - EnemyNPC.HumanoidRootPart.Position).Magnitude

			if canSeeTarget(target) then
				lastSeenPosition = target.HumanoidRootPart.Position
				lostTargetTimer  = 0

				if distance > 20 and math.random() < 0.1 then
					local currentTime = tick()
					if currentTime - lastRockCheck >= rockCooldown then
						lastRockCheck = currentTime
						throwRockAtTarget(target)
					end
				end

				if distance <= 5 then
					humanoid:MoveTo(EnemyNPC.HumanoidRootPart.Position)

					if target.Humanoid.Health <= 25 then
						local weld = Instance.new("Motor6D")
						weld.Part0 = EnemyNPC:FindFirstChild("Left Arm")
						weld.Part1 = target.HumanoidRootPart
						weld.C1 = CFrame.new(0, -0.2, -2) * CFrame.Angles(math.rad(-90), 0, 0)
						weld.Parent = EnemyNPC.HumanoidRootPart

						local player = game.Players:GetPlayerFromCharacter(target)
						if player then
							local closeRemote = game.ReplicatedStorage:FindFirstChild("CloseLocalEffects")
							if closeRemote then
								closeRemote:FireClient(player)
							end
						end

						FitalityAnimation:Play()
						task.wait(FitalityAnimation.Length)

						humanoid.WalkSpeed = 0
						if EnemyNPC:FindFirstChild("GrabKill") then
							EnemyNPC.GrabKill:Play()
						end

						local humanoid2 = target:FindFirstChild("Humanoid")
						if humanoid2 then
							humanoid2.EvaluateStateMachine = false
							humanoid2.PlatformStand = true
						end

						for _, part in ipairs(target:GetDescendants()) do
							if part:IsA("BasePart") then
								part.CanCollide = false
							end
						end

						local head = target:FindFirstChild("Head")
						if head and head:FindFirstChild("Neck") then
							head.Neck:Destroy()
						end

						for _, part in ipairs(target:GetDescendants()) do
							if part:IsA("BasePart") then
								part.CanCollide = true
							end
						end

						if humanoid2 then
							humanoid2.EvaluateStateMachine = true
							humanoid2.PlatformStand = false
						end

						weld:Destroy()
						target.Humanoid.Health = 0
						return
					else
						target.Humanoid.WalkSpeed = 16
						local currentTime = tick()
						if currentTime - lastAttackTime >= attackCooldown then
							lastAttackTime = currentTime
							if not attackAnimation.IsPlaying then
								attackAnimation:Play()
							end
							task.spawn(function()
								for i = 0, 1 do
									if target and target:FindFirstChild("Humanoid") and target.Humanoid.Health > 0 then
										target.Humanoid:TakeDamage(10)
										task.wait(1)
									else
										break
									end
								end
							end)
						end
						return
					end
				else
					if distance <= 10 then
						humanoid:MoveTo(target.HumanoidRootPart.Position)
					else
						if isPathClearOrJump(target.HumanoidRootPart.Position) then
							humanoid:MoveTo(target.HumanoidRootPart.Position)
						else
							local path = getPath(target.HumanoidRootPart)
							if path.Status == Enum.PathStatus.Success then
								for _, waypoint in ipairs(path:GetWaypoints()) do
									humanoid:MoveTo(waypoint.Position)
									humanoid.MoveToFinished:Wait(0.1)
									if not canSeeTarget(target) then
										break
									end
								end
							end
						end
					end
				end
			else
				if lastSeenPosition then
					humanoid:MoveTo(lastSeenPosition)
					lostTargetTimer += 0.5
					task.wait(0.5)

					if (EnemyNPC.HumanoidRootPart.Position - lastSeenPosition).Magnitude <= 6 then
						lastSeenPosition = nil
					end

					if lostTargetTimer >= maxLostTime then
						lastSeenPosition = nil
						return
					end
				else
					return
				end
			end

			task.wait(0.1)
		end
	end

	local function walkTo(destination)
		local path = getPath(destination)
		if path.Status == Enum.PathStatus.Success then
			for _, waypoint in ipairs(path:GetWaypoints()) do
				local target = findTarget()
				if target and target:FindFirstChild("Humanoid") and target.Humanoid.Health > 0 then
					task.wait(0.6)
					chaseTarget(target)
					break
				else
					local heightDifference = waypoint.Position.Y - EnemyNPC.HumanoidRootPart.Position.Y
					if heightDifference > 6 then
						if humanoid:GetState() ~= Enum.HumanoidStateType.Jumping then
							humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
						end
					end
					humanoid:MoveTo(waypoint.Position)
					humanoid.MoveToFinished:Wait()
				end
			end
		else
			humanoid:MoveTo(destination.Position - (EnemyNPC.HumanoidRootPart.CFrame.LookVector * 10))
		end
	end

	local function patrol()
		local wpList = mapClone:FindFirstChild("waypoint"):GetChildren()
		local randomNum = math.random(1, #wpList)
		walkTo(wpList[randomNum])
	end


	humanoid.WalkSpeed = normalSpeed
	while true do
		task.wait(0.5)
		local target = findTarget()
		if target and target:FindFirstChild("HumanoidRootPart") and target.Humanoid.Health > 0 then
			turnAndPlayAnimation(target)
			humanoid.WalkSpeed = chaseSpeed
			chaseTarget(target)
		else
			humanoid.WalkSpeed = normalSpeed
			patrol()
		end
	end
end

return module


Several time later…


And this problem how is looking like.

Your NPC is stuttering because of timed pauses in its code, like task.wait(0.1). When your console’s speed changes, these pauses don’t match up, which makes the NPC look like it’s stuttering.

then Ok. it not related with console rate, how can Fix types can you give me to fix time pauses

I noticed something it happens only in he is not lost target and still chasing npc. it corrupting later this happens. Somebody have advice?