I have a unit which cycles through all the children of workspace that fulfill the if conditions states after the for loop. Then, if those conditions are met, it will fire a ray and attach a beam to deal damage.
The problem is that the ray is often fired more than once in succession at all the units in its range. I've tried using a break after the damage is dealt but that makes it so that damage is dealt in rapid succession to only one unit.
My question is whether my method of using a for i,v in pairs loop to search for a unit is good or whether there is another way to find a single unit.
I've attached the function itself below.
function attackShoot() for i,v in pairs(workspace:GetChildren()) do -- thisUnit is the unit trying to shoot. if thisUnit.Configuration.Team.Value ~= 0 and -- not neutral thisUnit.Configuration.Team.Value ~= -1 and -- not dead v:FindFirstChild("Configuration") and -- is a unit v.Configuration.Team.Value ~= 0 and -- not neutral v.Configuration.Team.Value ~= -1 and -- not dead v.Configuration.Team.Value ~= thisUnit.Configuration.Team.Value and -- not in the same team v.Configuration.UnitType.Value == "infantrySupport" and -- infantry cannot fight vehicles (v.Torso.Position - thisUnit.Torso.Position).Magnitude <= range -- checks if in range then local ray = Ray.new(thisUnit.Torso.Position, (v.Torso.Position - thisUnit.Torso.Position).Unit * range) local hit, pos = workspace:FindPartOnRay(ray, nil, false, true) if hit.Parent:FindFirstChild("Humanoid", false) then -- checks if enemy is in LOS inLOS = true elseif not hit.Parent:FindFirstChild("Humanoid", false) then inLOS = false break end if hit.Name == "ray" then wait(math.random(1.9, 3.9)) end if inLOS == true then thisUnit.Torso.Beam.Attachment1 = v.Torso.Attachment end wait(0.1) thisUnit.Torso.Beam.Attachment1 = nil if hit and hit.Parent and hit.Parent.Humanoid then hit.Parent.Humanoid:TakeDamage(math.random(1.9, 3.9)) end break end end end end while true do wait(math.random(1.9, 3.9)) inLOS = true -- resets inLOS attackShoot() end
I believe the bug you are currently experiencing is because you're trying to put all your logic in your attackShoot
function. Instead, you should break your problem into smaller steps; a good way to do this is to use more functions, each with a single task. For example, you could have a function for each of the following jobs:
The logic of each part of your script will become clearer if you do so.
Your script could then look like this (note: you have to fill in some spots):
local function targetIsValid(target) -- return true if target is in range, in line of sight, is on an enemy team, etc -- TODO end local function findClosestValidTarget() local closestTarget local closestDist for i, v in ipairs(workspace:GetChildren()) do if targetIsValid(v) then local dist = -- TODO get distance if not closestDist or dist < closestDist then closestDist = dist closestTarget = v end end end return closestTarget end local function performAttack(target) -- TODO set up the beam and deal damage to the target end local curTarget while true do wait(math.random(1.9, 3.9)) if not curTarget or not targetIsValid(curTarget) then -- if there is no target or if the current target is no longer valid (out of range or dead or changed teams or out of line-of-sight) curTarget = findClosestValidTarget() end if curTarget then performAttack(curTarget) end end
Other tips:
hit
is not nil before accessing its .Parent
if thisUnit:CanAttack(v) then thisUnit:SetTarget(v) end
The Only Answer is GetChildren() but you can use others like FindFirstChild("YourName") but remember if this is a string dont put .Value nor .Name or it would result to an error but if its objects like attachments then its like other values but you use the term .Name instead of .Value and i cant see where thisUnit came from