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

How to properly use the methods Roblox has provided, namely :FindFirstChild() and :WaitForChild()? [closed]

Asked by 5 years ago
Edited 5 years ago

In this question, I will be focusing mainly on :FindFirstChild() and :WaitForChild(). Often I am confused and uncertain as to whether or not I should use one, or neither, of the listed methods. Because :FindFirstChild() returns nil when the object is not found, I am sure that I should not use it like this:

local box = game.Workspace:FindFirstChild("Model").box

Because :FindFirstChild() returns nil, the variable definition will error. If I am doing it that way, why not just do:

local box = game.Workspace.Model.box

I can see using :FindFirstChild() if the name has spaces, but I could just do something like this:

local box = game.Workspace["Box Model"].box

If I want to use :FindFirstChild() without it erroring, I end up needing to do checks all along the way like this:

local boxModel = game.Workspace:FindFirstChild("Box Model")
if boxModel then
    local boxHolder = boxModel:FindFirstChild("Box Holder")
    if boxHolder then
        box = boxHolder:FindFirstChild("box")
        if box then
            -- code
        end
    end
end

That method just seems extremely inefficient. Another thing I have done to be careful is this:

local players = game:GetService("Players")
players.PlayerRemoving:Connect(function(player)
    local canSave = player;FindFirstChild("CanSave")
    if canSave and canSave.Value then -- canSave is a BoolValue
        --code
    end
end

There must be a better or more efficient and intelligent way to go about using :FindFirstChild().


I have been told :WaitForChild() is for the client when waiting for things that have not spawned in yet. This makes sense, until I talked to someone on the discord (this is the code that was in the discussion):

local text = script.Parent 
local players = game:GetService("Players")
local player = players.LocalPlayer
local contentProvider = game:GetService("ContentProvider")
local loaded = player:WaitForChild("Loaded")
local connection
while contentProvider.RequestQueueSize > 0 and not loaded.Value do
    for i = 1, 3 do
        text.Text = "Loading"..string.rep(".",i)
        wait(.5)
    end
end
local backpackDisplay = player.PlayerGui.StatsGui.Backpack.Display
require(backpackDisplay) 
text.Parent.Visible = false

The backpackDisplay was not working correctly and I was uncertain why because the loop should have waited till everything had loaded in. After asking on the ScriptingHelpers discord I was informed that RequestQueueSize did not count GUI stuff, but only assets. In order to fix this the person recommended using :WaitForChild, which made sense. I then became confused as to where I should place the :WaitForChild. I tried doing this and it worked:

local text = script.Parent 
local players = game:GetService("Players")
local player = players.LocalPlayer
local contentProvider = game:GetService("ContentProvider")
local loaded = player:WaitForChild("Loaded")
local connection
while contentProvider.RequestQueueSize > 0 and not loaded.Value do
    for i = 1, 3 do
        text.Text = "Loading"..string.rep(".",i)
        wait(.5)
    end
end
local backpackDisplay = player.PlayerGui:WaitForChild("StatsGui").Backpack.Display
require(backpackDisplay) 
text.Parent.Visible = false

I went back and asked the guy who answered who answered my question originally and he said that was all that was needed. Why did I not need to do:

local backpackDisplay = player.PlayerGui:WaitForChild("StatsGui"):WaitForChild("Backpack"):WaitForChild("Display")

or, as one of the options I listed with the :WaitForChild(), why not do this:

local statsGui = player.PlayerGui:WaitForChild("StatsGui")
if statsGui then
    local backpack = statsGui:WaitForChild("Backpack")
    if backpack then
        local display = backpack:WaitForChild("Display")
        if display then
            require(display)
        end
    end
end

All of these questions berate me whenever I am writing scripts involving objects, which as you may guess, is pretty much every time I script in Roblox Studio. Any help would be much appreciated. Thanks!

0
there should be more questions like this :/ theking48989987 2147 — 5y
0
^ LOL I AGREE greatneil80 2647 — 5y
0
Ew, nested conditionals. The 'and' operator and 'or' operators exist. User#19524 175 — 5y
0
Yes, but those don't work in the situation that I pointed out. Also, I used and in my canSave example. User#21908 42 — 5y

Locked by User#19524

This question has been locked to preserve its current state and prevent spam and unwanted comments and answers.

Why was this question closed?

2 answers

Log in to vote
3
Answered by 5 years ago

FindFirstChild is only necessary when you want to confirm that an object actually exists. If you directly index it via ., it will error. FindFirstChildwould just return nil. In your first example, you do

local box = game.Workspace:FindFirstChild("Model").box

This is rather pointless. Because you index box without any additional checks to verify Model exists, you are assuming that it will be there. The better way to write this is

local box = game.Workspace.ModelFindFirstChild("box")

The second example using brackets is fine, and its a valid point. Your third example can be simplified, although it is not recommended

local boxModel = game.Workspace:FindFirstChild("Box Model")
if boxModel then
    local boxHolder = boxModel:FindFirstChild("Box Holder")
    if boxHolder then
        box = boxHolder:FindFirstChild("box")
        if box then
            -- code
        end
    end
end

If you know the object exists but don't know when it will exist of you're afraid that it won't when you attempt to index, use WaitForChild. If you are this unsure about an object existing at every stage, you can do one of two things.

1) Rewrite something else so that you don't run into such a large issue 2) Use the second recursive parameter of FindFirstChild

local boxModel = game.Workspace:FindFirstChild("Box Model")
if boxModel then
    local box = boxModel:FindFirstChild("box", true)
    if box then
        --do code
    end
end

I don't recommend this because you could either have multiple instances with the same name or have a massive model which could make this time-consuming.

As for WaitForChild, up until recently, I would've only recommended that it be used on the server because the scripts load before ReplicatedStorage does. In a more recent update (i think this is what occurred), the player now loads before any scripts do. This changes a few things that I don't know a whole lot about yet, so take a look at the article here

Hope this answer explained some of what you were looking for!

Ad
Log in to vote
0
Answered by 5 years ago
Edited 5 years ago

I, myself am somewhat of a newbie when it comes to built in functions of Roblox Lua, but I think one of the purposes, or at least one of the ways I use :FindFirstChild() is when there is a bit a latency, or if I am waiting a set about of time after an event to do something, say damage a person with a sword after an animation has played, or checking if the player has a creator tag in them when they die: Ex1:

game.Players.PlayerAdded:Connect(function(plr)
    plr.CharacterAdded:Connect(function(char)
        local creator = plr:FindFirstChild("creator")       
        if creator then
            if creator.Value and  then
                local leaderstats = creator.Value:FindFirstChild("leaderstats")
                if leaderstats  and leaderstats:FindFirstChild("points") then
                    local points = leaderstats:FindFirstChild("points")
                    points.Value = Points.Value + 1
                end
            end 
        end
    end)
end)--the creator tag is an object value and the value of it is a plr object

Ex2:

--one script
wait(2)
local thing = Instance.new("Part")
thing.Parent = workspace
thing.Name = "brick"
--another script
local thing
repeat
thing = workspace:FindFirstChild("brick")
until thing ~= nil--not needed but to prove a point

This brings me to the second usage of FindFirstChild, it allows you find the child of some object without throwing an error if it isn't there, say I have a typo on a bit of code like this :

local a = Instance.new("Part")
a.Parent = workspace
a.Name = "thing"

if workspace.thkng then--throws an error

end

alternatively, if I have a typo on a bit of code like this

local a = Instance.new("Part")
a.Parent = workspace
a.Name = "thing"

if workspace:FindFirstChild("thkng") then--doesn't throw an error
    print("yay")
else
    print("uh oh")
end

In this aspect, it is superior to WaitForChild and the .as it doesn't infinitely wait for "thkng" and allows the rest of your script to go on undisturbed.

--if WaitForChild() is used without a timout argument
local a = Instance.new("Part")
a.Parent = workspace
a.Name = "thing"

if workspace:WaitForChild("thkng") then--waits forever and doesnt let the rest of the script run
    print("yay")
else
    print("uh oh")
end

However, In some areas, WaitForChild() is better, such as trying to get the humanoid of a player character in a local script;

local humanoid = game.Players.LocalPlayer.Character:WaitForChild("Humanoid")

or in the script i mentioned earlier on

--one script
wait(2)
local thing = Instance.new("Part")
thing.Parent = workspace
thing.Name = "brick"
--another script
local thing = workspace:WaitForChild("brick")
0
Why not use WaitForChild on example 2? hiimgoodpack 2009 — 5y
0
I did in a separate part, just without the wait(2), theking48989987 2147 — 5y
0
I understand that example 2 was highly inefficient, but it still probably works theking48989987 2147 — 5y
0
Don't use code that just works, use code that is both good code and works User#19524 175 — 5y