I need help finding a more effecient way to make my AI!

Include a standalone, bare-bones rbxl file with only the code you want reviewed.

  • Code Review is for reviewing specific parts of your code, and not your whole game.
  • Code Review is intended for improving already-working code. If you need help debugging your code, please use Scripting Support.

Provide an overview of:

  • This code is a simple AI movement system for my creatures in one of my latest projects. The problem is that its really ineffecient when theres a bunch in the workspace at once. When I dont loop around the whole code using a While wait() do loop it works but when theres not a player for a second the script will stop refreshing causing the AI to freeze up. Yet when I run While loops and theres multiple AI it makes the ping go up tremendously.
  • I’ve tried using for i = 0, math.huge, .1 do with a wait and it had a similar effect
  • I need a better way to loop around the script without causing lag to the players, or overall game! please help asap!
wait(1)
	
-- Zombie Settings
local normalWalkSpeed = 8
local runningWalkSpeed = 16
-- setings	
local SearchDistance = 	100 	-- How far a player can be before it detects you

local ZombieDamage = 35		-- How much damage the Zombie inficts towards the player
local DamageWait = 	1.5		-- How many seconds to wait before it can damage the player again
local loadedAnimTrack = nil
local WanderX, WanderZ = 50, 50
function getHumanoid(model)
	for _, v in pairs(model:GetChildren())do
		if v:IsA'Humanoid' then
			return v
		end
	end
end


local zombie = script.Parent
local human = getHumanoid(zombie)
human.WalkSpeed = math.random(12,17)	
human.Health = human.MaxHealth
local hroot = zombie.HumanoidRootPart
local zspeed = hroot.Velocity.magnitude
local head = zombie:FindFirstChild'Head'
local vars = script.vars
local canShout = true
local pfs = game:GetService("PathfindingService")
local players = game:GetService('Players')

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

local path
local waypoint

local chaseName = nil

function GetTorso(part)
	local chars = game.Workspace:GetChildren()
	local chaseRoot = nil
	local chaseTorso = nil
	local chasePlr = nil
	local chaseHuman = nil
	local mag = SearchDistance
	for i = 1, #chars do
		chasePlr = chars[i]
		if chasePlr:IsA'Model' and chasePlr ~= zombie then
			chaseHuman = getHumanoid(chasePlr)
			chaseRoot = chasePlr:FindFirstChild'HumanoidRootPart'
			if chaseRoot ~= nil and chaseHuman ~= nil and chaseHuman.Health > 0 and chaseHuman.Name ~= "Zombie" then
				if (chaseRoot.Position - part).magnitude < mag then
					chaseName = chasePlr.Name
					chaseTorso = chaseRoot
					mag = (chaseRoot.Position - part).magnitude
				end
			end
		end
	end
	return chaseTorso
end

function GetPlayersBodyParts(t)
	local torso = t
	if t == nil then return end
	if torso then
		local figure = torso.Parent
		if not figure or figure == nil then return end
		for _, v in pairs(figure:GetChildren())do
			if v:IsA'Part' then
				return v.Name
			end
		end
	else
		return "HumanoidRootPart"
	end
end

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
local damagetime
local damagedb = false
local combo = 1
for _, zambieparts in pairs(zombie:GetChildren())do
	if zambieparts:IsA'Part' and human.Health > 0 then
		zambieparts.Touched:connect(function(p)
			if p.Parent.Name == chaseName and p.Parent.Name ~= zombie.Name and not damagedb then -- damage
				damagedb = true
				damagetime = time()
				local enemy = p.Parent
				local enemyhuman = getHumanoid(enemy)
				vars.Attacking.Value = true
				if combo == 1 then
					loadedAnimTrack = human:LoadAnimation(script.Swing1):Play()
					combo = 2
				elseif combo == 2 then
					loadedAnimTrack = human:LoadAnimation(script.Swing2):Play()
					combo = 3
				elseif combo == 3 then
					loadedAnimTrack = human:LoadAnimation(script.Swing3):Play()
					combo = 1
				end
				script.Parent.Head["Slash 1"]:Play()
				enemyhuman:TakeDamage(ZombieDamage)
				wait(.1)
				script.Parent.Head["Z-Attack"]:Play()
				
				vars.Attacking.Value = false
				while wait() do
					if damagetime ~= nil and time() >= (damagetime + DamageWait) then
						damagedb = false
						damagetime = nil
					end
				end
			end
		end)
	end
end

-- wandering 
spawn(function()
	while vars.Wandering.Value == false and human.Health > 0 do	
		vars.Chasing.Value = false	
		vars.Wandering.Value = true
		if canShout then
			canShout = false
			script.Parent.Head:WaitForChild("Zombie-Growl"):Play()
			else
			task.delay(math.random(15,30), function()
				canShout = true
			end)
		end
		human.WalkSpeed = normalWalkSpeed
		local desgx, desgz = hroot.Position.x+math.random(-WanderX,WanderX), hroot.Position.z+math.random(-WanderZ,WanderZ)
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat ci = ci + 1 wait() until t[ci] ~= nil
				return Vector3.new(1,0,0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end
		
		path = pfs:FindPathAsync(hroot.Position, Vector3.new(desgx, 0, desgz))
		waypoint = path:GetWaypoints()
		local connection;
		
		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)
		
		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end
			
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo( waypoint[4].Position )
			else
				human.Jump = false
			end
			
			if connection then
				connection:Disconnect()
			end
			
		else
			for i = 3, #waypoint do
				human:MoveTo( waypoint[i].Position )	
			end
		end
		wait(math.random(1,5))
		vars.Wandering.Value = false
	end
end)

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

while wait() do
	local nrstt = GetTorso(hroot.Position)
	if nrstt ~= nil and human.Health > 0 then -- if player detected	
		if nrstt.Parent:FindFirstChild("Humanoid").Health == 0 then return end
		vars.Wandering.Value = false
		vars.Chasing.Value = true
		human.WalkSpeed = runningWalkSpeed
		local function checkw(t)
			local ci = 3
			if ci > #t then
				ci = 3
			end
			if t[ci] == nil and ci < #t then
				repeat ci = ci + 1 wait() until t[ci] ~= nil
				return Vector3.new(1,0,0) + t[ci]
			else
				ci = 3
				return t[ci]
			end
		end
		
		path = pfs:FindPathAsync(hroot.Position, nrstt.Position)
		waypoint = path:GetWaypoints()
		local connection;
		
		local direct = Vector3.FromNormalId(Enum.NormalId.Front)
		local ncf = hroot.CFrame * CFrame.new(direct)
		direct = ncf.p.unit
		local rootr = Ray.new(hroot.Position, direct)
		local phit, ppos = game.Workspace:FindPartOnRay(rootr, hroot)
		
		if path and waypoint or checkw(waypoint) then
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Walk then
				human:MoveTo( checkw(waypoint).Position )
				human.Jump = false
			end
			
			if checkw(waypoint) ~= nil and checkw(waypoint).Action == Enum.PathWaypointAction.Jump then
				connection = human.Changed:connect(function()
					human.Jump = true
				end)
				human:MoveTo( waypoint[4].Position )
			else
				human.Jump = false
			end
			
			hroot.Touched:connect(function(p)
				local bodypartnames = GetPlayersBodyParts(nrstt)
				if p:IsA'Part' and not p.Name == bodypartnames and phit and phit.Name ~= bodypartnames and phit:IsA'Part' and rootr:Distance(phit.Position) < 5 then
					connection = human.Changed:connect(function()
						human.Jump = true
					end)
				else
					human.Jump = false
				end
			end)
			
			if connection then
				connection:Disconnect()
			end
			
		else
			for i = 4, #waypoint do
				human:MoveTo( waypoint[i].Position )	
			end
		end
		path = nil
		waypoint = nil
	elseif nrstt == nil then -- if player not detected
		vars.Wandering.Value = false
		vars.Chasing.Value = false
		CchaseName = nil
		path = nil
		--waypoint = nil
		human.MoveToFinished:Wait()
	end
	end

-- Base script for NPC enemy movement

1 Like

I highly recommend you use RunService.

it’s like an enemy NPC; try avoiding loops.

Hey, what kind of run service because I tried heartbeat, stepped, and renderstepped and they all have a different effect and cause more issues! also where in the code would I impliment this change?

You’d want to make it heartbeat, render stepped is local.
RunService works like while loops but just better (not as fast as while loops)

there is the task scheduler in case you need it.

RunService.HeartBeat:Connect(function()
        -- script here 
end)

Use humanoid.Animator:LoadAnimation() as humanoid:LoadAnimation() is deprecated, try to use repeat and for loops less, it’s most likey why the game is laggy (specifically repeat loop).

I have no idea what exactly do you want to achieve in the script because variables are hard to read try to simplify your code

Hey thanks I appreciate all your help I will take all of that into consideration to try to upgrade my script as much as possible.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.