Issue with AI - Stuttering and other issues

Recently been working on a AI System with someone for a game - seems every time we fix it there’s a new problem. I have a general idea on what the issue is and what it could be but I can’t quite put my finger on it. When we put the MoveToFinished:Wait(), it stutters a lot when moving - but when we remove it, the AI gets stuck on certain parts.

image

Feels like we’re doing something wrong here and we’ve been chipping away at it but nothing is conclusive. Any help would be great :grinning: (P.S The code is a bit messy, we’ve been playing around with a quite a few things so there’s a lot of clutter)

--Variables
character = script.Parent.Parent
hum = character.Humanoid
root = character.HumanoidRootPart
ps = game:GetService("PathfindingService")
animFolder = character.AnimFolder
attackAnimation = animFolder.Attack
onCooldown = false
attackCD = 1
attackDmg = 15
attackReach = 5
chaseDistance = 80
isRoaming = false
isChasing = false
character.PrimaryPart:SetNetworkOwner(nil)
local newWaypointIndex
local waypoints

wait(3)

--Pathfinding
function Roam()
	isRoaming = true
	wait(1)
	--print("Roaming")
	--print(script:GetAttribute("Target"))
	--Variables
	local xRand = nil
	local zRand = nil
	local n = math.random(1,#workspace.Model.Floors:GetChildren())
	----print(n)
	
	for i, v in pairs(workspace.Model.Floors:GetChildren()) do
		if n == i then
		xRand = v.Position.X
		zRand = v.Position.Z
		break
		end
	end
	local goal = Vector3.new(xRand,0,zRand)

	
	local path = ps:CreatePath()
	path:ComputeAsync(root.Position,goal)
	local waypoints = path:GetWaypoints()
	local n = math.random(1,1000)
	--Move the character to the goal
	if path.Status == Enum.PathStatus.Success then
		for _, waypoint in ipairs(waypoints) do
			local part = game.ReplicatedStorage.Part:Clone()
			part.Parent = workspace
			part.Position = waypoint.Position
			part.Name = n
			if #waypoints == _ then
				print("Roam complete")
				isRoaming = false
				break
			else
			if waypoint.Action == Enum.PathWaypointAction.Jump then
				hum.Jump = true
			end
			
			
			
			hum:MoveTo(waypoint.Position)
			
			local timeOut = hum.MoveToFinished:Wait()
			
			script:SetAttribute("Target","")
			
			if not timeOut then --AI is stuck
				hum.Jump = true
				Roam()
			end
		end
	end
	else --Path has failed
		wait(1)
		Roam()
		end
	end
pathTab = {}
function WalkTo(target)
	isChasing = true
--	print(target)
	--Variables
--	wait()
	local path = ps:CreatePath({AgentHeight = 6, Costs = {DangerZone = math.huge}})
	table.insert(pathTab,path)
	local success, errorMessage = pcall(function()
		path:ComputeAsync(root.Position, target.Position)
	end)
	
	--print(success)
	--Move character to the goal
	local n = math.random(1,1000)
if success and path.Status == Enum.PathStatus.Success then
		waypoints = path:GetWaypoints()
		path.Blocked:Connect(function(bwIndex)
			print(bwIndex)
			--if bwIndex >= newWaypointIndex then
			--	blockedConnection:Disconnect()
			--  WalkTo(target)
			--end
		end)
		path.Unblocked:Connect(function(ubwIndex)
			print(ubwIndex)
		end)
		--print(pathTab)
		
		for _, waypoint in pairs(waypoints) do
			print(_,"/",#waypoints)
			if _ == #waypoints then
				isChasing = false
				WalkTo(target)
				return
			end
			if waypoint.Action == Enum.PathWaypointAction.Jump and (root.Position - target.Position).magnitude > 20 then
				hum.Jump = true
			end
			
			--if n == 1 then
			local part = game.ReplicatedStorage.Part:Clone()
			part.Parent = workspace
			part.Position = waypoint.Position
			part.Name = n
			--	end
			hum:MoveTo(waypoint.Position)
			--local timeOut
			--    timeOut = hum.MoveToFinished:Connect(function(tO)
			--		if not tO then --AI is stuck
			--			wait()
			--		    hum.Jump = true
			--			WalkTo(target)
			--		end
			--	end)
			local timeOut = hum.MoveToFinished:Wait()

			if not timeOut then --AI is stuck
				hum.Jump = true
				WalkTo(target)
			end
			
			if CheckSight(target) then
				repeat
					if (root.Position - target.Position).magnitude < attackReach then
						Attack(target)
					end
					if target == nil then
						isChasing = false
					    break
					elseif target.Parent == nil then
						isChasing = false
						break
					end
					local sight = CheckSight(target)
					--print(sight)
				until sight ~= nil or hum.Health <= 1 or target.Parent.Humanoid.Health <= 1
			end
			
			if (root.Position - waypoints[1].Position).magnitude > 20 then
				--WalkTo(target)
				CheckSight(target)
				isChasing = false
				break
			end
		end
	else
		if (root.Position - target.Position).magnitude < attackReach + 2 and target.Parent.Humanoid.Health > 0 then
			--wait(0.5)
			--hum.Jump = true
			Attack(target)
		else
			print('Not success')
			isChasing = false
			Main()
		end
	end
	
end

--Attacking
function Attack(target)
	local atk = coroutine.create(function()
	if (root.Position - target.Position).magnitude < attackReach and not onCooldown and target.Parent.Humanoid.Health > 0 then
        --Set the cooldown
		onCooldown = true
		
		--Variables
		local atkAnim = hum:LoadAnimation(attackAnimation)
		
		--Attack the player
		
			atkAnim:Play()
			target.Parent.Humanoid.Health -= attackDmg
			if target.Parent.Humanoid.Health <= 0 then
				script:SetAttribute("Target", nil)
			end
			wait(attackCD)
			onCooldown = false
		end	
	end)
	coroutine.resume(atk)
end

--Finding targets
function RandomPlayer()
	--Variables
	local players = game.Players:GetPlayers()
	local target = nil
	local playerPos = {}
	
	for i, v in pairs(players) do
		local character = v.Character
		local hrp = character:WaitForChild("HumanoidRootPart")
		local tabInfo = {v,(hrp.Position - root.Position).magnitude}
		
		table.insert(playerPos, tabInfo)
	end
	for i, v in pairs(playerPos) do
		local holdTarget = nil
		if not holdTarget then
			--target = v
			holdTarget = v
			--print(holdTarget)
			if v[2] >= holdTarget[2] and v[2] <= chaseDistance then
				target = v
				holdTarget = v
			end
		end
	end
	--print(target)
	if target then
		script:SetAttribute("Target",target[1].Character.Name)
		return target[1].Character:WaitForChild("HumanoidRootPart")
	end
	end

function CheckSight(target)
	--Variables
	--local ray = Ray.new(root.Position, (target.Position - root.Position).Unit * 360)
	--local hit, position = workspace:FindPartOnRayWithIgnoreList(ray,{character})
	
	----Check if a player is in its sight
	--if hit then
	--	if hit:IsDescendantOf(target.Parent) and math.abs(hit.Position.Y - root.Position.Y) < 10 then
	--		return true
	--	end
	--end
	--return false
	return true
end

function FindTarget()
	
	--Variables
	local distance = 100
	local target = nil
	local potentialTargets = {}
	local seeTargets = {}
	
	--Find potential targets
	for i,v in pairs(workspace:GetChildren()) do
		local human = v:FindFirstChild("Humanoid")
		local hrp = v:FindFirstChild("HumanoidRootPart")
		local isAI = v:FindFirstChild("IsAI")
		if human and hrp and not isAI then
			if (root.Position - hrp.Position).magnitude < distance and human.Health > 0 then
				table.insert(potentialTargets,hrp)
			end
		end
	end
	
    --Find players that it can see
	if #potentialTargets > 0 then
		for i,v in pairs(potentialTargets) do
			if CheckSight(v) then
				table.insert(seeTargets,v)
			elseif #seeTargets == 0 and (root.Position - v.Position).magnitude < distance then
				target = v
			end
		end
	end
	
	--Set the target to the closest player that it can see
	if #seeTargets > 0 then
		for i,v in pairs(seeTargets) do
			if (root.Position - v.Position).magnitude < distance then
				target = v
				script:SetAttribute("Target",v.Parent.Name)
				return target
			end
		end
	end
end

--Main system
function Main()
	--Variables
	local target = RandomPlayer()
	
	--If there is a target, then walk to it
	if target and script:GetAttribute("Target") == target.Parent.Name and isChasing == false then
		--print(target,script:GetAttribute("Target"))
		WalkTo(target)
		isChasing = false
		print("Chasing")
	else
		if isRoaming == false then
		Roam()
		--print("Roaming")
		end
	end
end

script.AttributeChanged:Connect(function(attribute)
	if attribute == "Target" then
		if script:GetAttribute("Target") == nil then
			Main()
		end
	end
end)

while wait() do
	if hum.Health <= 0 then
		break
	end
	Main()
end
1 Like

Have you tried replacing .MoveToFinished:Wait() with a custom loop?

repeat
    task.wait()
    local Distance = (HumanoidRootPart.Position - Waypoint.Position).Magnitude
until Distance < 5

Unfortunately this doesn’t work. I think the issue is there’s just too much going on with the code - causing some weird delay. And then on top of the MoveToFinished:Wait() it is just causing stuttering. If we get rid of the MoveToFinished line it is no longer able to go around corners properly. Thanks for the help though!