I have an issue, my RTS game’s attack detection seems to be biased off the order which my RTS units are alphabetically sorted.
This is a question I’ve asked before, but I’m not really happy with my answer (it lags to high degrees even past 100 units).
My previous answer did essentially work but I think there’s a better way to do it, I just can’t figure it out. Previously, I was sorting a table with every unit I added and doing a magnitude check. However I was advised that magnitude checks for very high number of units is not too optimized (I would have to nest my loop, making it search through units^2).
So I’ve resorted to doing GetPartsInRadius with some overlap params. I also wanted to remove my previous table sort algorithm because it would severely lag my game on the server past 256 units. I’ve seen Roblox games such as Normal RTS and Medieval RTS be able to handle a lot more units and work a-okay (for the most part).
Is there really a way to optimize this further, without my detection being biased?
(For the code sample, I’m sending the entire thing but you only need to look at TargetLogic for attack and the while true do loop.)
https://gyazo.com/90b113be310f61c76d36657290fb89e4
https://gyazo.com/5d1084d7b242c87db9b5a0cb0e0e7c88
local target = nil
local teams = workspace.InitializedUnits:GetDescendants()
local function TargetLogic(Unit, TargetType)
if TargetType == "Attack" then
local ovParams = OverlapParams.new()
ovParams.MaxParts = 10 -- fidelity
ovParams.CollisionGroup = "Units"
ovParams.FilterType = Enum.RaycastFilterType.Exclude
ovParams.FilterDescendantsInstances = Unit.Parent.Parent:GetChildren()
local thing = workspace:GetPartBoundsInRadius(Unit.Position, 10, ovParams)
for i, Target in thing do
local EnemyMovementType = Target.Parent.Detection.Movement.Value -- The type of plane the target is travelling on (i.e, air, ground)
local TargetDetection = Unit.Parent.Detection.TargetDetection.Value -- What the unit can detect (i.e, it detects air, ground, or both)
if TargetDetection == EnemyMovementType or TargetDetection == "AttackAll" then -- If it just has the thing "AttackAll", there's no need to check the other variables, this is bruteforce
Target = thing[1]
return Target
end
end
end
if TargetType == "Heal" then
local ovParams = OverlapParams.new()
ovParams.MaxParts = 1 -- fidelity
ovParams.CollisionGroup = "Units"
ovParams.FilterType = Enum.RaycastFilterType.Include
ovParams.FilterDescendantsInstances = Unit.Parent.Parent:GetChildren()
local thing = workspace:GetPartBoundsInRadius(Unit.Position, 10, ovParams)
for _, Target in thing do
local EnemyMovementType = Target.Parent.Detection.Movement.Value -- The type of plane the target is travelling on (i.e, air, ground)
local TargetDetection = Unit.Parent.Detection.TargetDetection.Value -- What the unit can detect (i.e, it detects air, ground, or both)
if TargetDetection == EnemyMovementType or TargetDetection == "AttackAll" then -- If it just has the thing "AttackAll", there's no need to check the other variables, this is bruteforce
end
end
end
if TargetType == "Build" then
local ovParams = OverlapParams.new()
ovParams.MaxParts = 1 -- fidelity
ovParams.CollisionGroup = "Units"
ovParams.FilterType = Enum.RaycastFilterType.Include
ovParams.FilterDescendantsInstances = Unit.Parent.Parent:GetChildren()
local thing = workspace:GetPartBoundsInRadius(Unit.Position, 10, ovParams)
for _, Target in thing do local EnemyMovementType = Target.Parent.Detection.Movement.Value -- The type of plane the target is travelling on (i.e, air, ground)
local TargetDetection = Unit.Parent.Detection.TargetDetection.Value -- What the unit can detect (i.e, it detects air, ground, or both)
if Target.Parent:GetAttribute("Status") == "Building" and Target.Parent:GetAttribute("BuilderCount") < Target.Parent:GetAttribute("buildMax") then
end
end
end
end
local function Attack(Unit)
local Target = TargetLogic(Unit, "Attack")
if Target and Target.Parent and Unit and Unit.Parent and
Unit.Parent:GetAttribute("VfxFired") == false then
Unit.Parent:SetAttribute("VfxFired", true)
Target.Parent:Destroy()
task.wait(Unit.Parent:GetAttribute("FireDelay"))
if Unit and Unit.Parent then
Unit.Parent:SetAttribute("VfxFired", false)
end
end
end
local function Heal(Unit)
local Target = TargetLogic(Unit,"Heal")
if Target and Target.Parent and Unit and Unit.Parent then
print("Healing sucessful")
end
end
local function Build(Unit)
local Target = TargetLogic(Unit,"Build")
if Target and Target.Parent and Unit and Unit.Parent then
if Unit.Parent:GetAttribute("VfxFired") == false then
Unit.Parent:SetAttribute("VfxFired", true)
if Target.Parent:GetAttribute("BuilderCount") < Target.Parent:GetAttribute("buildMax") then
Target.Parent:SetAttribute("BuilderCount", Target.Parent:GetAttribute("BuilderCount") + 1)
print(Unit.Parent)
task.wait(Unit.Parent:GetAttribute("fireDelay"))
Unit.Parent:SetAttribute("VfxFired", false)
end
end
end
end
while task.wait(0.35) do
for _, SelectUnit in teams do
if SelectUnit:IsA("Model") then
local Unit = SelectUnit.PrimaryPart
if Unit and Unit.Parent then
if Unit.Parent:GetAttribute("Status") == "Attacker" then
task.spawn(function()
Attack(Unit)
end)
end
if Unit.Parent:GetAttribute("Status") == "Healer" then
task.spawn(function()
Heal(Unit)
end)
end
if Unit.Parent:GetAttribute("Status") == "Builder" then
task.spawn(function()
Build(Unit)
end)
end
end
end
end
end