How to stop AI stutter?

how to make it not stop then go. and not like do this odd thing where it like goes after player ONLY if they move:

-- Variables --

local waypointFolder = Instance.new("Folder")
waypointFolder.Name = "Waypoints"
waypointFolder.Parent = script.Parent

local Pathfinding = game:GetService("PathfindingService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ConfigModule = require(game.ServerStorage.ConfigModule)

local path = Pathfinding:CreatePath({
	AgentHeight = 6;
	AgentRadius = 3;
	AgentCanJump = false;

	Costs = {
		Water = 100;
		DangerZone = math.huge
	}
})

local Character = script.Parent
local humanoid = Character:WaitForChild("Humanoid")
local humanoidRootPart = Character:WaitForChild("HumanoidRootPart")
local aiPaths = script.Parent.Parent:FindFirstChild("AiPaths")

local waypoints
local nextWaypointIndex
local reachedConnection
local blockedConnection
local lastTargetPosition = nil
local isChasing = false
local isRoaming = false
local currentRoamPoint = nil

local function findTarget()
	local maxDistance = 60
	local nearestTarget = nil

	for _, player in pairs(Players:GetPlayers()) do
		if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
			local target = player.Character
			local targetRoot = target.HumanoidRootPart
			local distance = (humanoidRootPart.Position - targetRoot.Position).Magnitude

			if distance < maxDistance then
				nearestTarget = target
				maxDistance = distance
			end

			-- **Kill instantly if too close**
			if distance < 3 then
				target.Humanoid.Health = 0
				return nil -- Stop chasing if the player is dead
			end
		end
	end

	return nearestTarget
end

local function followPath(destination, onComplete)
	local success, errorMessage = pcall(function()
		path:ComputeAsync(humanoidRootPart.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		waypoints = path:GetWaypoints()
		nextWaypointIndex = 2

		if blockedConnection then blockedConnection:Disconnect() end
		if reachedConnection then reachedConnection:Disconnect() end

		if ConfigModule.BotDebuger then
			waypointFolder:ClearAllChildren()

			local previousPart
			for i, waypoint in ipairs(waypoints) do
				local part = Instance.new("Part")
				part.Size = Vector3.new(1, 1, 1)
				part.Position = waypoint.Position
				part.Transparency = 1
				part.Anchored = true
				part.CanCollide = false
				part.BrickColor = BrickColor.new("Bright blue")
				part.Parent = waypointFolder

				if previousPart then
					local attachment0 = Instance.new("Attachment", previousPart)
					local attachment1 = Instance.new("Attachment", part)

					local beam = Instance.new("Beam")
					beam.Attachment0 = attachment0
					beam.Attachment1 = attachment1
					beam.Width0 = 0.2
					beam.Width1 = 0.2
					beam.Color = ColorSequence.new(Color3.new(0, 0, 1))
					beam.Parent = previousPart
				end

				previousPart = part
			end
		end

		blockedConnection = path.Blocked:Connect(function(blockedWayPointIndex)
			if blockedWayPointIndex >= nextWaypointIndex then
				blockedConnection:Disconnect()
				followPath(destination, onComplete)
			end
		end)

		reachedConnection = humanoid.MoveToFinished:Connect(function(reached)
			if reached and nextWaypointIndex < #waypoints then
				nextWaypointIndex += 1
				humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
			else
				reachedConnection:Disconnect()
				blockedConnection:Disconnect()
				if onComplete then onComplete() end
			end
		end)

		humanoid:MoveTo(waypoints[nextWaypointIndex].Position)
	else
		warn("Path not computed!", errorMessage)
	end
end

local function roam()
	if aiPaths and not isChasing and not isRoaming then
		local points = aiPaths:GetChildren()
		if #points > 0 then
			isRoaming = true
			local randomPoint = points[math.random(1, #points)]
			if randomPoint:IsA("Part") then
				currentRoamPoint = randomPoint.Position
				followPath(currentRoamPoint, function()
					isRoaming = false -- **Only pick a new roam point after fully reaching the current one**
					currentRoamPoint = nil
				end)
			else
				isRoaming = false
			end
		end
	end
end

while wait(0.2) do -- **Faster updates**
	local target = findTarget()

	if target then
		isChasing = true
		isRoaming = false -- **Stop roaming instantly**
		currentRoamPoint = nil -- **Clear roam target**
		local targetRoot = target.HumanoidRootPart

		-- **Update path only if the target moves more than 3 studs**
		if not lastTargetPosition or (targetRoot.Position - lastTargetPosition).Magnitude > 3 then
			lastTargetPosition = targetRoot.Position
			followPath(targetRoot.Position)
		end
	else
		isChasing = false
		if not isRoaming and not currentRoamPoint then
			roam()
		end
	end
end

Trying using RunService.Heartbeat instead of a while loop.

local RunService = game:GetService("RunService")

RunService.Heartbeat:Connect(function(dt: number)
	local target = findTarget()

	if target then
		isChasing = true
		isRoaming = false -- **Stop roaming instantly**
		currentRoamPoint = nil -- **Clear roam target**
		local targetRoot = target.HumanoidRootPart

		-- **Update path only if the target moves more than 3 studs**
		if not lastTargetPosition or (targetRoot.Position - lastTargetPosition).Magnitude > 3 then
			lastTargetPosition = targetRoot.Position
			followPath(targetRoot.Position)
		end
	else
		isChasing = false
		if not isRoaming and not currentRoamPoint then
			roam()
		end
	end
end)

or RunService:BindToRenderStep() if you want to instantly stop checking by using RunService:UnbindFromRenderStep()

Can you show a video of what you mean?

Using RunService is a good option but it’s worth noting that if OP decides to use a lot of NPCs then it can cause a lot of latency issues.

Set the npc rootpart’s network owner to Server by
humanoidRootPart:SetNetworkOwner(nil)
You can learn about network ownership here …

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