How can I make a tower defense attacking system?

Sorry the title is really vague, I can’t word things properly half the time.

I am trying to make a tower defense game, and I want to make the attacking system to where once an enemy enters the range, the tower will keep attacking until either the enemy dies or leaves the range. I then want the tower to target the newest entered enemy. How would I do this?

local zone = require(game.ServerScriptService.Modules.Main.Zone)
local attackRange = zone.new(script.Parent)

local enemies = workspace.Placing.Enemies
local dealDmg = game.ServerStorage.Events.DealDamage
local code = script.Parent.Parent.Code.Value

local damage = script.Parent.Parent:GetAttribute("Damage")
local hitDelay = script.Parent.Parent:GetAttribute("HitDelay")

local target = nil
local nextTarget = nil
local queue = {}

dealDmg.Event:Connect(function(enemy, accessCode)
	if accessCode == code then 
		if not target then 
			target = enemy
		elseif not nextTarget then
			nextTarget = enemy
		else
			table.insert(queue, enemy)
		end
	end
end)

Here’s my current code I have. This just receives the call the enemies make when they enter a hit zone, and it checks if the given code is the same (checking if the current tower is the right tower). I was going to do a while wait() do loop, however I don’t think this would be resource friendly as there is going to be more than one towers, and also I was going to have it check the queue() table. I’ve been stuck on this topic all day and any help is appreciated. Thanks :slight_smile:

1 Like

2 Likes

Thanks but I don’t want to follow any tutorials as most show the code and vaguely explain it, but I don’t want to copy someone else’s work but problem solve through my own, which is why I made this post

3 Likes

Understandable, it’s just that GnomeCode’s tutorial has 16 parts or so so far and they are all really in depth and well made.

I’m honestly not sure what is the best approach, but you are correct. I would also use task.wait instead.
Task Library - Now Available! - Updates / Announcements - DevForum | Roblox

Using events as triggers is probably the way to go, however which events you should listen for is unclear. You could use a .Touched to find out when something has entered the range (if range is a part). You could then add it to a list (or something) of enemies to attack. I am not sure, however, how you would remove it in a smooth way.

BindableEvents may be useful, however I don’t know when you should call said event. If you are using a BindableEvent you could have one on each tower, then fire said event when entering range. HOWEVER this is not a good solution, as the enemy would have to keep track of what should be shooting it, which is just a backwards way of doing it. It could be used to trigger removal of said enemy when it leaves, but I think you can find a better way to do this.

Sorry for no real solutions, but I hope at least something is of help.

1 Like

If you dont want to go the route to while wait() check magnitude between enemy and tower then I would suggest having a physical part representing the range for the tower and having a .touched event and checking if enemy touched and then while wait() and check magnitude, if no longer in range then break the loop.

both seem bad, I would ultimately suggest trying to utilize zoneplus v3.x module it seems to have something effective if you plan to have a physical part representing the range of a tower but requires basepart or etc to work for the enemies:

trackItem
--where characterOrBasePart is the determined physical range part, zone is the zone module.
zone:trackItem(characterOrBasePart)
This is used to detect your own custom instances within zones, such as NPCs, and is a recommended replacement for part-events/methods.
An item can be any BasePart or Character/NPC (i.e. a model with a Humanoid and HumanoidRootPart). Once tracked, it can be listened for with the zone.itemEntered and zone.itemExited events.

An item will be automatically untracked if destroyed or has its parent set to nil.
--can be untracked manually

I actually just noticed you have the zone module already lol

with zoneplus in mind, youll have to determine how to identify and specify each unique enemy that comes and goes within the range zone and not duplicate. I think the best way would be to have a table of in-range enemies:

--strongly reccommended to have a singular basepart to prevent range errors i.e. part leaves but enemy still in range barely by other parts.
EnemiesTargetable = {}

--you need only to trackitem when you spawn in a new enemy! anything else not being tracked will not be identified.

--utilizing zone.itemEntered:

zone.itemEntered:Connect(function(BasePart)
--parent crucial because there can be multiple,
--rely on parents of basepart to prevent duplicates.
if not table.find(EnemiesTargetable, BasePart.Parent) then
table.insert(EnemiesTargetable, BasePart.Parent)
end
end)

--inversely for itemexitted

zone.itemExited:Connect(function(BasePart)
--parent crucial because there can be multiple baseparts associated with a parent,
--rely on parents of basepart to prevent duplicates or errors.

--we can also assume that this is a queue type system,
--so theoretically we can always do: table.remove(EnemiesTargetable, 1)
--due to first in line is always the first to exit yes? Otherwise the following will work:
   if table.find(EnemiesTargetable, BasePart.Parent) then
      for i=1, #EnemiesTargetable, 1 do
         if EnemiesTargetable[i]==BasePart.Parent then
            table.remove(EnemiesTargetable, i)
         end
      end
   end
end)

and then obviously, you will need to come up with when target==nil then attack the first target in enemies targetable table, you also need to implement when table.remove, targetbool = nil.

Although I am very confused on why you have a deal damage event…?

2 Likes

Thanks, I’ll try out something with this in mind :smiley:

Yea I was thinking of tracking the enemy with the module until they leave, but I was unsure if it would actually work

My naming is bad, but it’s basically what the enemies fire once they get in range of a target. The reason why the code variable is in place is so only the tower that the enemy is in recieves the data, and so it can begin attacking if theres none in the range