Search code examples
for-loopluatouch-eventchildrenroblox

How do I make a script affect all its children in Roblox LUA?


I'm new to programming in LUA, although I've learned similar languages like JS. It's frustrating if I have to alter the same script in many parts in a group by replacing each script, and I don't know of an elegant way to do it. Instead, I decided to nest all the parts inside of the script. I've seen some examples and I've tried to adapt some of them, but they don't exactly apply to what I want to do and I can't get them to work.

In essence, what I'm trying to do is monitor all the bricks for a player to contact them. I took the original disappearing brick script that was nested inside each brick and modified it. If a part (brick) is touched, that should call the onTouch function, which will make the brick's transparency decrease over time until the in pairs loop is done, after which the brick disappears and CanCollide is turned off. After 2 seconds, it then returns back to normal. I think the problem is with the coding I used to monitor the parts as I don't really understand the right way to monitor multiple objects. Can someone please help? Thanks!

File structure:

File structure

function onTouched(brick)
    local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
    local RestoreDelay = 2 -- delay before the brick reappears
    local inc = .1 -- how much the brick disappears each time

    -- All characters have a Humanoid object
    -- if the model has one, it is a character
    local h = script.Child:findFirstChild("Humanoid") -- Find Humanoids in whatever touched this
    if (h ~=nil) then -- If there is a Humanoid then
        h.Health = h.MaxHealth -- Set the health to maximum (full healing)
        for x=0,1, inc do
            script.Child.Transparency = x+inc
            script.Child.CanCollide = true
            wait(delay)
        end
        wait(delay)
        script.Child.Transparency = 1
        script.Child.CanCollide = false
        wait(RestoreDelay)
        script.Child.Transparency = 0
        script.Child.CanCollide = true
    else
    end
end

while true do
    local bricks=script:GetChildren():IsA("basic.part")
    for x=1,brick in pairs(bricks) do
        brick.Touched:connect(onTouched(brick)) -- Make it call onTouched when touched
    end
end
end

Solution

  • For the most part, you've gotten it right, but you've got a few syntax errors where there are different conventions between JavaScript and Lua.

    In JS, you would fetch an array of objects and then bee able to filter it immediately, but in Lua, there is limited support for that. So a JavaScript line like :

    var bricks = script.GetChildren().filter(function(item) {
       return item === "basic.part"
    })
    

    cannot be done all in one line in Lua without assistance from some library. So you'll need to move the check into the loop as you iterate over the objects.

    Other than that, the only other thing to change is the onTouched handler's function signature. The BasePart.Touched event tells you which object has touched the brick, not the brick itself. But by creating a higher order function, it's easy to get access to the brick, and the thing that touched it.

    -- create a helper function to access the brick and the thing that touched it
    function createOnTouched(brick)
        -- keep track whether the animation is running
        local isFading = false
    
        return function(otherPart)
            -- do not do the animation again if it has already started
            if isFading then
                return
            end
    
            local delay = .1 -- the delay between each increase in transparency (affects speed of disappearance)
            local restoreDelay = 2 -- delay before the brick reappears
            local inc = .1 -- how much the brick disappears each time
    
            -- All characters have a Humanoid object, check for one
            local h = otherPart.Parent:FindFirstChild("Humanoid")
            if h then
                -- heal the player
                h.Health = h.MaxHealth
    
                -- start fading the brick
                isFading = true
                brick.CanCollide = true
                for i = 0, 1, inc do
                    brick.Transparency = i
                    wait(delay)
                end
                -- turn off collision for the brick
                wait(delay)
                brick.Transparency = 1
                brick.Anchored = true
                brick.CanCollide = false
    
                -- turn the part back on
                wait(restoreDelay)
                brick.Transparency = 0
                brick.CanCollide = true
    
                -- reset the animation flag
                isFading = false
            end
        end
    end
    
    -- loop over the children and connect touch events
    local bricks = script:GetChildren()
    for i, brick in ipairs(bricks) do
        if brick:IsA("BasePart") then
            local onTouchedFunc = createOnTouched(brick)
            brick.Touched:Connect(onTouchedFunc)
        end
    end