Hi there.
I’m currently working on an improved AI system for my game and everything’s been working fine except for one thing, pathfinding. So basically I have an enemy that can attack you, run towards you, and pathfind to you if he can’t see you. The problem with pathfinding with this is that it works for some time (30 seconds maybe) and then the enemy for some reason just can’t pathfind more and gets stuck in one place. There is a video down below if my explanation sounds confusing.
Here is the code for the enemy:
local runService = game:GetService("RunService")
wait(4)
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
local root = char:WaitForChild("HumanoidRootPart")
local target = nil
local distance = nil
local attackDistance = 2
local maxChasingDistance = 50
char.PrimaryPart:SetNetworkOwner(nil)
hum.BreakJointsOnDeath = false
local currentState = "Patroling"
local pathFindFails = 0
local pathParams = {
["AgentRadius"] = 4,
["AgentHeight"] = 5.11,
["AgentCanJump"] = true
}
local function FindTarget()
local closestDist = 0
local closestPlayer = nil
local firstIteration = true
for i,player in ipairs(game.Players:GetChildren()) do
if firstIteration then
if char and char:FindFirstChild("Humanoid") and player and player.Character and player.Character.Humanoid.Health > 0 then
closestDist = (player.Character.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude
closestPlayer = player
firstIteration = false
end
else
if char and char:FindFirstChild("Humanoid") and player and player.Character and player.Character.Humanoid.Health > 0 then
if(player.Character.HumanoidRootPart.Position - char.HumanoidRootPart.Position).Magnitude < closestDist then
closestDist = (char.HumanoidRootPart.Position - player.Character.HumanoidRootPart.Position ).Magnitude
closestPlayer = player
end
end
end
end
if closestPlayer and (char.HumanoidRootPart.Position - closestPlayer.Character.HumanoidRootPart.Position).Magnitude < 100 then
return closestPlayer.Character
else
return nil
end
end
local function CheckSight(target)
local rayCastParams = RaycastParams.new()
rayCastParams.FilterType = Enum.RaycastFilterType.Blacklist
rayCastParams.FilterDescendantsInstances = {char}
local direction = (target.HumanoidRootPart.Position - root.Position).Unit * 200
local rayCastResult = workspace:Raycast(char.HumanoidRootPart.Position, direction, rayCastParams)
if rayCastResult then
if rayCastResult.Instance:IsDescendantOf(target) then
return true
end
else
return false
end
end
local function GetUnstuck()
hum:MoveTo(Vector3.new(root.Position.X + math.random(-1, 1), 0, root.Position.Z + math.random(-1, 1)))
wait(1)
end
local canPathFind = true
--Pathfinding function
local function PathToTarget(target)
local path = game:GetService("PathfindingService"):CreatePath(pathParams)
path:ComputeAsync(root.Position, target.HumanoidRootPart.Position)
local wayPoints = path:GetWaypoints()
if path.Status == Enum.PathStatus.Success then
for i,v in ipairs(wayPoints) do
if v.Action == Enum.PathWaypointAction.Jump then
hum.Jump = true
else
hum:MoveTo(v.Position)
local coro = coroutine.wrap(function()
wait(0.5)
if hum.WalkToPoint.Y > root.Position.Y then
hum.Jump = true
end
end)
coro()
hum.MoveToFinished:Wait()
end
if (CheckSight(target) == false) and math.abs(root.Position.Y - target.HumanoidRootPart.Position.Y) > 1.5 then
break
elseif (target.HumanoidRootPart.Position - wayPoints[#wayPoints].Position).Magnitude > 30 then
break
elseif math.abs(root.Position.Y - v.Position.Y) > 10 then
break
elseif (root.Position - v.Position).Magnitude > 10 then
break
end
local timeOut = hum.MoveToFinished:Wait()
if not timeOut then
GetUnstuck()
break
end
end
else
print("Failed to generate a path")
GetUnstuck()
pathFindFails = pathFindFails + 1
if pathFindFails > 10 then
GetUnstuck()
pathFindFails = 0
end
end
end
----------
-- State functions
local canPatrol = true
local function Patroling()
if target and CheckSight(target) then
currentState = "Running"
end
hum.WalkSpeed = 16
if canPatrol then
canPatrol = false
hum:MoveTo(root.Position + Vector3.new(math.random(-20, 20), 0, math.random(-20, 20)))
hum.MoveToFinished:Wait()
wait(math.random(1, 4))
canPatrol = true
end
end
local debounce = true
local function Running()
if target then
if distance < maxChasingDistance and distance > attackDistance then
hum.WalkSpeed = 16
if CheckSight(target) then
hum:MoveTo(target.HumanoidRootPart.Position)
else
PathToTarget(target)
end
elseif distance <= attackDistance then
currentState = "Attacking"
end
end
end
local canAttack = true
local function Attacking()
hum.WalkSpeed = 0
if canAttack then
if target then
local targetHum = target.Humanoid
if targetHum then
canAttack = false
targetHum:TakeDamage(5)
wait(0.5)
canAttack = true
end
else
currentState = "Patroling"
end
end
if distance > attackDistance then
currentState = "Running"
end
end
-----------
local lastState = nil
-- Logic loop
runService.Heartbeat:Connect(function()
if target == nil then
currentState = "Patroling"
target = FindTarget()
else
distance = (target.HumanoidRootPart.Position - root.Position).Magnitude
if target.Humanoid.Health < 1 or distance > maxChasingDistance then
target = nil
end
if distance < attackDistance + 0.1 then
currentState = "Attacking"
end
end
if currentState == "Patroling" then
Patroling()
elseif currentState == "Running" then
Running()
elseif currentState == "Attacking" then
Attacking()
end
end)
Also if you wanna try this yourself, just put this script in some character, make sure there are no anchored parts and you are good to go.
And here is a video:
I’ve spent almost 4 hours trying to find the issue but didn’t, and at this point, I have no idea, so any suggestion on what might be the problem would be appreciated.