Most efficient way to handle damage

Scenario:

If 100 different objects we will call ‘attackers’ are attacking an object we will call ‘wall’, with:
1/3 of them dealing 1 damage every second
1/3 of them dealing 2 damage every 2 seconds
1/3 of them dealing 3 damage every 3 seconds

What is the most efficient and most performant way to handle the damage being dealt to the wall, ideally reflecting the ‘attack rate’ of each attacker, however not necessary if another solution is very well designed.

Id rather the obvious were not stated of have each attacker have their own loop decreasing the health value. unless you have sufficient backing to prove thats the most efficient method.

2 Likes

I believe that a more efficient way of tackling this is to use the CollectionService and tag the “attackers” with different tags denoting their corresponding damage values. To do that, you can use the Tag Editor plugin and select the instances you want to be a specific tag and give them that tag. Then, you can loop through the tagged instances and incorporate a function there that damages the wall.

Essentially, this is what I’m reffering to:

local cs = game:GetService("CollectionService")
local attack1 = cs:GetTagged("Attack1") --does 1 damage
local attack2 = cs:GetTagged("Attack2")
local attack3 = cs:GetTagged("Attack3")

local function attack(damage)

    if damage == 1 then wall.Health.Value = wall.Health.Value - 1 end
    if damage == 2 then wall.Health.Value = wall.Health.Value - 2 end
    if damage == 3 then wall.Health.Value = wall.Health.Value - 3 end 

end

local function loop()

    for i, v in pairs(attack1) do attack(1) end
    for i, v in pairs(attack2) do attack(2) end
    for i, v in pairs(attack3) do attack(3) end

end

--run the function loop() whenever you want to!

This is done in only ~15 lines (and the manual tagging with the Tag Editor). But hopefully, this is what you were looking for (i.e. I hope you weren’t referring to this in your last sentence).

Hope that helps!

2 Likes

This is what I tried:

  • The attackers will tell the wall they have begun attacking and for how much damage every variable seconds.

  • The wall will group attackers with other attackers with the same damage and attack rate.

  • When it is time for that group to attack, the damage is calculated something like Damage*Amount of Attackers.

So, the wall will damage itself based on groups of attackers rather than every individual attacker.

My implementation assumes all attack rates are in integer seconds because of the way I use a table to count attackers with the same damage and attack rate.

local Health = 5000
local TimeSinceExistence = 0

local AttackerGroups = 
{
	--[[
	{
		AttackRate = 20, -- seconds
		Damage = 5,
		TimeOffsetGroups = 
		{
			[1] = 4, -- 0 time offset, but tables in lua start at 1
			[2] = 40, -- values are the amount of attackers at this time offset
			[3] = 33,
			[4] = 8,
			[5] = 9,
			[6] = 100,
			[7] = 2,
			[20] = 9
		}
	}
	--]]
}

local function DealDamage(Damage)
	print('Took',Damage,'Damage')
	Health = Health - Damage
end

local function GetExistingAttackGroup(AttackRate,Damage)
	for i = 1, #AttackerGroups do
		local AttackGroup = AttackerGroups[i]
		if AttackGroup.Damage == Damage and AttackGroup.AttackRate == AttackRate then
			return i
		end
	end
end

local function AddAttacker(AttackRate,Damage,ImmediateAttack)
	print('New Attacker dealing',Damage,'damage every',AttackRate,'seconds')
	
	local FoundExistingAttackGroup = GetExistingAttackGroup(AttackRate,Damage)
	
	local TimeOffset = TimeSinceExistence % AttackRate + 1 -- adding 1 to match table indexes
	
	if FoundExistingAttackGroup then
		local SameAttackerCount = AttackerGroups[FoundExistingAttackGroup].TimeOffsetGroups[TimeOffset]
		AttackerGroups[FoundExistingAttackGroup].TimeOffsetGroups[TimeOffset] = SameAttackerCount and SameAttackerCount+1 or 1
	else
		table.insert(AttackerGroups,
		{
			AttackRate = AttackRate,
			Damage = Damage,
			TimeOffsetGroups = 
			{
				[TimeOffset] = 1
			}
		})
	end
	
	if ImmediateAttack then
		DealDamage(Damage)
	end
	
	return TimeOffset -- for removing attacker later
end

local function RemoveAttacker(AttackRate,Damage,TimeOffset)
	print('Removing Attacker dealing',Damage,'damage every',AttackRate,'seconds')
	
	local FoundExistingAttackGroup = GetExistingAttackGroup(AttackRate,Damage)
	
	if FoundExistingAttackGroup then
		local SameAttackerCount = AttackerGroups[FoundExistingAttackGroup].TimeOffsetGroups[TimeOffset]
		AttackerGroups[FoundExistingAttackGroup].TimeOffsetGroups[TimeOffset] = SameAttackerCount and SameAttackerCount-1 or nil
		
		-- removing group from table if there are no attackers left
		for OffsetTime,AttackersAmount in next, AttackerGroups[FoundExistingAttackGroup].TimeOffsetGroups do
			if AttackersAmount and AttackersAmount > 0 then
				return
			end
		end
		table.remove(AttackerGroups,FoundExistingAttackGroup)
	end
end

local function DealGroupDamages()
	for i = 1, #AttackerGroups do
		local AttackGroup = AttackerGroups[i]
		for OffsetTime,AttackersAmount in next, AttackGroup.TimeOffsetGroups do
			if TimeSinceExistence % AttackGroup.AttackRate + 1 == OffsetTime then
				print(AttackGroup.Damage,TimeSinceExistence)
				DealDamage(AttackGroup.Damage*AttackersAmount)
			end
		end
	end
end

-- Testing code
for i = 1, math.random(1,10) do
	local AttackRate,Damage = math.random(5),math.random(100)
	local TimeOffset = AddAttacker(AttackRate,Damage,true)
	spawn(function()
		wait(math.random(10))
		RemoveAttacker(AttackRate,Damage,TimeOffset)
	end)
end

while wait(1) do
	TimeSinceExistence = TimeSinceExistence + 1
	
	DealGroupDamages()
	print(Health,TimeSinceExistence)
end
1 Like