Problem with loading screen

So I followed AlvinBlox’s latest loading screen tutorial (I recommend you watch it if you want to make one, very informative) and made my own modifications.

The expectation
The script from the video worked fine, so I decided to make the text animate by increasing the ellipse in “Loading…” from 1 to 3. After the game loads, the animation is supposed to stop and replaced with “Welcome to [REDACTED]!”, wait a few seconds and tween downwards and then execute the :Destroy() line.

The result
The screen just sticks to the “Loading…” part and doesn’t animate. (By default the text reads “Loading…”)

You can take a look yourself here:
LoadingProblem.rbxl (561.9 KB)

Either you typed it wrong, or this code is broken. The while read == false loop will go infinitely, as ready is never set to true. Yes, it is later in the code, but the while loop will run forever, and the repeat loop will never run.

I suggest wrapping your while loop in a coroutine. That way, it will run without stopping anything else.

coroutine.resume(coroutine.create(function()
     while not ready do
          text.Text = "Loading."
          wait(1)
          text.Text = "Loading.."
          wait(1)
          text.Text = "Loading..."
          wait(1)
     end
     text.Text = "Welcome to [REDACTED]!"
end))

This might not fix your issue, but it’ll fix one of the problems with it

2 Likes

Or you could just spawn it in as a function:

spawn(function()
--While loop here
end)

Now everything should run properly as long as you have your objects defined properly.

  1. Your variable text refers to the one still sitting in script.LoadingScreen, not the one cloned to the PlayerGui
  2. Instead of a game:IsLoaded() poll after the while loop, you can:
  • Make the while loop a coroutine, as mentioned by Xiousa
  • spawning it also works I guess
  • connect a function to the game.Loaded event
    – This can be unreliable if your game loads very quickly, however. Make sure to connect it as soon as possible.

In a lot of cases (like this one), it’d be better to use a coroutine as they execute instantly whereas spawn() doesn’t. This is because spawn() has a built in wait() before it executes the code within it.

3 Likes

Just suggesting alternatives to the problem. Both ways work with minimal delays.

2 Likes

Hey!

I have heard about similiar issues from my friend who also has followed @Alvin_Blox 's tutorial, so will probably investigate it when helping my friend and update the topic. Alvin may also probably want to see what is wrong.

Not sure why this isn’t marked as the solution. It’s exactly the solution this topic needs. OP needs to mark.

I didn’t include the ellipse while loop stuff in the video, but the issue you’re having is indeed because your while loop is going to run forever and ready will never be made true (because it’s stuck in the original loop).

A spawn function will fix this problem, running it synchronously. Agreed, Timothy’s solution should be marked as solution.

Thanks for using my tutorial! :+1:

4 Likes

I just want to stick it in there that DataModel.(Is)Loaded is probably not an accurate way to handle a loading screen. Loading is only used to determine when the server finished replicating instances to the client, it doesn’t actually do any loading of any kind. Kind of irrelevant though.

Instead of using an upvalue bool as a condition, you should make use of the conditional statement where IsLoaded actually belongs. You’re reinventing the wheel by having two loops here.

Based on the code you’ve already provided, here’s a rewrite (not really spoon feeding code because you already have some, but I still would’ve liked to avoid this lol):

-- Typically good to index services first.
local Players = game:GetService("Players")
local ReplicatedFirst = game:GetService("ReplicatedFirst")
local TweenService = game:GetService("TweenService") -- PLEASE use this over the ugly GuiObject tween methods!

-- You'll want your objects next.
local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")
local Gui = script.LoadingScreen -- ReplicatedFirst only runs once per client. Cloning is unnecessary.

-- Get to coding next.
PlayerGui:SetTopbarTransparency(0)
Gui.Parent = PlayerGui -- This is fine to do over cloning.

-- Don't add a wait after. Create your loop next.
-- Remember; MAKE USE OF THE CONDITIONAL STATEMENT.

do -- Open up a new scope so we can have a few local variables
    local dots = 0 -- For later
    local displayText = Gui.Frame.LoadingText
    -- You were previously editing the wrong Gui's text, to note
    while not game:IsLoaded() do
        -- This is an interesting way to do it, and possibly better
        displayText.Text = "Loading" .. string.rep(".", dots % 4)
        wait(1)
    end -- If the loop's done, it'll terminate here
    print("Ready!") -- If you're up for debugging
    displayText.Text = "Welcome to [REDACTED]!"
end -- Clean up scope and local variables we don't need

script.victory:Play() -- You should pick a better place to put this, like the Gui
-- Gui.victory:Play()
wait(5)

do -- Open up a new scope to stick some variables in
    local TweenData = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)
    local Tween = TweenService:Create(Gui.Frame, TweenData, {Position = UDim2.new(0, 0, 1, 0)})
    Tween:Play()
    Tween.Completed:Connect(function ()
        Gui:Destroy()
    end)
end -- Close scope and clean up the variables

-- Code finished!

A little explanation to my code, if you’re curious some:

Why services first?
Later in your code, it can get very tedious to keep on writing out “game:GetService” if you want to access something. This is also a canonical way of fetching services - don’t use dot syntax, since the names of services can change and some of them aren’t even named properly in the DataModel. Services should go first since you may want to use them again later.

Why did you remove the Gui clone?
LocalScripts in ReplicatedFirst are run only once per client and are the first items to be replicated to the client. You can safely parent things out of ReplicatedFirst and it will still work as intended for every other client. Since the Gui exists in a script in ReplicatedFirst, it will also be replicated along with the code. A clone means you’re asking for another copy of the Gui to be made and replicated to the client. You can use the copy that’s already replicated instead and avoid reinventing the wheel unnecessarily here.

Why is your code in “do-end” blocks?
A do-end block:

do
    -- Your code
end

These blocks are nice for executing “throw-away” code, I guess. Think of your entire script being in a do-end block, except you don’t actually write it out. These blocks are created instead so you can store a few local variables that you don’t need anywhere else in your script, it organises your code and nothing runs after it until the code in the do-end block has finished running. It’s pretty beneficial.

Why did you remove the ready value and replace it with IsLoaded?
Like I said, you’re reinventing the wheel by using two loops. The conditional statement is already available for you to use in a while loop, so make good use of it. By putting this here, I’m telling the loop to run until IsLoaded returns something that isn’t false. This is a better alternative and doesn’t require anything like additional threading via coroutines or spawn.

What is the string you wrote in the while loop?
Your original code would always wait 3 seconds explicitly; for every second, a dot would be added. My code truncates that to only add dots every time the loop runs around and the script can terminate at any time, not explicitly only after three dots have been added and IsLoaded doesn’t return false.

Why TweenService?
I don’t feel like explaining, too lazy. Just know that it can do everything that TweenSize/TweenPosition/TweenSizeAndPosition can do and more. It’s also far better.

6 Likes

Hey! I’ve been looking into the issue and found out that tracking the game loaded would work better with the https://developer.roblox.com/api-reference/property/ContentProvider/RequestQueueSize.

local contentProvider = game:GetService("ContentProvider")
local runService = game:GetService("RunService")

while contentProvider.RequestQueueSize > 0 do -- At least 1 file is loading
    runService.RenderStepped:wait() -- Waiting for the load
end

-- The game assets have been loaded.

This property checks for the amount of files that are currently loading. These are automatically pushed there by Roblox when loading eg. decals and so on.

RequestQueueSize is not a reliable property to use for loading (in fact, not one associated with that at all) and you shouldn’t use it when you’re creating a loading screen. It’s a queue size, not a processing size.

RequestQueueSize is a dynamic property that determines how many assets are in the queue to be downloaded for the first time. It has nothing to do with loading. The queue expands when new assets are used for the first time and gets smaller as assets are downloaded. Depending on how many assets are being used, this can result in long wait times which is bad for UX.

You should also only be creating loading screens for things that need to be loaded immediately, not waiting for your entire game to be “loaded” (or replicated).

Hello everyone! Sorry for this mess and the belated Solution marking. Had some problems I had to deal with. The marked Solution gave me some good pointers and I used some code in the latest script albeit with some modifications of a format I’m currently used to. (Will take notes and hopefully grow out of it)

game.ReplicatedFirst:RemoveDefaultLoadingScreen()

local Players = game:GetService("Players")

local ReplicatedFirst = game:GetService("ReplicatedFirst")

local TweenService = game:GetService("TweenService")

local LocalPlayer = Players.LocalPlayer

local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")

local Gui = script.LoadingScreen

local displayText = Gui.Frame.LoadingText

PlayerGui:SetTopbarTransparency(0)

Gui.Parent = PlayerGui

while not game:IsLoaded() do

displayText.Text = "Loading."

wait(1)

displayText.Text = "Loading.."

wait(1)

displayText.Text = "Loading..."

wait(1)

end

Gui.victory:Play()

displayText.Text = "Welcome to [REDACTED]!"

displayText.TextScaled = true

wait(5)

do

local TweenData = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)

local Tween = TweenService:Create(Gui.Frame, TweenData, {Position = UDim2.new(0, 0, 1, 0)})

Tween:Play()

Tween.Completed:Connect(function ()

Gui:Destroy()

end)

end

Again, sorry for answering this late and all the trouble, but thank you all!