[SOLVED] Why is sometimes my AI not working? (Simple Path)

Greetings!

I’m making a zombie game, but the AI sometimes doesn’t work, like it actually chases the player, but sometimes, when a zombie spawns for the first time, it like… bugs out and just stays still, he doesn’t do anything, i need help on this, here is both the scripts
oh and before i show it, some people may be saying: “Why don’t you use a loop, bro, that’s the most easy thing ever, just use a loop” EVERY TIME I use a loop, it lags out when there is a bit many zombies, like 7 zombies like what??? Ok now here is the script: (the ai and the spawner)

Spawner:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ZombiesFolder = ReplicatedStorage.Zombies

local ZombiesLimit = 7

while task.wait(10) do
	if #workspace.Zombies:GetChildren() >= ZombiesLimit then
		continue
	end
	local Zombies = ZombiesFolder:GetChildren()
	local RandomZombie = Zombies[math.random(1, #Zombies)]
	local NewZombie = RandomZombie:Clone()
	NewZombie.Parent = workspace.Zombies
	NewZombie:FindFirstChild(NewZombie.Name.."AI").Enabled = true
	NewZombie:MoveTo(Vector3.new(math.random(-100, 100), 35, math.random(-100, 100)))
end

AI:

-- Import the module so you can start using it
local ServerStorage = game:GetService("ServerStorage")
local SimplePath = require(ServerStorage.SimplePath)
local RaycastHitbox = require(game.ReplicatedStorage.Modules.RaycastHitboxV4)

local NPC = script.Parent -- Ensure NPC is defined

local AttackSounds = NPC.Attacks

local AttackAnim = NPC.Humanoid:LoadAnimation(NPC.Animations.Attack)
local DamageEvent = game.ReplicatedStorage.Events.DamageEvent

local StunnedAnim = NPC.Humanoid:LoadAnimation(NPC.Animations.Stunned)
local StunnedIdleAnim = NPC.Humanoid:LoadAnimation(NPC.Animations.StunnedIdle)

StunnedAnim.Priority = Enum.AnimationPriority.Action4
StunnedIdleAnim.Priority = Enum.AnimationPriority.Action2

local AttackDB = false

local Players = game:GetService("Players")

local function findClosestPlayer(NPC)
	local closestPlayer = nil
	local closestDistance = math.huge

	for _, player in Players:GetPlayers() do
		local character = player.Character
		if character and character:FindFirstChild("HumanoidRootPart") then
			local distance = (character.HumanoidRootPart.Position - NPC.HumanoidRootPart.Position).Magnitude
			if distance < closestDistance then
				closestDistance = distance
				closestPlayer = player
			end
		end
	end

	return closestPlayer
end

function AttackCheck(closestPlayer, path)
	local distance = (closestPlayer.Character.HumanoidRootPart.Position - NPC.HumanoidRootPart.Position).Magnitude
	if distance < 5 and AttackDB == false then -- Assuming 5 is the attack range
		local NewHitbox = RaycastHitbox.new(NPC)
		local Params = RaycastParams.new()
		Params.FilterType = Enum.RaycastFilterType.Exclude
		Params.FilterDescendantsInstances = {NPC}
		NewHitbox.OnHit:Connect(function(hit, hum)
			local Player = game.Players:GetPlayerFromCharacter(hum.Parent)

			if Player and Player:FindFirstChild("Invincible").Value == false then
				if Player:FindFirstChild("Parrying").Value == true then
					script.Parent.HumanoidRootPart.Anchored = true
					StunnedAnim:Play()
					AttackAnim:Stop()
					StunnedIdleAnim:Play()
					task.wait(5)
					StunnedAnim:Stop()
					StunnedIdleAnim:Stop()
					script.Parent.HumanoidRootPart.Anchored = false
					--path:Run(closestPlayer.Character.HumanoidRootPart)
				else
					if Player:FindFirstChild("Invincible").Value == false then
						DamageEvent:FireClient(Player)
						hum:TakeDamage(5)
						Player:FindFirstChild("Invincible").Value = true
						--path:Run(closestPlayer.Character.HumanoidRootPart)
					end
				end
			end
		end)
		local RandomAttackSound = AttackSounds:GetChildren()[math.random(1, #AttackSounds:GetChildren())]
		AttackDB = true
		NewHitbox:HitStart(1)
		RandomAttackSound:Play()
		AttackAnim:Play()
		task.wait(2)
		AttackDB = false
		--task.wait(2)
		path:Run(closestPlayer.Character.HumanoidRootPart)
	else
		path:Run(closestPlayer.Character.HumanoidRootPart)
	end
end
--if NPC and NPC:WaitForChild("HumanoidRootPart") then
local success, errormessage = pcall(function()
	local closestPlayer = findClosestPlayer(NPC)

	if closestPlayer then
		local path = SimplePath.new(NPC)
		path:Run(closestPlayer.Character.HumanoidRootPart)

		path.Blocked:Connect(function()
			AttackCheck(closestPlayer, path)
			closestPlayer = findClosestPlayer(NPC)
		end)

		path.WaypointReached:Connect(function()
			AttackCheck(closestPlayer, path)
			closestPlayer = findClosestPlayer(NPC)
		end)

		path.Error:Connect(function(errorType)
			AttackCheck(closestPlayer, path)
			closestPlayer = findClosestPlayer(NPC)
		end)

		path.Reached:Connect(function()
			AttackCheck(closestPlayer, path)
			closestPlayer = findClosestPlayer(NPC)
		end)
	end
end)
--end

DamageEvent.OnServerEvent:Connect(function(Player)
	Player:FindFirstChild("Invincible").Value = false
end)

Can someone help? I’m having this problem even in the start of development, also the AI breaks only when it spawns for the first time (and sometimes it breaks naturally).

2 Likes

Guys, i need help, please! Any help is appreciated!!!

1 Like

So, are you guys gonna help or what?

Please be patient, we all just volunteer here, and not everyone knows the answer to your particular issue.
I’m not sure of why, but a good troubleshooting tool is to print variables before they are checked with an if. It tells you what the variable is so you can see any unexpected behavior and trace back to where the variable is set.
Let’s say in this example the output gives you a name you don’t expect. You trace back where the variable is set and look for why it’s different:

    print("closestPlayer = ", closestPlayer.Name)
	if closestPlayer then
		local path = SimplePath.new(NPC)
		path:Run(closestPlayer.Character.HumanoidRootPart)

You can do this for any if checks.

1 Like

Sorry i was sleeping by the time you responded. hehe, anyways i get what you’re saying. I’m gonna try, i’ll get back to you if something happens with it

Hmm that’s weird, it prints, but it doesn’t run to the player, now that’s a weird thing

Try debugging using print on each line to see where it is getting stuck.

1 Like

I’m currently working on another thing right now so i can’t debug FOR NOW.

Well, sometimes it doesn’t even fire anything, like it broke or something

So that means the findClosestPlayer function isn’t even firing. This is why we troubleshoot the way I suggested.
Go to the if checks before you call the findClosestPlayer function to troubleshoot why it isn’t happening.

To be honest, i actually changed the whole thing, here is the new zombiespawner (i actually don’t know if this will lag or not, and also i’ll make the damage system later):

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ZombiesFolder = ReplicatedStorage:WaitForChild("Zombies")
local Workspace = game:GetService("Workspace")

local WorkspaceZombiesFolder = Workspace:WaitForChild("Zombies")
local SpawnZombieEvent = ReplicatedStorage:WaitForChild("SpawnZombie")

local ZombiesLimit = 7
local SpawnArea = {minX = -100, maxX = 100, minY = 35, maxY = 35, minZ = -100, maxZ = 100}

local ZombieRarity = {
    NormalZombie = 70,  -- 70% chance
    FastZombie = 20,    -- 20% chance
    StrongZombie = 10   -- 10% chance
}

local function getRandomPosition()
    return Vector3.new(
        math.random(SpawnArea.minX, SpawnArea.maxX),
        math.random(SpawnArea.minY, SpawnArea.maxY),
        math.random(SpawnArea.minZ, SpawnArea.maxZ)
    )
end

local function getRandomZombieType()
    local totalWeight = 0
    for _, weight in ZombieRarity do
        totalWeight = totalWeight + weight
    end

    local randomWeight = math.random(1, totalWeight)
    local cumulativeWeight = 0

    for zombieType, weight in ZombieRarity do
        cumulativeWeight = cumulativeWeight + weight
        if randomWeight <= cumulativeWeight then
            return zombieType
        end
    end
end

local function spawnZombie()
    local success, errorMessage = pcall(function()
        if #WorkspaceZombiesFolder:GetChildren() >= ZombiesLimit then
            return
        end

        local Zombies = ZombiesFolder:GetChildren()
        if #Zombies == 0 then
            warn("No zombies found in ReplicatedStorage.Zombies")
            return
        end

        local selectedZombieType = getRandomZombieType()
        local RandomZombie

        for _, zombie in Zombies do
            if zombie.Name == selectedZombieType then
                RandomZombie = zombie
                break
            end
        end

        if not RandomZombie then
            warn("Selected zombie type not found: " .. selectedZombieType)
            return
        end

		local NewZombie = RandomZombie:Clone()
		NewZombie.Parent = WorkspaceZombiesFolder
		NewZombie.HumanoidRootPart:SetNetworkOwner(nil)
        local AIScript = NewZombie:FindFirstChild(NewZombie.Name.."AI")
        if AIScript then
            AIScript.Enabled = true
        else
            warn("AI script not found for zombie: " .. NewZombie.Name)
        end
        NewZombie:MoveTo(getRandomPosition())
    end)

    if not success then
        warn("Error in ZombieSpawner: " .. errorMessage)
    end
end

local function adjustZombieLimit(newLimit)
    ZombiesLimit = newLimit
    print("ZombiesLimit adjusted to: " .. ZombiesLimit)
end

SpawnZombieEvent.OnServerEvent:Connect(function(player, action, newLimit)
    if action == "spawn" then
        spawnZombie()
    elseif action == "adjustLimit" and newLimit then
        adjustZombieLimit(newLimit)
    end
end)

-- Initial spawn loop
game.Players.PlayerAdded:Connect(function(Player)
	Player.CharacterAdded:Connect(function(Character)
		task.wait(1)
		for i = 1, 2 do
			spawnZombie()
			task.wait(0.3)
		end
	end)
end)

ok nevermind it is COMPLETELY broken, it is so bad bro wut

Uhh… hey guys, can you uhhh help me? i’m still having this problem, it is not fixed yet…

I keep telling you an easy way to troubleshoot your scripts. You just have to do a tiny of work to identify the area of code that isn’t working and track backwards through it to find out where the issue is.
It’s way easier to find the specific section of code (or even just a single line) that’s giving you trouble and ask forum members for help with that, rather than handing them the entire script and saying ‘it doesn’t work’.
I’m not a great scripter, but if I have a problem I just use this method. Trust me, it helps you out a lot.

Please reply to the OP, not me. Otherwise they won’t get a notification of your reply.

First Pls Be Patient,I saw the error,Ther error is on (NewZombie.name.“AI”)

remove the AI part

NewZombie:FindFirstChild(NewZombie.Name…“AI”).Enabled = true – This part just removed it

1 Like

But it will not work, i already tried removing it, it just became worse from what i remember

It seems that i have fixed the problem, here is the new ai handler:

local closestPlayer = findClosestPlayer(NPC)

if closestPlayer then
	local path = SimplePath.new(NPC)
	--task.wait(2)
	NPC.HumanoidRootPart.Anchored = false
	AttackCheck(path)
	path:Run(closestPlayer.HumanoidRootPart)

	path.Blocked:Connect(function()
		AttackCheck(path)
		closestPlayer = findClosestPlayer()
	end)

	path.WaypointReached:Connect(function()
		AttackCheck(path)
		closestPlayer = findClosestPlayer()
	end)

	path.Error:Connect(function(errorType)
		AttackCheck(path)
		closestPlayer = findClosestPlayer()
	end)

	path.Reached:Connect(function()
		AttackCheck(path)
		closestPlayer = findClosestPlayer()
	end)--]]

--new part
else
	closestPlayer = findClosestPlayer(NPC)
	repeat task.wait() until closestPlayer
end

Nevermind, it still happens, it is a bit hard tho, but it still bugs out… COME ON!!! WHY IS THIS SUPPOSED TO BE SO HARD!!!

Bro, i can’t fix it, what is happening with that AI?! For some reason, i tried troubleshooting the way you suggested, but it looks like the closest player function fires, but it for some reason just don’t run to the player, and for some reason, it like, do you know a loop? Exactly, it has a break function, it is like you’re calling the break function to stop the loop, which is not what i’m doing! What should I do? Wrap the function into a pcall?!