Need help for a good NPC Attack system

Hello developers,

Im currently looking for the best way to make an NPC attack system that does not affect the server performance at all. For example, tower defense games can handle many npc allies shooting without ruining server performance.

The only issue is, since i want to have multiple NPC’s that can attack enemies, this means it will constantly need to do a for i loop through the folder that contain the enemies on the map. I would expect this to somewhat drain performance. So my question is, how would i do this without of course, draining the server speed.

Like i said, games like tower defense games are a great example as they can handle a bunch of enemies and can handle a bunch of npcs attacking without hurting the server.

2 Likes

Why not just make an “Attack” script that is put inside every NPC, and that’s what makes them attack? You wouldn’t need any loops

1 Like

I meant, for the npcs to detect the enemies it will need to loop through the folder that contains the enemies to detect which is closest and whether or not they are in range. Sorry if i made it confusing.

1 Like

Oh okay, that makes more sense. Then you could create an “area” around the NPC (lets say a 5x5 part, weld it to the HRP), and use :GetTouchingParts (here) to check if there’s “another body” inside the area. If there is, attack it, if there isn’t, then get a random enemy from the folder and attack that. Or if you want them to always attack the closest, you only need to loop through it once, find which is closest, and then keep attacking that one until another gets inside the area.

Also, loops aren’t that bad to use. Okay yes if you have hundreds of NPCs and enemies then it might weigh the server down, but if you use just a few loops then it’s all right. And since there isn’t a more “performance-friendly” way of doing this, you have to use loops, just use them cleverly.

1 Like

Okay thanks for the solution. But, could you let me know if what i made was a smartest way of finding the enemies or is there an easier way?

function module.LocateTarget(currenttowerhrp, range)
	local target = nil
	
	if #workspace.RaidStuff.RaidEnemys:GetChildren() <= 0 then return end
	
	for i, v in ipairs(workspace.RaidStuff.RaidEnemys:GetChildren()) do
		if v:FindFirstChild("HumanoidRootPart") and v:FindFirstChild("Humanoid") and v:FindFirstChild("Humanoid").Health > 0 then else continue end
		local distance = (v.HumanoidRootPart.Position - currenttowerhrp.Position).Magnitude
		if distance < range then
			target = v
			range = distance
		end
	end
	
	return target
end
1 Like

I dont really understand the “then else continue end” part. Wouldnt you want to attack the enemy if they have a HRP and Humanoid and they arent dead?

So I guess like

for i, v in ipairs(workspace.RaidStuff.RaidEnemys:GetChildren()) do
	if v:FindFirstChild("HumanoidRootPart") and v:FindFirstChild("Humanoid") and v:FindFirstChild("Humanoid").Health > 0 then
		local distance = (v.HumanoidRootPart.Position - currenttowerhrp.Position).Magnitude
		if distance < range then
			target = v
			range = distance
		end
	end
end

And no need for an else if it doesn’t do anything anyway.
But other than that, yes I think this looks as simple as it can be. Maybe you could move the “dead bodies” to another folder, so you don’t have to check for health, but that’s optional.

1 Like

Okay, thank you for the help. If you do want to check through the main attacking system below and let me know if there was anything wrong with it then it would be appreciated. Either way, thanks for the support.

function Attack()
	local target = attackModule.LocateTarget(script.Parent.Parent.Parent, range)

	if game.Workspace.GameValues.RaidOngoing.Value == false then ToggleLasers(nil) return end
	if script.Parent.Parent.Parent.Stunned.Value == true then ToggleLasers(nil) task.wait(1) return Attack() end
	if target then else ToggleLasers(nil) task.wait(0.5) return Attack() end

	if target and target:FindFirstChild("HumanoidRootPart") and target.Humanoid and target.Humanoid.Health > 0 then
		game.ReplicatedStorage.Events.LookToTarget:Fire(target.HumanoidRootPart.Position)
	end	

	if target and target.Humanoid and target.Humanoid.Health > 0 then
		attackModule.DamageTarget(target, damage)
		script.Parent.Parent.Parent.Fire:Play()
		ToggleLasers(target)
	end	
	task.wait(cooldown)
	return Attack()
end

game.Workspace.GameValues.RaidOngoing.Changed:Connect(function(val)
	if val == true then Attack() end
end)

Having multiple of these, or even just having over 50 enemies will start showing some significant server slowness.

1 Like