the problem is your script was just here where you are making a loop of creating many threads and each thread try to move the zombie to a different target or maybe to the same targets but with different paths
you made a thread for the loop to make run forever 0.5 sec is not enough for the zombie to finish it is path
i tried with 1 second but still it gives the same result
try a huge number like 10 or 20 sec , you can make more dynamic by not updating a zombie if it is already have a target
Is there any delay in the MoveToFinished:Wait() function? Whenever the zombie reached my old position, it stops for a split of seconds and then continue moving to my newest position
I tried 10 and 20, they just go for my position and then stood for 20 seconds then continue…
EDIT: disable the humanoid move to finished somehow fix everything, but no smart movement
yes there is some delay with MoveToFinish event it yields until the humanoid reach the point that set by MoveTo function after that it waits until the humanoid speed to be equal 0
just make 1 thread for every zombie and in this thread make a while loop that move the zombie rather than making a new thread every time
what about the waypoints, they seem still wanted to change route everytime they move
Here is the new script
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local stunmodule = require(game.ReplicatedStorage.EffectsDebuff)
local calculator = require(game.ReplicatedStorage.DamageCalculator)
local ZOMBIE_TAG = "zombie"
local TARGET_FIND_RADIUS = 150
local PATH_REFRESH_INTERVAL = .5
local ATTACK_RADIUS = 5.5
local DAMAGE = 4.5
local ATTACK_COOLDOWN = 0.55
local zombies = {}
local function getNearestTarget(zombie)
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return nil end
local closestTarget
local closestDistance = math.huge
for _, model in ipairs(workspace:GetChildren()) do
if model:IsA("Model") and model ~= zombie and model.Name ~= zombie.Name then
local humanoid = model:FindFirstChildOfClass("Humanoid")
local targetRoot = model:FindFirstChild("HumanoidRootPart")
if humanoid and humanoid.Health > 0 and targetRoot then
local distance = (root.Position - targetRoot.Position).Magnitude
if distance < closestDistance and distance <= TARGET_FIND_RADIUS then
closestTarget = targetRoot
closestDistance = distance
end
end
end
end
return closestTarget
end
local function attackZombie(zombie, target)
if zombie:GetAttribute("CanAttack", true) then
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return end
local attackAnim = zombie.HumanoidRootPart:FindFirstChild("punch")
local attackSound = zombie.HumanoidRootPart:FindFirstChild("hit")
local attacktrack = zombie.Humanoid:LoadAnimation(attackAnim)
local targetCharacter = target.Parent
local victimstats = targetCharacter:FindFirstChild("Stats")
local victimdefend = (victimstats and victimstats:FindFirstChild("Defensive")) and victimstats.Defensive.Value
local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
local isBlocking = targetCharacter:GetAttribute("isBlocking")
if isBlocking then
local targetHumanoidRoot = targetCharacter:FindFirstChild("HumanoidRootPart")
if targetHumanoidRoot then
local directionToTarget = (targetHumanoidRoot.Position - root.Position).unit
local dotProduct = directionToTarget:Dot(targetHumanoidRoot.CFrame.LookVector)
if dotProduct > 0 then
return
end
end
end
local damage = calculator:Calculate(DAMAGE,1,victimdefend)
if (root.Position - target.Position).Magnitude < ATTACK_RADIUS then
if attacktrack then
attacktrack:Play()
end
if humanoid and humanoid.Health > 0 and zombie.Humanoid.Health > 0 and humanoid ~= zombie.Humanoid then
humanoid:TakeDamage(damage)
stunmodule:Stun(targetCharacter, 10, 0.85)
if attackSound then
attackSound:Play()
end
end
end
end
end
local function updateZombie(zombie)
local humanoid = zombie:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
local target = getNearestTarget(zombie)
if target then
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return end
local path = PathfindingService:CreatePath()
local success, errorMessage = pcall(function()
path:ComputeAsync(root.Position, target.Position)
if path.Status == Enum.PathStatus.Success then
for _, waypoint in ipairs(path:GetWaypoints()) do
humanoid:MoveTo(waypoint.Position)
--humanoid.MoveToFinished:Wait()
if waypoint.Action == Enum.PathWaypointAction.Jump then
humanoid.Jump = true
end
end
end
end)
humanoid:MoveTo(target.Position)
--humanoid.MoveToFinished:Wait()
if not success then
warn("Pathfinding failed:", errorMessage)
end
local lastAttackTime = zombie:GetAttribute("LastAttackTime") or 0
if os.clock() - lastAttackTime >= ATTACK_COOLDOWN then
zombie:SetAttribute("LastAttackTime", os.clock())
attackZombie(zombie, target)
end
end
end
RunService.Heartbeat:Connect(function()
for zombie, lastUpdate in pairs(zombies) do
if zombie.Parent and os.clock() - lastUpdate >= PATH_REFRESH_INTERVAL then
zombies[zombie] = os.clock()
updateZombie(zombie)
elseif not zombie.Parent then
zombies[zombie] = nil
end
end
end)
CollectionService:GetInstanceAddedSignal(ZOMBIE_TAG):Connect(function(zombie)
zombies[zombie] = os.clock()
zombie:SetAttribute("LastAttackTime", 0)
for _, v in pairs(zombie:GetDescendants()) do
if v:IsA("BasePart") and v:CanSetNetworkOwnership() then
v:SetNetworkOwner(nil)
end
end
end)
for _, zombie in ipairs(CollectionService:GetTagged(ZOMBIE_TAG)) do
zombies[zombie] = os.clock()
zombie:SetAttribute("LastAttackTime", 0)
for _, v in pairs(zombie:GetDescendants()) do
if v:IsA("BasePart") and v:CanSetNetworkOwnership() then
v:SetNetworkOwner(nil)
end
end
end
this is why you should make 1 thread that control 1 zombie , in that thread you can make a check if the last waypoint position is equal to the target position or not if not make a new path
i am gonna test the script by my self right now and i will try to fix the issue
Thanks, i will wait for the result
try this one : -
local CollectionService = game:GetService("CollectionService")
local RunService = game:GetService("RunService")
local PathfindingService = game:GetService("PathfindingService")
local stunmodule = require(game.ReplicatedStorage.EffectsDebuff)
local calculator = require(game.ReplicatedStorage.DamageCalculator)
local ZOMBIE_TAG = "zombie"
local TARGET_FIND_RADIUS = 150
local PATH_REFRESH_INTERVAL = .5
local ATTACK_RADIUS = 5.5
local DAMAGE = 4.5
local ATTACK_COOLDOWN = 0.55
local zombies = {}
local function getNearestTarget(zombie)
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return nil end
local closestTarget
local closestDistance = math.huge
for _, model in ipairs(workspace:GetChildren()) do
if model:IsA("Model") and model ~= zombie and model.Name ~= zombie.Name then
local humanoid = model:FindFirstChildOfClass("Humanoid")
local targetRoot = model:FindFirstChild("HumanoidRootPart")
if humanoid and humanoid.Health > 0 and targetRoot then
local distance = (root.Position - targetRoot.Position).Magnitude
if distance < closestDistance and distance <= TARGET_FIND_RADIUS then
closestTarget = targetRoot
closestDistance = distance
end
end
end
task.wait()
end
return closestTarget
end
local function attackZombie(zombie, target)
if zombie:GetAttribute("CanAttack", true) then
local root = zombie:FindFirstChild("HumanoidRootPart")
if not root then return end
local attackAnim = zombie.HumanoidRootPart:FindFirstChild("punch")
local attackSound = zombie.HumanoidRootPart:FindFirstChild("hit")
local attacktrack = zombie.Humanoid:LoadAnimation(attackAnim)
local targetCharacter = target.Parent
local victimstats = targetCharacter:FindFirstChild("Stats")
local victimdefend = (victimstats and victimstats:FindFirstChild("Defensive")) and victimstats.Defensive.Value
local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
local isBlocking = targetCharacter:GetAttribute("isBlocking")
if isBlocking then
local targetHumanoidRoot = targetCharacter:FindFirstChild("HumanoidRootPart")
if targetHumanoidRoot then
local directionToTarget = (targetHumanoidRoot.Position - root.Position).unit
local dotProduct = directionToTarget:Dot(targetHumanoidRoot.CFrame.LookVector)
if dotProduct > 0 then
return
end
end
end
local damage = calculator:Calculate(DAMAGE,1,victimdefend)
if (root.Position - target.Position).Magnitude < ATTACK_RADIUS then
if attacktrack then
attacktrack:Play()
end
if humanoid and humanoid.Health > 0 and zombie.Humanoid.Health > 0 and humanoid ~= zombie.Humanoid then
humanoid:TakeDamage(damage)
stunmodule:Stun(targetCharacter, 10, 0.85)
if attackSound then
attackSound:Play()
end
end
end
end
end
local function updateZombie(zombie)
local humanoid:Humanoid = zombie:FindFirstChildOfClass("Humanoid")
while true do
if not humanoid or humanoid.Health < 1 then
break
end
local target = getNearestTarget(zombie)
if target then
local root = zombie:FindFirstChild("HumanoidRootPart")
if root then
local path = PathfindingService:CreatePath({
["AgentRadius"] = 4,
["AgentHeight"] = 6,
["AgentCanJump"] = true,
["AgentCanClimb"] = true,
["WaypointSpacing"] = 4,
})
local success, errorMessage = pcall(function()
path:ComputeAsync(root.Position, target.Position)
end)
if not success then
warn("Pathfinding failed:", errorMessage)
elseif path.Status == Enum.PathStatus.Success then
local WayPoints = path:GetWaypoints()
for _, waypoint in pairs(WayPoints) do
humanoid:MoveTo(waypoint.Position)
if waypoint.Action == Enum.PathWaypointAction.Jump and humanoid:GetState() ~= Enum.HumanoidStateType.Jumping and humanoid:GetState() ~= Enum.HumanoidStateType.Freefall then
humanoid.Jump = true
humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
end
end
end
local Distance = (root.Position - target.Position).Magnitude
local lastAttackTime = zombie:GetAttribute("LastAttackTime") or 0
if os.clock() - lastAttackTime >= ATTACK_COOLDOWN and Distance < 10 then
zombie:SetAttribute("LastAttackTime", os.clock())
attackZombie(zombie, target)
end
end
end
end
end
CollectionService:GetInstanceAddedSignal(ZOMBIE_TAG):Connect(function(zombie)
zombies[zombie] = os.clock()
zombie:SetAttribute("LastAttackTime", 0)
for _, v in pairs(zombie:GetDescendants()) do
if v:IsA("BasePart") and v:CanSetNetworkOwnership() then
v:SetNetworkOwner(nil)
end
end
task.spawn(updateZombie , zombie)
end)
for _, zombie in ipairs(CollectionService:GetTagged(ZOMBIE_TAG)) do
zombies[zombie] = os.clock()
zombie:SetAttribute("LastAttackTime", 0)
for _, v in pairs(zombie:GetDescendants()) do
if v:IsA("BasePart") and v:CanSetNetworkOwnership() then
v:SetNetworkOwner(nil)
end
end
task.spawn(updateZombie , zombie)
task.wait()
end
i recommend saving the old code because this may not work , i tested it and they should work fine now
They do work, thank you so much, although there is a small delay when reached the waypoints but is okay!
no problem , i am glad to hear that , you can play with the “WaypointSpacing” value but this may increase or decrease the quality of the path
wait do they able to unstuck they selves when path got block?
hmm you can use Path.Blocked event to trigger when the path get blocked and then create a new path