How it works is It’ll detect if there’s any possible Target or not in the field, if not it will move to the Base/Goal to attack it.
However the issue is if once i deployed one of my Units/Target the AI will act confused as he is about to move to the target but then refused and went for the Base instead.
Any possible way i could fix this?
local YIELDING = false
local MAX_RETRIES = 10
local RETRY_COOLDOWN = 0.7
local reachedConnection
local pathBlockedConnection
local path = pathfindingservice:CreatePath({
AgentRadius = 3,
WaypointSpacing = 4,
Costs = {
Barrier = math.huge,
HutWalls = math.huge,
}
})
local function ComputePath(targetTorso,yield)
local RETRY_NUM = 0
local success, errorMessage
repeat
RETRY_NUM += 1
success, errorMessage = pcall(path.ComputeAsync, path, enemy:WaitForChild("HumanoidRootPart").Position, targetTorso.Position)
if not success then
task.wait(RETRY_COOLDOWN)
end
until success == true or RETRY_NUM > MAX_RETRIES
if success then
if path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
local currentWaypointIndex = 2
if not reachedConnection then
reachedConnection = zombiehumanoid.MoveToFinished:Connect(function(reached)
if reached and currentWaypointIndex < #waypoints then
currentWaypointIndex += 1
zombiehumanoid:MoveTo(waypoints[currentWaypointIndex].Position)
else
reachedConnection:Disconnect()
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
YIELDING = false
end
end)
end
pathBlockedConnection = path.Blocked:Connect(function(waypointNumber)
if waypointNumber > currentWaypointIndex then
reachedConnection:Disconnect()
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
ComputePath(targetTorso.Position, true)
end
end)
if targetTorso == nil then
reachedConnection:Disconnect()
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
ComputePath(targetTorso.Position, true)
end
zombiehumanoid:MoveTo(waypoints[currentWaypointIndex].Position)
if yield then
YIELDING = true
repeat
task.wait()
until YIELDING == false
end
else
return
end
end
end
coroutine.wrap(function()
while task.wait() do
local torso = findtarget()
if torso then
ComputePath(torso,true)
else
ComputePath(Map:WaitForChild("Goal"),false)
end
end
end)()
When you say he is confused, it seems like you are the one confused. I would add some print statements around to see why and when the NPC recalculates it’s path back to the base.
Pathfinding is rather simple. The problem with making an ai is that it’s not just pathfinding. However I have a script here you can use. It’s already old as in I made a new version for one of my games but still works.
All around. Whenever you are computing a new path and following it, print what the target is. Adding a few simple print statements is a pretty quick way to find and solve bugs, and you’ll get better at knowing where and what to print as you code more and run into more bugs.
So add a getpropertychangedsignal and inside of it see if the humanoid is dead, if so recalculate the nearest player. You didn’t show your retargetting code, but I’m assuming you aren’t checking the humanoid’s health before considering them.
I recalculated the path now but it keeps running on the last dead body position. Was it because of the while loop or something?
Updated Code:
local YIELDING = false
local MAX_RETRIES = 10
local RETRY_COOLDOWN = 0.7
local reachedConnection
local pathBlockedConnection
local path = pathfindingservice:CreatePath({
AgentRadius = 3,
WaypointSpacing = 8,
Costs = {
Barrier = math.huge,
HutWalls = math.huge,
}
})
local FoundNewTarget = false
local MoveIsFinished = false
local movingToGoal = false
local movingToEnemy = false
local pathfindingTarget = enemy:WaitForChild("PathfindingTarget")
local HRP = enemy:WaitForChild("HumanoidRootPart")
local function ComputePath(targetTorso,yield)
local RETRY_NUM = 0
local success, errorMessage
pathfindingTarget.Value = targetTorso
repeat
RETRY_NUM += 1
success, errorMessage = pcall(path.ComputeAsync, path, HRP.Position, targetTorso.Position)
if not success then
task.wait(RETRY_COOLDOWN)
end
until success == true or RETRY_NUM > MAX_RETRIES
if success then
if path.Status == Enum.PathStatus.Success then
local waypoints = path:GetWaypoints()
local currentWaypointIndex = 2
if not reachedConnection then
reachedConnection = zombiehumanoid.MoveToFinished:Connect(function(reached)
if reached and currentWaypointIndex < #waypoints then
currentWaypointIndex += 1
zombiehumanoid:MoveTo(waypoints[currentWaypointIndex].Position)
else
reachedConnection:Disconnect()
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
YIELDING = false
MoveIsFinished = true
end
end)
end
pathBlockedConnection = path.Blocked:Connect(function(waypointNumber)
if waypointNumber > currentWaypointIndex then -- blocked path is ahead of the bot
reachedConnection:Disconnect() -- disconnect these events to prevent memory leaks
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
ComputePath(targetTorso, false) -- compute and cycle new path
end
end)
local targetHumanoid = targetTorso.Parent:FindFirstChild("Humanoid")
if targetHumanoid then
targetHumanoid:GetPropertyChangedSignal("Health"):Connect(function()
if targetTorso and targetTorso.Parent:WaitForChild("Humanoid").Health <= 0 then
ComputePath(targetTorso,false)
end
end)
end
if targetTorso == nil then
reachedConnection:Disconnect()
pathBlockedConnection:Disconnect()
reachedConnection = nil
pathBlockedConnection = nil
ComputePath(targetTorso, false)
end
zombiehumanoid:MoveTo(waypoints[currentWaypointIndex].Position)
if yield then
YIELDING = true
repeat
task.wait()
until YIELDING == false
end
else
return
end
end
end
while task.wait() do
local torso = findtarget()
if torso and torso.Parent:FindFirstChild("Humanoid").Health > 0 then
movingToGoal = true
movingToEnemy = true
if enemy:FindFirstChild("Boss") then
zombiehumanoid:MoveTo(torso.Position)
else
if movingToEnemy and not MoveIsFinished then
if torso.Parent.Humanoid.Health > 0 then
movingToGoal = false
ComputePath(torso,false)
end
end
end
else
if enemy:FindFirstChild("Boss") then
zombiehumanoid:MoveTo(workspace.Map.Goal.Position)
else
if movingToGoal == false then
ComputePath(Map:WaitForChild("Goal"),false)
movingToGoal = true
end
end
end
You still haven’t sent the find target code, so I can’t know if you are checking the target humanoid’s health while you are finding a target. Add some print statements throughout the while loop to see what is going on.
When you are looping through humanoids, ensure they are alive before considering them. You should also print the status in a few places in the while loop at the bottom so you can keep track what state the NPC is at.
If you could send your findTarget code or do what I’ve said above, we could fix your code faster. In the segment you sent, you only check the health once, when it should be checked multiple times in your find target function, so it won’t route to a dead body and will always go to the nearest alive player.
Neat, I was roaming around here
here’s the findtarget code anyways
local function findtarget()
local agrodistance = math.huge
local target = nil
for i, v in pairs(game.Workspace.Unit:GetChildren()) do
local human = v:FindFirstChild("Humanoid")
local torso = v:FindFirstChild("Torso")
if v:FindFirstChild("Undetectable") then
if not enemyStats.DetectHidden then
continue
end
end
if human and torso and v ~= script.Parent and human.Health > 0 then
if (zombietorso.Position - torso.Position).magnitude < agrodistance then
agrodistance = (zombietorso.Position - torso.Position).magnitude
target = torso
end
end
end
return target
end
Ok, so you do check that the humanoid is alive when searching through in this method. The next step is to put some print statements around your while loop to see when he changes target, and what his status is.
What his destination is, who he is targetting, just get an idea of what he is doing so you know what is going on and can tell me what the issue is. You should be able to filter it down to a single line.