Earlier today, while scripting my main menu, I wanted to add an aesthetic effect where a set of four dots would tween in order from left to right from visible to invisible. I got rid of it because I wasn’t sure whether I was using coroutines correctly or not, or if it would achieve my desired effect.
I don’t think seeing the object hierarchy is necessary, so I’ll provide you with an outline of the code I was using to attain this effect. Assume the variables I use already exist.
while true do
if not Loading then coroutine.yield() break end
for n in next, #Dots:GetChildren()
local Tween = TweenService:Create(Dots["Dot"..n], TweenInfo, Goals)
-- ^ Repeated to make them invisible below
This code is successfully able to attain the fade in effect that I desire for my main menu. The issue lies in halting this coroutine. I want it to stop only after the dots have been made invisible. That also spawns other issues that I wish to determine:
Am I using coroutines right?
How do I make the script not do anything until a full run of this goes? I’ve been thinking of creating a “pass” upvalue where its set to false at the top of the loop and true at the bottom, with repeat wait() until Pass == true around the bottom after preloading has finished.
If I’m not doing this right, how can I achieve an effect where a loading aesthetic runs in parallel to some other form of live-updating code? I want the loading percentage to update real time while this aesthetic is running, but I don’t want the next screen to progress until preloading is done AND the dots are invisible.
I am not certain whether you should want to use yield; if you want to resume it later the break would stop it, while simply substituting yield and break for return should set the coroutine state to dead as well. (the moment a coroutine returns the state is set to dead and execution stops)
I’d try doing this the other way around; if the part executing parallel to the routine sets ‘finish_loading’ or some other flag to true, the coroutine returns (after executing a full loop). You could wait till its state is dead to resume execution of the code below that;
– preload is done
finish_loading = true – causes coroutine to return after its current loop
while coroutine.status( that_loading_routine ) ~= “dead” do wait(.1) end
– continue about your business
Coroutines run within the same environment, they do run in a simulated thread. I assume that’s what you meant to say.
From what I gather, you want to make sure a full execution of the loop is done AND the bottom code is done doing some other task. If the bottom code is ready to continue, the coroutine should stop executing after it’s done one full loop, correct?
To achieve that, the coroutine executes its loop and then checks for a variable that’s set externally. If it’s set to true, the routine returns and stops executing.
Below the coroutine is other code that at some point decides it’s done. When this happens, it tells the coroutine to stop executing after its current loop (by setting the variable to true), and then proceeds to wait for that loop to actually be done by checking the coroutine status. (“dead” means it has returned and stopped executing)
You don’t need coroutine.yield, just the break statement (which is never reached with the code as-is). You have no way to resume the coroutine, because you’re not using the return value from coroutine.wrap, which would be the coroutine reference you’d need to resume it. It appears you want this to only run once, so that’s fine.
local co = coroutine.create(function()
while true do
-- dot stuff in a loop
if end_anim then
coroutine.resume( co )
while requestqueuesize > 1 do
-- set bar process
end_anim = true
while coroutine.status( co ) ~= "dead" do
end -- wait till the coroutine is done with its current loop
-- continue doing things
When you use tweenservice there’s a property inside of tweeninfo you can change so it stops, and doesn’t loop. I’m almost certain on the wiki it’s set to -1 which is repetitive but if you set it to 0 or above it will stop after a certain number of times.
@TaaRt Would using break as opposed to return also kill the coroutine?
@Arcerion The TweenInfo associated with the Tween is a read-only property. Once you create a Tween, you can’t change the info property. You have to either cancel it out with an overlapping tween or call cancel on the Tween itself. I also can’t use repeat count because loading time is variable.
I’m confused as to how this is relevant to the question I posed. I’m aware this can happen. As well, you cannot explicitly call coroutine.yield like this - it has to be called within the coroutine itself.
Whenever the function is done executing its body the coroutine will enter a ‘dead’ state. Whether you use break or return in this case doesn’t matter, and is very much personal preference. You could have scenarios where there’s code below the loop, which you do not want executed (return would ensure immediate exit rather than just the current loop).
My bad, coroutine.wrap doesn’t behave the same as coroutine.create. The value returned by coroutine.create should be the thread (co), this requires you to run the coroutine by calling coroutine.resume(co) after creating it.
local co = coroutine.create(function ()
while true do
while not coroutine.status(co) == "dead" do wait() end
Firstly, Lua is singled threaded. This means that anything you do will always be sequential, even if it appears to run in parallel. For this reason, coroutines are essentially psuedo-threads.
If I understand correctly, you want your code to yield until all the dots have faded out, and that’s totally achievable!
Here’s some code that will hopefully set you in the right direction.
local taskIsCompleted = false
local runTask = coroutine.wrap(function ()
-- do something which takes time (fading the dots)
if thread then
taskIsCompleted = true
-- do something else which takes time (preloading assets)
if not taskIsCompleted then
thread = coroutine.running()