Can I use game.Loaded to avoid using WaitForChild?


#1

My client code is littered with WaitForChild invocations. The reason you have to do this is because you can’t guarantee that everything is loaded when the script first executes.

My question: Using game:IsLoaded() and game.Loaded, could you avoid using WaitForChilds?

For instance:

if (not game:IsLoaded()) then game.Loaded:Wait() end

-- Dot-notation without WaitForChilds:
local gui = game.Players.LocalPlayer.Gui
local frame = gui.Some.Item.Nested.Deep.Down

In most cases, my client code is not required to execute immediately; I can wait for the game to load without causing any issue.


Note: I am not talking about cases when the client is waiting for an object that the server adds in later. I’m strictly talking about items replicated when the player first joins the game. For instance, GUI objects within StarterGui.


#2

According to the Roblox Developer Hub: " Unless they are parented to ReplicatedFirst , LocalScripts will not run prior to this event firing."

So, note that the only use of waiting for the game to load would be when you’re working with a LocalScript inside ReplicatedFirst. From the fact that regular LocalScripts run after the game has loaded anyway, we can probably conclude that this will not remove the necessity of using Instance:WaitForChild a lot.


#3

That’s unfortunate, dang.

I wish there was a way to explicitly mark a LocalScript to not execute until everything has loaded.


#4

It’d be nice to have a clear explanation of the difference between “when all initial Instances in the game have finished replicating to the client” (which is what DataModel.Loaded claims to wait for) and “when all initial Instances in the game have been properly parented”.


#5

The only things you should be using Instance:WaitForChild() for is Player.PlayerGui and top-level ScreenGuis.

No, you can’t use game.Loaded to avoid that. GUIs are not cloned until the character spawns from game.StarterGui.

I recommend ditching game.StarterGui entirely so as not to wait on the player’s character, and instead just clone them all from a folder in game.ReplicatedStorage.


#6

You can do something like this on the server and just wait for the Loaded bool value since it was created last it will load in last.

local StarterGuiObjects = {}
for _,x in ipairs(game.StarterGui:GetChildren())do
	x.Parent = nil
	table.insert(StarterGuiObjects, x)
end


local function PlayerAdded(player)
	local playerGui = player:WaitForChild("PlayerGui")
	for _,x in ipairs(StarterGuiObjects)do
		x:Clone().Parent = playerGui
	end
	local loaded = Instance.new("BoolValue")
	loaded.Name = "Loaded"
	loaded.Parent = playerGui
end

for _,x in ipairs(game.Players:GetPlayers())do
	coroutine.wrap(PlayerAdded)(x)
end
game.Players.PlayerAdded:Connect(PlayerAdded)

#7

Someone just posted a community resource which could be the solution to your problem :joy:


Probably mainly useful for Guis

[Haven’t tested it myself right now, but from the way he has set it up, seems to work quite well]


#8

That’s the thread that spawned my idea for this topic. I figured this would be way easier, but I guess it doesn’t work.


#9

I put this at the beginning of all my client scripts. Never seen an error since.
image


#10

I think just putting wait() at the beginning of the LocalScript is enough.

Otherwise, The only thing that occurs to me so far is a LocalScript in PlayerGui that waits for all children to load and then creates an object called “Loaded” or else you change the name or disable etc, then in the rest of the scripts a line at the beginning like this.

game.Players.LocalPlayer:WaitForChild("Loaded")

You can make a Plugin that generates a script with a WaitForChild for each object in the StarterGui.


#11

Wiki:
Instance:WaitForChild, a function which can be used to wait for an individual Instance to replicate without having to wait for the whole game to.

Unless they are parented to ReplicatedFirst, LocalScripts will not run while the game has not loaded.

This would imply that waiting for game to load is enough.

I’ve rarely had any issues with referencing Instances in LocalScripts instantly.

Some tests in studio:

Game loaded: 1547240178.5973
LocalScript started 1547240180.0001
Difference: 1.4027998447418

Parenting 100 GuiObjects to Gui and checking how many are loaded:

print(#script.Parent:GetChildren()-1) --> 100

#13

You mean there is no need to use WaitForChild in LocalScripts located in PlayerGui?, because I’m confused with your answer, of course it’s necessary.


#14

In that case there would be no such thing as WaitForChild() to begin with. I belive this is misinformation. Roblox does not guarantee the time or order in which objects are replicated from the server to the client.


#15

They give hints about it, but never explicitly say so:
Unless they are parented to ReplicatedFirst, LocalScripts will not run while the game has not loaded.

Although, it’s probably not safe to reference any object which wasn’t there when game started, as theres a possibility they won’t be loaded when you reference them.


#16

They do accually, sorry for not linking to it, I’ve edited my post to properly redirect now.


#17

That shouldn’t matter, as game.Loaded fires when all of them are replicated.
It doesn’t matter if A gets replicated before B, because you LocalScripts start after they’re both replicated.


#18

Refer to the diagram in this post:

Using the information in this post, I can come up with a system to determine how objects should be accessed:

Use of WaitForChild depends on the location of the invoking script, and whether the target object is

  • “static”: exists before game starts; i.e. stored in the place file.
  • “dynamic”: added after the game starts; opposite of static.

Consider the following rules (subsequent rules override previous rules, when applicable):

  1. Generally, scripts may access static objects directly.
  2. Generally, scripts must use WaitForChild to access dynamic objects.
  3. Scripts under ReplicatedFirst must use WaitForChild to access static objects.
  4. Scripts under ReplicatedFirst may access static objects under ReplicatedFirst directly.
  5. Code protected by game.Loaded may access static objects directly.

Services, as targets, are exempt from these rules as long as they are accessed via GetService.

Also worth noting is that an object being static assumes that the object is never removed during the runtime of the game. A supposedly static object that is removed at any point must be treated as dynamic. Technically, because any object can be removed at any time, even before scripts run, static objects do not exist. But it’s probably safe to assume that the runtime has some integrity, and leaves removing objects only to the developer.

Finally, here’s a handy table:

LocalScript\Target Static Dynamic Static (RF) Dynamic (RF)
General Direct WFC Direct WFC
ReplicatedFirst (RF) WFC WFC Direct WFC
Loaded-Protected Direct WFC Direct WFC

#19

It should be mentioned that this is only for LocalScripts.
Regular scripts still require you to use :WaitForChild on both Static and Dynamic objects.

Loading order times, tested in studio:

  Regular Script: 0
  Local Script inside ReplicatedFirst: 0.1
  Game Loaded: 0.1
  Local Script: 3.1

#20

I think that in Studio everything goes well, but in ROBLOX Player is a little different, I have happened bugs when I do not put WaitForChild in a LocalScript, I have all those errors in my Game Analytics, do not always occur, I think it depends on the performance of the client, never occur in ROBLOX Studio.


#21

The rules can be applied to server scripts (Static -> Direct, Dynamic -> WaitForChild). But there are no nuances like ReplicatedFirst and game.Loaded. Basically, rules 3-onward apply only to the client.
Server scripts can access static server objects directly because they are on the same peer, and are therefore visible from the start.

On the client, the game tree starts out empty, and all static objects replicated from the server are actually dynamic. However, there are procedures and APIs (as described in the referred post) that allow us to treat a subset of these objects (“DataModel” in the diagram) as static, as long as certain conditions are met. The rules were created from these conditions.