2

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
Jakye
  • 6,440
  • 3
  • 19
  • 38
GAM II
  • 45
  • 8

1 Answers1

3

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
Kylaaa
  • 6,349
  • 2
  • 16
  • 27
  • Wow, thanks! I had been thinking about using IsA but wasn't sure it would help. I had originally used a regular for loop instead of in pairs. Still not entirely certain of when to use a regular vs in pairs vs in ipairs. Can you explain, please? Does it have to do with what ipairs can handle that pairs can't? – GAM II Apr 09 '21 at 18:33
  • 2
    @GAMII `ipairs` is for an "array": when the (interesting) keys of the table are numbers starting from `1` and counting up. – aschepler Apr 09 '21 at 18:39
  • 1
    [Here's a great rundown of the differences between the two](https://stackoverflow.com/questions/55108794/what-is-the-difference-of-pairs-vs-ipairs-in-lua) Generally in lua, tables can be indexed with numbers like an array, or indexed with any other kind of object like a dictionary or associative array. It's important never to mix the two in a single table or you'll get crazy results. When you use `ipairs()` you are saying that you expect the table to act like an array. `pairs()` is generally more flexible and will work with arrays or dictionaries, but it helps with readability. – Kylaaa Apr 09 '21 at 18:43
  • Not really used to Stack Overflow, so I'm not sure if @Kylaaa or aschepler gave me the solution...:( It seems like it was Kylaaa, and aschepler added the comment about the parts of the character. The latter brings me to ask whether or not some parts of the character would require GetDescendants instead? Also, is the function(otherPart) a system function? I cut and pasted what you gave me but it didn't work. :( – GAM II Apr 09 '21 at 18:45
  • 1
    When you connect to the `Touched` event, it calls the function like this : `brick.Touched:Connect(function(otherPart)`. I have just moved that function to a place where it has access to the original brick as well.... Hmmm. You may be running into an issue where the character model is touching the part multiple times, and this might be impacting the fade animation. Some debounce might be needed. – Kylaaa Apr 09 '21 at 18:51
  • The parent of HardPath is the group "Tunnel of death", which is in the Workspace. Lots of errors, LOL! but most of them seem to be from other parts of the game. Not sure which it might be. – GAM II Apr 09 '21 at 18:56
  • I'm not sure what from the output you'll need. Just errors and warnings? It's too long if I post everything. – GAM II Apr 09 '21 at 18:58
  • @Kylaaa I'm not seeing bouncing. How do I proceed? – GAM II Apr 09 '21 at 19:01
  • 1
    When you say something isn't working, you need to be a little more specific. is the specific script throwing errors? The Output window will let you know where an error originated. Is nothing happening at all? Is the animation playing? Do errors appear when you touch a part? Do errors appear when you start running the game? – Kylaaa Apr 09 '21 at 19:02
  • @Kylaaa  15:03:23.034 touched is not a valid member of Model "Workspace.Timed jump.1st platform" - Server - Move on touch:2   15:03:23.204 Touched is not a valid member of Model "Workspace.Tunnel of death.EasyPath" - Server - Disappearing Brick Script:30   15:03:23.218 The Parent property of Workspace is locked - Server - Importer:7   15:03:33.024 Downloading asset failed for asset id 5718725404. Is the asset id correct and is the asset type "Model"? - Server - Script:2   15:03:34.944 ▼ ActivateCameraController did not select a module. (x2) - Client - CameraModule:362 – GAM II Apr 09 '21 at 19:13
  • 15:03:41.081    ActivateCameraController did not select a module.   15:03:41.846 Something unexpectedly tried to set the parent of Script to 4D Being while trying to set the parent of Script. Current parent is 1st Stage Pathway. That last error I have for every grouping of my game. No errors when I step on the test brick. No animation. Nothing happens - it acts like a normal brick. – GAM II Apr 09 '21 at 19:14
  • One more error listed:   15:03:42.476 Anim is not a valid member of LocalScript "Mobile" - Studio – GAM II Apr 09 '21 at 19:14
  • 1
    I've tested my code in a fresh place and it's working as expected without errors. Try using the latest code. – Kylaaa Apr 09 '21 at 19:20
  • I'm sorry, what latest code? If you mean the code in your original message, I did that. If it's something else, I'm not sure what you mean. – GAM II Apr 09 '21 at 19:33
  • I saw that you'd changed something in the code and now it works. Thanks so much! Good work, Kylaaa! I'm sorry I'm so daft - I've been out of programming for quite some time and have lost my edge. :( That isFading check was a good idea, I'm sure! I still don't really understand "return function(otherPart)". Is "otherPart" a system function that you use to determine which part touched the brick, or does it find the correct child? Out of curiosity, since it won't fade a brick that is fading, why use "brick.CanCollide = true" before the for loop? Is that a "just in case" safeguard? – GAM II Apr 09 '21 at 20:24
  • I think I should probably run through some LUA tutorials. Do you have any suggested series? – GAM II Apr 09 '21 at 20:24
  • 1
    In your original code, you defined `function onTouched(brick)`. In this case, `brick` is just a variable that refers to the object returned when the `BasePart.Touched` event fires. This is a bad name for the variable, because `BasePart.Touched` returns the other object, not the original brick. So I renamed it to `otherPart` to clarify the variable's meaning. I then modified your code so that your original `function onTouched(brick)` is now returned when you call the `createOnTouched` function. This way you can pass in a brick reference and have custom logic that fires when it is touched. – Kylaaa Apr 09 '21 at 20:38
  • 1
    I moved `brick.CanCollide = true` outside the for loop because it doesn't need to be called multiple times, which was happening in your original code. – Kylaaa Apr 09 '21 at 20:39
  • 1
    For tutorials, check out The [Lua 5.3 Manual](https://www.lua.org/manual/5.3/manual.html), and for Roblox stuff there are great tutorials on the [Developer Hub](https://developer.roblox.com/en-us/onboarding). – Kylaaa Apr 09 '21 at 20:41
  • @Kylaaa Thanks again! Really appreciate the support!!! – GAM II Apr 09 '21 at 20:55
  • @Kylaaa A few questions: 1) I see that you edited your answer in October. What did you change? 2) Why isn't "isFading" introduced and set above the function? How does the – GAM II Oct 25 '21 at 15:42
  • @Kylaaa (continued) This seems to make it impossible that line 8 will ever trigger. 3) What exactly is "return function" (line 6)? Is it just returning "otherPart"? If so, why is that necessary, and what is otherPart being sent to? 4) Why was it necessary to call the function from within the onTouched variable? Why not just call the function directly? Thanks for explaining! – GAM II Oct 25 '21 at 15:57
  • 1
    You can see the history of a post by clicking on the "edited" link. I only fixed some JavaScript syntax in the explanation. The 'createOnTouched' function returns a function. That returned function gets called any time you touch the brick. We do this because the original OnTouched event listener only gives you access to the part you touched, and not the original brick. So by constructing an onTouched function ahead of time with the helper function, we can get access to both objects. – Kylaaa Oct 25 '21 at 16:35
  • @Kylaaa Ok, I guess I don't fully understand but I understand enough. ;) One more thing you missed: 2) Why isn't "isFading" introduced and set above the function? This seems to make it impossible that line 8 will ever trigger. – GAM II Oct 25 '21 at 20:17
  • 1
    isFading is introduced outside the function but it is still in [the same scope](http://lua-users.org/wiki/ScopeTutorial), this allows for the value to be persisted across multiple function calls. If isFading was defined inside the returned function, its value would be reset every time the function was called and it would not be able to debounce multiple requests. – Kylaaa Oct 25 '21 at 20:58
  • @kylaaa I find this confusing because the Roblox LUA tutorial puts the initial debounce var before the function: developer.roblox.com/en-us/onboarding/fading-trap/2. – GAM II Oct 26 '21 at 03:51
  • 1
    That tutorial is an example of 1 script for 1 object. This question is asking how to make a script work for all its children, so 1 script to many children. By putting `isFading` inside the `createOnTouchedFunc` function, and outside the returned function, we can create a brand new `onTouched` function with its own debounce logic every time we call the `createOnTouchedFunc` function. – Kylaaa Oct 26 '21 at 06:24
  • Thanks, Kylaaa! That makes sense! – GAM II Oct 26 '21 at 14:41