I need some suggestions/help with optimizing my ai code

So basically, I made this npc so that it charges at you and beats you to death, while using pathfinding and stuff which is why I’m using a pathfinding module. It works relatively smoothly and alright up until around 30 of them. This code is inside of a spawn function in a module script so it’s all in one place and any server script and spawn an enemy.

I’m just trying to see if I can change things in the code so that it does what it already does but better, and I also want to make it less laggy and be able to handle more

local ServerStorage = game:GetService("ServerStorage")
local SimplePath = require(ServerStorage.SimplePath)
local RaycastHitbox = require(ServerStorage:WaitForChild("RaycastHitboxV4"))

local spawnMarker = ServerStorage:FindFirstChild("spawnMarker")

function BehaviorHandler:SpawnDwarf(x,y,z)
	local Thread = coroutine.create(function()

		--some variables

		local character = game.ServerStorage.Enemies:FindFirstChild("Dwarf"):Clone()
		local human = character:WaitForChild("Humanoid")
		local OldHealth = human.Health
		local myHRP = character:WaitForChild("HumanoidRootPart")
		local assets = character:FindFirstChild("Assets")

		--spawn markers so i see where they're gonna spawn

		local clone = spawnMarker:Clone()
		clone.Position = Vector3.new(x, spawnMarker.Position.Y,z)
		clone.Parent = workspace

		task.wait(3)
		character:SetPrimaryPartCFrame(CFrame.new(Vector3.new(x,y,z)))
		character.Parent = workspace.Enemies
		clone:Destroy()

		--more variables

		local Path = SimplePath.new(character)
		local HitboxRight = RaycastHitbox.new(character:WaitForChild("RightHand"))
		local HitboxLeft = RaycastHitbox.new(character:WaitForChild("LeftHand"))

		local DamageParticle = assets:WaitForChild("DamageParticle")

		local attack1Sound = assets:WaitForChild("Attack1Sound")
		local hitSound = assets:WaitForChild("HitSound")

		local attack1 = assets:WaitForChild("Attack1")
		local attack2 = assets:WaitForChild("Attack2")

		local attack1Track = human:LoadAnimation(attack1)
		local attack2Track = human:LoadAnimation(attack2)

		local Damage = 15
		local mainWalkSpeed = 32
		local attackSpeed = 0.3
		local attackCooldown = 0.5
		local lastAttack = 2
		local attackDistance = 8
		local faceDistance = 25

		local db = true

		local timeCount = 0
		local timeCountOfLastAttack = 0

		--setting network ownership

		if myHRP:CanSetNetworkOwnership() then
			myHRP:SetNetworkOwner(nil)
		end

		--functions
		local function Attack()
			local attackThread = coroutine.create(function()
				if db then
					db = false
					--human.WalkSpeed = mainWalkSpeed/2
					attack1Sound:Play()
					if lastAttack == 2 then
						lastAttack = 1
						attack1Track:Play()
						HitboxRight:HitStart()
						task.wait(attackSpeed)
						HitboxRight:HitStop()
						human.WalkSpeed = mainWalkSpeed
						db = true
					else
						lastAttack = 2
						attack2Track:Play()
						HitboxLeft:HitStart()
						task.wait(attackSpeed)
						HitboxLeft:HitStop()
						human.WalkSpeed = mainWalkSpeed
						db = true
					end
				end

			end)
			coroutine.resume(attackThread)
		end

		local target = nil

		local function findTarget()
			local tempTarget = nil
			local maxDist = 1000
			for i,v in pairs(workspace:GetDescendants()) do
				if v:IsA("Humanoid") then
					local PotEnemyChar = v.Parent
					if PotEnemyChar.Parent ~= workspace.Enemies and PotEnemyChar.Parent ~= workspace.Passive then
						local enemyHRP = PotEnemyChar:FindFirstChild("HumanoidRootPart")
						local dist = (enemyHRP.Position - myHRP.Position).Magnitude
						if dist < maxDist and v.Health > 0 then
							maxDist = dist
							tempTarget = enemyHRP
						end
					end
				end
			end
			target = tempTarget
		end

		--hit detection

		HitboxRight.OnHit:Connect(function(hit, humanoid)
			if humanoid.Parent ~= character and humanoid.Parent.Parent ~= workspace.Enemies and humanoid.Parent.Parent ~= workspace.Passive then
				local clone = DamageParticle:Clone()
				humanoid:TakeDamage(Damage)
				hitSound:Play()
				clone.Parent = hit
				clone:Emit(50)
				game:GetService("Debris"):AddItem(clone, 1)
			end
		end)

		HitboxLeft.OnHit:Connect(function(hit, humanoid)
			if humanoid.Parent ~= character and humanoid.Parent.Parent ~= workspace.Enemies and humanoid.Parent.Parent ~= workspace.Passive then
				local clone = DamageParticle:Clone()
				humanoid:TakeDamage(Damage)
				hitSound:Play()
				clone.Parent = hit
				clone:Emit(50)
				game:GetService("Debris"):AddItem(clone, 1)
			end
		end)

		--main

		while task.wait(.1) do
			if human.Health == 0 then
				break
			end

			timeCount = timeCount + 0.1

			if human.Health > 0 then
				findTarget()
				if target then
					local futurePos = target.Position + (target.Velocity / 3)
					Path:Run(futurePos)

					if (target.Position - myHRP.Position).Magnitude <= attackDistance and timeCount - timeCountOfLastAttack >= attackCooldown then
						Attack()
						timeCountOfLastAttack = timeCount
					end
				end
			end

			--no use for this yet

			human:GetPropertyChangedSignal("Health"):Connect(function()
				if human.Health < OldHealth then
					OldHealth = human.Health
				end
			end)

			--remove body on death

			human.Died:Connect(function()
				game:GetService("Debris"):AddItem(character, 10)
			end)


		end

	end)


	coroutine.resume(Thread)

end

What do you think of this? 42 lines less

local ServerStorage = game:GetService("ServerStorage")
local SimplePath = require(ServerStorage.SimplePath)
local RaycastHitbox = require(ServerStorage:WaitForChild("RaycastHitboxV4"))

local spawnMarker = ServerStorage:FindFirstChild("spawnMarker")

function BehaviorHandler:SpawnDwarf(x,y,z)
	task.spawn(function()		
		--some variables
		local character = game.ServerStorage.Enemies:FindFirstChild("Dwarf"):Clone()
		local human = character:WaitForChild("Humanoid")
		local myHRP = character:WaitForChild("HumanoidRootPart")
		local assets = character:FindFirstChild("Assets")

		--spawn markers so i see where they're gonna spawn
		local clone = spawnMarker:Clone()
		clone.Position = Vector3.new(x, spawnMarker.Position.Y,z)
		clone.Parent = workspace

		task.wait(3)
		character:SetPrimaryPartCFrame(CFrame.new(x,y,z))
		character.Parent = workspace.Enemies
		clone:Destroy()

		--more variables
		local Path = SimplePath.new(character)
		local HitboxRight = RaycastHitbox.new(character:WaitForChild("RightHand"))
		local HitboxLeft = RaycastHitbox.new(character:WaitForChild("LeftHand"))

		local DamageParticle = assets:WaitForChild("DamageParticle")

		local attack1Sound = assets:WaitForChild("Attack1Sound")
		local hitSound = assets:WaitForChild("HitSound")

		local attack1 = assets:WaitForChild("Attack1")
		local attack2 = assets:WaitForChild("Attack2")

		local attack1Track = human:WaitForChild("Animator"):LoadAnimation(attack1)
		local attack2Track = human:WaitForChild("Animator"):LoadAnimation(attack2)

		local Damage = 15
		local mainWalkSpeed = 32
		local attackSpeed = 0.3
		local attackCooldown = 0.5
		local lastAttack = 2
		local attackDistance = 8
		local faceDistance = 25

		local db = true

		local timeCount = 0
		local timeCountOfLastAttack = 0

		--setting network ownership
		if myHRP:CanSetNetworkOwnership() then
			myHRP:SetNetworkOwner(nil)
		end

		--functions
		local function Attack()
			if not db then		return		end
			db = false

			--human.WalkSpeed = mainWalkSpeed/2
			attack1Sound:Play()
			if lastAttack == 2 then
				lastAttack = 1
				attack1Track:Play()
				HitboxRight:HitStart()
				task.wait(attackSpeed)
				HitboxRight:HitStop()
				human.WalkSpeed = mainWalkSpeed
				db = true
			else
				lastAttack = 2
				attack2Track:Play()
				HitboxLeft:HitStart()
				task.wait(attackSpeed)
				HitboxLeft:HitStop()
				human.WalkSpeed = mainWalkSpeed
				db = true
			end
		end
		local function findTarget()
			local tempTarget = nil
			local maxDist = 1000
			for _,v in pairs(workspace:GetDescendants()) do
				if not v:IsA("Humanoid") or v:IsDescendantOf(workspace.Enemies) or v:IsDescendantOf(workspace.Passive) then		continue			end

				local PotEnemyChar = v.Parent
				local enemyHRP = PotEnemyChar:FindFirstChild("HumanoidRootPart")
				local dist = (enemyHRP.Position - myHRP.Position).Magnitude
				if dist < maxDist and v.Health > 0 then
					maxDist = dist
					tempTarget = enemyHRP
				end
			end
			return tempTarget
		end

		--hit detection
		local function OnHit(hit, humanoid)
			if humanoid.Parent ~= character and humanoid.Parent.Parent ~= workspace.Enemies and humanoid.Parent.Parent ~= workspace.Passive then
				local clone = DamageParticle:Clone()
				humanoid:TakeDamage(Damage)
				hitSound:Play()
				clone.Parent = hit
				clone:Emit(50)
				game:GetService("Debris"):AddItem(clone, 1)
			end
		end
		HitboxRight.OnHit:Connect(OnHit)				HitboxLeft.OnHit:Connect(OnHit)
		
		--no use for this yet
		local OldHealth = human.Health
		human.HealthChanged:Connect(function(Health)
			OldHealth = (Health < OldHealth and Health) or OldHealth
		end)
		
		--remove body on death
		human.Died:Connect(function()
			game:GetService("Debris"):AddItem(character, 10)
		end)

		--main
		while human.Health > 0 do
			timeCount += 0.1
			
			local target = findTarget()
			if target then
				local futurePos = target.Position + (target.Velocity / 3)
				Path:Run(futurePos)

				if (target.Position - myHRP.Position).Magnitude <= attackDistance and timeCount - timeCountOfLastAttack >= attackCooldown then
					task.spawn(Attack)
					timeCountOfLastAttack = timeCount
				end
			end
			task.wait(.1)
		end
	end)
end

142 lines and your code had 184, also 2 updates, Animator:LoadAnimation() and task.spawn.

1 Like

does task.spawn make a new thread?

Yes, it is similar to creating and calling a coroutine.

1 Like

this is repeated code
tried to fix it a bit

local function Attack()
    local Hitbox = if lastAttack == 1 then HitboxLeft else HitboxRight
    local Track = if lastAttack == 1 then attack2Track else attack1Track

    if db then
        db = false

        --human.WalkSpeed = mainWalkSpeed / 2
        attack1Sound:Play()
        lastAttack = math.abs(lastAttack - 3)
        Track:Play()
        Hitbox:HitStart()
        task.wait(attackSpeed)
        Hitbox:HitStop()
        human.WalkSpeed = mainWalkSpeed

        db = true
    end
end
2 Likes