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.

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
(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