I have the habit of using FindFirstChild whenever I’m trying to get the child of something, is it necessary? I see a-lot of people who don’t do it but when I was learning to script every tutorial I watched they would use it too.
I think it’s useful for avoiding errors. I also use WaitForChild
.
FindFirstChild is sometimes necessary but you should not use it if you don’t have to since it has big performance issues. WaitForChild is a good replacement for times when you are sure that it is going to be a child.
It is really necessary when trying to verify if an instance exists to execute the following actions, otherwise it will not. It will return the instance if FindFirstChild
finds the child, otherwise it will return nil
.
This is somewhat misleading, it’d be more accurate to say that indexing a child is faster than using :FindFirstChild()
For example:
local part = workspace.Part;
Is slightly faster than
local part = workspace:FindFirstChild("Part");
You’ll only see this difference if you’re calling :FindFirstChild()
many times in short intervals, like a loop.
yes bec when u use FindFirstChild it gets the object first without having to wait
FindFirstChild
is only needed for an if statement. Otherwise you should only directly index children. That is of course with the exception of waiting for a child to exist via WaitForChild
, which is necessary if dealing with instances at the start of the game.
Actually FindFirstChild
does have a use with its second argument, if set to true. This will perform a recursive search that will check all descendants instead of just the immediate children. This is the only other use-case I see.
Here’s my attempt at summarizing it all.
FindFirstChild is best for checking whether or not the child actually exists, when whether it does exist varies, so the script can handle both cases. This mainly applies to objects that can be destroyed and replaced. I also used to rely on it for getting children with names containing spaces before Parent[“Child Name”] became a thing, or at least, before I learned of it, but that no longer is necessary.
If you expect the child to exist at all times, FindFirstChild isn’t necessary at all. If the child is in a client-sided location, you likely should use WaitForChild in case it hasn’t loaded in yet, because the client isn’t guaranteed to have everything ready before scripts begin to run. This also applies in any other case where you expect a child to exist, but not immediately.
performance dependent code.
Roblox agrees with me here. Once again, saying it takes 20% longer can paint the picture that it can take a noticeable amount of time to compute when in reality, it’s a hundredth of a second difference in time.
WaitForChild
is not limited to the client. It should be used any time a script is expected to run on start. It should also be used for PlayerAdded
and CharacterAdded
, or any other event in which a more complex model is added. The only time this wouldn’t be needed in the case of a complex model being added is if it’s a clone on the same thread.
Interesting. So far I have rarely if ever needed to use it elsewhere to avoid errors, but I will keep that in mind. Thanks!
I probably should add that FindFirstChildOfClass is another option, when and where one expects the object in question to be the only of its class within its parent. I use this for Humanoids nowadays. Granted, I no longer rely on differently-named humanoids to differentiate player characters’ humanoids from enemies’ humanoids in combat games, but it can help. It’s definitely useful for running commands in Studio.
In an ideal world you would never be allowed to index children without FindFirstChild. Since that ship has sailed, it’s up to you. It’s not particularly necessary but it could help you dodge some trouble that you might get when using dot syntax as the first post mentioned.
The thing about Roblox is that dot syntax can be used to access both properties and children so these often find themselves with namespace conflicts. You can take a look at a few controversial property updates and code support questions – since instances may share the same name as a property and properties have precedence when indexing, you might unexpectedly get the wrong thing.
Despite this, don’t let the “20% slower” comment scare you. Even if it’s slower by a significant margin, you should consider other factors like how many children are in the instance you’re using FindFirstChild on (or descendants in the case of setting the second argument to true). The engine can still execute FindFirstChild fast.
So is it necessary? We’re not in that ideal world, so no. It may also be unreadable to you or your collaborators (daisy chaining still has the same problem as dot syntax if a step in the chain is nil). Your use case for FindFirstChild will boil down to indexing dynamically created instances you can’t guarantee will exist when the code runs (i.e. character’s Humanoid).
Find first child shouldn’t be used in all situations due to the fact it takes 8 times longer.
Here’s a summary of what it does in case you’re wondering though.
Find First Child will return a value of nil if the object doesn’t exist, which is quite useful at avoiding errors but isn’t necessary to be used constantly as a habit. If the object does exist, it will return the value of the object.
For example:
local hi = workspace:FindFirstChild("bye")
if hi then
print("bye!")
end
Basically, in a short sentence, it just checks if the object exists.
WaitForChild() just waits for the object to load before continuing the script, when exceeded 5 seconds, will print a warn message.
The wiki says it’s worse than FindFirstChild
Use FindFirstChild for something like if he has keys, or touched to see if it has humanoid, etc.
It really depends, I only use FindFirstChild() if it is a temporary Child, else if it is a permanent Child I don’t use it.
@yname I figure you would also like to know about these results.
So I’ve done some testing and have come across some interesting results.
At 50 million calls for a part asdfghjklkjhgfdsa
, WaitForChild is consistently faster than FindFirstChild @regexman
19:19:10.069 Testing Benchmark Results: - Client - Timer:59
19:19:10.069 Timer WaitFor: 9.072540699969977 secs - Client - Timer:22
19:19:10.070 Timer FindFirst: 9.315383000299335 secs - Client - Timer:22
Over 120 million calls for that same name, dot tends to be faster than bracket
19:26:01.843 Testing Benchmark Results: - Client - Timer:59
19:26:01.843 Timer Bracket: 8.576460700016469 secs - Client - Timer:22
19:26:01.843 Timer Dot: 8.532256300095469 secs - Client - Timer:22
sometimes bracket wins, other times dot wins.
If you are wondering how the dot/brack and the finder functions compare over the same 50mil calls
19:37:56.623 Testing Benchmark Results: - Client - Timer:59
19:37:56.624 Timer Bracket: 7.963528700172901 secs - Client - Timer:22
19:37:56.624 Timer Dot: 7.975315900053829 secs - Client - Timer:22
19:37:56.624 Timer WaitFor: 9.235567100346088 secs - Client - Timer:22
19:37:56.624 Timer FindFirst: 9.557014799676836 secs - Client - Timer:22
Now I’ve got something really interesting for you. What if we just look for a part a
? Same # of trials, just a shorter name:
19:41:57.714 Testing Benchmark Results: - Client - Timer:59
19:41:57.714 Timer Bracket: 3.4464027998037636 secs - Client - Timer:22
19:41:57.714 Timer Dot: 3.417652999982238 secs - Client - Timer:22
19:41:57.714 Timer WaitFor: 4.808735299855471 secs - Client - Timer:22
19:41:57.714 Timer FindFirst: 5.248661700170487 secs - Client - Timer:22
A consistent ~50% performance increase over asdfghjklkjhgfdsa
My test code:
local Bench = require(game.ReplicatedStorage.Timer).NewBench("Testing")
wait(5)
local trials = 5e7
--asdfghjklkjhgfdsa
Bench:NewTimer("Bracket"):Start()
for i=1,trials do
local thing = workspace["a"]
end
Bench:StopCurrentTimer()
wait()
Bench:NewTimer("Dot"):Start()
for i=1,trials do
local thing = workspace.a
end
Bench:StopCurrentTimer()
wait()
Bench:NewTimer("WaitFor"):Start()
for i=1,trials do
local thing = workspace:WaitForChild("a")
end
Bench:StopCurrentTimer()
wait()
Bench:NewTimer("FindFirst"):Start()
for i=1,trials do
local thing = workspace:FindFirstChild("a")
end
Bench:StopCurrentTimer()
wait()
Bench:Results()
I’m pretty sure you will never call WaitForChild 50 million times ever in game development right?