Pathfinding AI stutter

I have an NPC which has 2 states such as: Walk Randomly (walk to random waypoints scattered across the map) and Attack (walk to player and shoot a raycast (lazer) at them and kill them on hit).
However after the NPC attacks once, the AI starts to stutter and be slow.

even if you don’t know the solution but see problems in the code, feel free to say!

Walk Randomly Code:

local function walkRandomly()
	CanShoot = true
	foundtarget = nil
	isattacking = false
	--humanoid.WalkSpeed = 10
	local point = points[math.random(1,#points)]
	local target = FindTarget()

	path.Blocked:Connect(function()
		print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
		main()
	end)
	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)
		end
if foundtarget == 1 then path:Destroy() break end
		humanoid.MoveToFinished:Wait()
		FindTarget()
	end
end

Attack Code (Ignore the atrocious tweens and effect code):

local function findPath(target)

	path.Blocked:Connect(function()
		print("DEBUG: PATH BLOCKED (WALKRANDOMLY)")
		main()
	end)
	path:ComputeAsync(humanoidrootpart.Position,target.Position)
	local waypoints = path:GetWaypoints()
	--
task.spawn(function()
if CanShoot then
	CanShoot = false
		--task.spawn(function()
		isattacking = true
		ChargeAnim:Play()
		print('attack')
			humanoidrootpart.Seen.Playing = true
		script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(0)
		script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(0)
		script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(0)
		script.Parent.Electric.PointLight.Brightness = 0
		script.Parent.Electric.PointLight.Range = 0
		script.Parent.Head.Attack2.PlaybackSpeed = .5
		script.Parent.Head.Attack2.Volume = 4
		script.Parent.Head.Attack.Playing = false
		script.Parent.Head.Attack3.Playing = false
		script.Parent.Head.Attack2:Play()
		--
		coroutine.wrap(function()
			script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(1)
			script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(1)
			script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(1)
			task.wait(.5)
			script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(3)
			script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(2.5)
			script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(2.5)
			task.wait(.5)
			script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(5)
			script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(3.5)
			script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(3.5)
			task.wait(.5)
			script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(6)
			script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(4)
			script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(4)
		end)()
		
		game:GetService('TweenService'):Create(script.Parent.Electric.PointLight, TweenInfo.new(1.5), {Brightness = 20}):Play()
		game:GetService('TweenService'):Create(script.Parent.Electric.PointLight, TweenInfo.new(1.5), {Range = 120}):Play()
		game:GetService('TweenService'):Create(script.Parent.Head.Attack2, TweenInfo.new(1.5), {PlaybackSpeed = 2.5}):Play()
		game:GetService('TweenService'):Create(script.Parent.Head.Attack2, TweenInfo.new(1.5), {Volume = 6.5}):Play()
		--humanoid.WalkSpeed = 6.5
		task.wait(2)
		--humanoid.WalkSpeed = 10
		task.spawn(function()
		RaycastSight(target)
		end)
		print('attack')
		script.Parent.Head.Attack:Play()
		script.Parent.Electric.PointLight.Brightness = 35
		script.Parent.Head.Attack3:Play()
		game.ReplicatedStorage.Attack:FireAllClients()
		coroutine.wrap(function()
			game:GetService('TweenService'):Create(game.Lighting.ColorCorrection, TweenInfo.new(1.25, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut, 0, true, 0), {Contrast = .75}):Play()
			game:GetService('TweenService'):Create(game.Lighting.ColorCorrection, TweenInfo.new(1, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut, 0, true, 0), {Brightness = .85}):Play()
			game:GetService('TweenService'):Create(game.Lighting.ColorCorrection, TweenInfo.new(1, Enum.EasingStyle.Bounce, Enum.EasingDirection.InOut, 0, true, 0), {TintColor = Color3.fromRGB(255, 61, 223)}):Play()
			task.wait(5)
			CanShoot = true
			
		end)()
		if RaycastSight(target) == true then
			--task.wait(.5)
			print('hit')
		target.Parent.Humanoid.Health = 0
		end
		ChargeAnim:Stop()
		isattacking = false
		coroutine.wrap(function()
			task.wait(3)
			script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(0)
			script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(0)
			script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(0)
			script.Parent.Electric.PointLight.Brightness = 0
			script.Parent.Electric.PointLight.Range = 0
			script.Parent.Head.Attack2.PlaybackSpeed = .5
			task.spawn(function()
				script.Parent.Head.Attack.Ended:Connect(function()
					script.Parent.Head.Attack.Playing = false
				end)

				script.Parent.Head.Attack2.Playing = false

				script.Parent.Head.Attack3.Ended:Connect(function()
					script.Parent.Head.Attack3.Playing = false
				end)
			end)
		end)()
		--end)
end	
end)
--
	for _, waypoint in ipairs(waypoints) do
		if checkSight(target) == true then
			repeat
				humanoid:MoveTo(target.Position)
				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)")
			main()
			--[[
			coroutine.wrap(function()
				script.Parent.Electric.ChargingEffect.Size = NumberSequence.new(0)
				script.Parent.Electric.ParticleEmitter.Size = NumberSequence.new(0)
				script.Parent.Electric.ParticleEmitter2.Size = NumberSequence.new(0)
				script.Parent.Electric.PointLight.Brightness = 0
				script.Parent.Electric.PointLight.Range = 0
				script.Parent.Head.Attack2.PlaybackSpeed = .5
				task.spawn(function()
					script.Parent.Head.Attack.Ended:Connect(function()
						script.Parent.Head.Attack.Playing = false
					end)

					script.Parent.Head.Attack2.Playing = false

					script.Parent.Head.Attack3.Ended:Connect(function()
						script.Parent.Head.Attack3.Playing = false
					end)
				end)
			end)()
			--]]
			foundtarget = nil
			break
		else
			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

Do humanoid.MoveToFinished:Wait(0). Works like a charm

1 Like

What does that even change

how

1 Like

Instead of doing a task.wait(), it does a task.wait(0) basically (no yield)

1 Like

This is modeled after Roblox’s own code. You will have to adapt the solution presented to fit your needs.

1 Like

Thanks and I will definitely use this, but any specific things you see in my code that should be changed?

The above code should be deleted. It’s not recommended to do what you are doing when events exist for this. The code in the thread that I linked was adapted from here.

1 Like

Then what would I replace it with without breaking the entire script?

For starters, do what I did: Copy the Roblox script and modify it from there. You can make it a full module that you can include in your targeting script. You may have to refactor your code to make it work.

1 Like

So, I do not know the exact issue to why your NPC is stuttering, but I do have a few ideas that might help you.

Try using a different math.random function. I do not know if yours is causing an issue but it’s always good to have a backup or two.
Try using a tween to make your NPC look more smooth.
I have never used the tween service before so I can not help you with it but I have heard that it is good to use.
Try using a different NPC. I do not know if the one you are using is causing an issue, but it’s always good to have a backup.
Try looking into the script and see if you can find any problems. Good luck!

NOTE: I do not know if this is your exact problem but I hope it helps you.