For my tower defense game, I want to make a AOE effect damage system. I did this by creating a circle, then when zombies touched it took their damage. The issue with this is that they can touch the circle multiple times, inflicting 4 damage every frame basically. I am wondering how to make zombies only take damage one time from the splash. Thanks!
tag the zombie with attribute use it as a debounce type setup something like this might work
if not Zombie:GetAttribute('Tagged') then
-- could even add to this number for different times it takes damage or just set it true
Zombie:SetAttribute('Tagged', 1)
-- do damage here
end
An easier solution is just using the workspace/worldroot method GetPartBoundsInRadius,
It only requires a position value and a radius, which if you’re using a sphere part to define the area then its just part.Size.X divided by 2.
Then for all the humanoidrootparts within the radius apply damage to each of them
If you have a physical circle (I.E. a cylinder part), you can use GetPartsInPart() to detect all zombies inside the circle at a given point in time.
With this approach, you would simply do workspace:GetPartsInPart(circle, params)
Params being an OverlapParams.new()
instance.
Alternatively, you could remove the need for a physical instance by using GetPartsBoundsInRadius(). This will tell you all of the parts inside a sphere of a given radius.
With this approach, you would simply do workspace:GetPartsBoundsInRadius(towerPos, radius, params)
Params being an OverlapParams.new()
instance.
As adrian and xander said, you could get parts in a radius, or you can use Magnitude to check how far from the center of your circle is. I’d only recommend it over the other methods as you can just check one parts vs getting an array of multiple parts if you’re only interested in their center part.
Its still dealing damage 24 times to 4 zombies.
for _,v in ipairs(workspace:GetPartsInPart(splash, OverlapParams.new())) do
local parent = v:FindFirstAncestorWhichIsA('Model')
if parent then
if parent:FindFirstChild('A') then
if parent.A:FindFirstChild('Zombie') then
print('Deal '..tostring(abs_damage))
parent.Humanoid:TakeDamage(abs_damage)
end
end
end
is my code.
The issue with this is that itll make so other AOE towers cant damage the same enemy.
your towers don’t have unique names or ids?
This is because the :GetPartsInPart() function returns every part. So if the zombie has both legs in the circle, both legs will show up on the table. To circumvent double damage, I’d advise you to create a temporary table containing already damaged humanoids during the current execution.
Creating a temporary array:
To do this, create a table outside of the for loop by doing DamagedEnemies = {}
.
Inserting Zombie to Array:
After that, you need to write a line of code that inserts a zombie into the table when it is damaged. To do this, you would simply write table.insert(DamagedEnemies, zombie)
right before the :TakeDamage()
line.
Checking Temp Array:
Then finally you can add a sanity check that ties this all together. After the if zombie then
conditional statement create another conditional statement. This statement will be:
if not table.find(DamagedEnemies, zombie) then -- if zombie has not been damaged yet
end
Clearing Array:
Lastly, you need to clear the table after the AOE attack. Simply do DamagedEnemies = {}
after the for pairs loop. By setting DamagedEnemies to { } you are essentially resetting the array to a blank slate.
Hey, thanks for your answer! This works perfectly and another thing is what Adrian said, you can also in your loop check for there humanoidrootparts before damage. Thanks!