Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

A way to get only one child that fulfills the conditions from a 'for i,v' loop?

Asked by 4 years ago
Edited 4 years ago

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

2 answers

Log in to vote
1
Answered by 4 years ago

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:

  • Find the closest valid target
  • Determine if the current target is out of range (useful if you want the unit to keep firing on the same target until it dies or goes out of range)
  • Damage the current target (and apply the beam effect)

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:

  • You should make sure hit is not nil before accessing its .Parent
  • In the future you might consider looking into using ModuleScripts and Object Oriented Programming so that you don't need to copy scripts around. You can attach functionality to a unit and so express concepts in your code like if thisUnit:CanAttack(v) then thisUnit:SetTarget(v) end
  • There are a number of efficiency improvements to be made. ex, if the current unit is on a neutral team, there is no need to run the while loop at all (unless it can switch teams later on, in which case it should wait for its team to be non-neutral before starting the while loop).
0
Thank you! I've been meaning to look around OOP and ModuleScripts (and also matrices, but that's math) but I keep procrastinating. radiant_Light203 1166 — 4y
Ad
Log in to vote
0
Answered by 4 years ago
Edited 4 years ago

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

0
but he did GetChildren ... Luka_Gaming07 534 — 4y
0
I'll make it clear in the post but thisUnit is the unit that's looking to shoot an enemy. radiant_Light203 1166 — 4y

Answer this question