How to stop and resume this while loop manually?

So, I have these fireworks, and I’m trying to edit the script to make them usable.
I was asked to edit the script so that the flames would turn on and off one after another in a loop, which is what I did, and it looks like this. The issue is: I can’t seem to stop the animation once the “on” button is clicked.
What I’m trying to achieve is the possibility of turning on and off the fireworks by clicking on the GUI button.
Here is the code:

script.Parent.MouseButton1Down:connect(function()
	while true do
			game.Workspace.Fire.S4.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S4.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S4.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S4.Sound:Play()
			wait(0.5)
			game.Workspace.Fire.S4.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S4.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S4.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S4.Sound:Pause()
			game.Workspace.Fire.S3.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S3.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S3.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S3.Sound:Play()
			game.Workspace.Fire.S5.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S5.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S5.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S5.Sound:Play()
			wait(0.5)
			game.Workspace.Fire.S3.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S3.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S3.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S3.Sound:Pause()
			game.Workspace.Fire.S5.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S5.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S5.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S5.Sound:Pause()
			game.Workspace.Fire.S2.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S2.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S2.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S2.Sound:Play()
			game.Workspace.Fire.S6.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S6.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S6.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S6.Sound:Play()
			wait(0.5)
			game.Workspace.Fire.S2.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S2.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S2.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S2.Sound:Pause()
			game.Workspace.Fire.S6.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S6.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S6.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S6.Sound:Pause() 
			game.Workspace.Fire.S1.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S1.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S1.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S1.Sound:Play()
			game.Workspace.Fire.S7.ParticleEmitter1.Enabled = true
			game.Workspace.Fire.S7.ParticleEmitter2.Enabled = true
			game.Workspace.Fire.S7.SurfaceLight.Brightness = 10
			game.Workspace.Fire.S7.Sound:Play()
			wait(0.5)
			game.Workspace.Fire.S1.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S1.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S1.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S1.Sound:Pause()
			game.Workspace.Fire.S7.ParticleEmitter1.Enabled = false
			game.Workspace.Fire.S7.ParticleEmitter2.Enabled = false
			game.Workspace.Fire.S7.SurfaceLight.Brightness = 0
			game.Workspace.Fire.S7.Sound:Pause()
	end
end)

Now, I tried to use a debounce method using a boolean variable that is checked by an if statement everytime the button is clicked, but it didn’t work. For reference, I followed this Wiki article.
As second attempt, I tried using break loop, as specified on this other article but obviously it didn’t work either.
More recently I also tried using a BoolValue object, placed in the workspace, and the code looks like this:

local Firing = game.Workspace.Firing

local function Fire()
	game.Workspace.Fire.S4.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S4.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S4.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S4.Sound:Play()
	wait(0.5)
	game.Workspace.Fire.S4.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S4.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S4.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S4.Sound:Pause()
	game.Workspace.Fire.S3.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S3.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S3.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S3.Sound:Play()
	game.Workspace.Fire.S5.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S5.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S5.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S5.Sound:Play()
	wait(0.5)
	game.Workspace.Fire.S3.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S3.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S3.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S3.Sound:Pause()
	game.Workspace.Fire.S5.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S5.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S5.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S5.Sound:Pause()
	game.Workspace.Fire.S2.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S2.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S2.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S2.Sound:Play()
	game.Workspace.Fire.S6.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S6.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S6.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S6.Sound:Play()
	wait(0.5)
	game.Workspace.Fire.S2.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S2.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S2.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S2.Sound:Pause()
	game.Workspace.Fire.S6.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S6.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S6.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S6.Sound:Pause() 
	game.Workspace.Fire.S1.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S1.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S1.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S1.Sound:Play()
	game.Workspace.Fire.S7.ParticleEmitter1.Enabled = true
	game.Workspace.Fire.S7.ParticleEmitter2.Enabled = true
	game.Workspace.Fire.S7.SurfaceLight.Brightness = 10
	game.Workspace.Fire.S7.Sound:Play()
	wait(0.5)
	game.Workspace.Fire.S1.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S1.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S1.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S1.Sound:Pause()
	game.Workspace.Fire.S7.ParticleEmitter1.Enabled = false
	game.Workspace.Fire.S7.ParticleEmitter2.Enabled = false
	game.Workspace.Fire.S7.SurfaceLight.Brightness = 0
	game.Workspace.Fire.S7.Sound:Pause()
	Firing.Value = false
end

script.Parent.MouseButton1Down:Connect(function()
	Firing.Value = true
	while Firing do
		Fire()
		
		if not Firing then
			break
		end
	end
end)

Script 2:

script.Parent.MouseButton1Click:Connect(function()
game.Workspace.Firing.Value = false
end)

If someone could please explain what I did wrong and how to achieve what I need, that would be great.

3 Likes

I would do something like this:

local Firing = game.Workspace.Firing
local Fire=workspace.Fire
local Folders={Fire.S1, Fire.S2, Fire.S3, Fire.S4, Fire.S5, Fire.S6, Fire.S7}

local function FireOn()
	
	for i=1, 7 do 
		local CurFolder=Folders[i]
		CurFolder.ParticleEmitter1.Enabled = true
		CurFolder.ParticleEmitter2.Enabled = true
		CurFolder.SurfaceLight.Brightness = 10
		CurFolder.Sound:Play()
		wait(0.5)
		CurFolder.ParticleEmitter1.Enabled = false
		CurFolder.ParticleEmitter2.Enabled = false
		CurFolder.SurfaceLight.Brightness = 0
		CurFolder.Sound:Pause()
	end
	Firing.Value = false
end

script.Parent.MouseButton1Down:Connect(function()
	Firing.Value = true
	
end)

Firing.Changed:Connect(function() if Firing.Value==true then FireOn()  end end)
1 Like

You should try creating a variable equal to false or something, and when the button is clicked, set it to true, and let it continue in the while loop.

Hi, thank you so much for taking the time to answer and help me! While your code is obviously neater than mine, it’s not exactly what I’m asking for…
This is the animation that your code executes:
https://gyazo.com/a109a337ca7bdabfc9fe224c783b431f
Unfortunately, this is not what I’m trying to achieve, it’s actually this: https://gyazo.com/aa7721c6d36a2f8d0ea5b695c66a2322 as you can see from the gif, the animation loops over and over. I’m trying to find a way to turn it on and off by clicking on the “Stop” button in the first gif.

It’s what I did, but for some reason it doesn’t work…

Can you provide your new script?

All the scripts are in the first post. ^^

Before I try to correct your script, can you possibly send a picture of your explorer?

image image

So the script is inside of a gui button? Or a surface gui button? Or a Clickdetector?

Gui button, ignore the “Confetti Panel” object, it’s unrelated lol.
The script was originally in a surface GUI, but I was asked to convert it in a normal GUI. And no, no click detectors insolved, only TextButtons.

One thing to note about your code is you have a lot of repetition in there. That’s generally a sign there is an easier way. For example you could create a function that handles enabling or disabling each firework and call that instead of typing out every change every time. That way if you ever need to change it, you can just edit the function instead of going through the whole script to find every place where you made a mistake.

I created some code in the way I would go about making this.

Hopefully doesn’t have errors, I didn’t actually test it.

local f = game.Workspace.Fire
local fireworkAnimationOrder = {
	{f.S4},
	{f.S3, f.S5},
	{f.S2, f.S6},
	{f.S1, f.S7},
}


local function setFireworkEnabled(fireworks, enabled)
	enabled = enabled ~= false --turns nil to true and leaves false alone
	fireworks = typeof(fireworks) == "table" and fireworks or {fireworks} --Makes it still work if you only passed a single object not in a table, not really necessary, but makes it less likely to be used in an incompatible way
	for _,firework in pairs(fireworks) do
		firework.ParticleEmitter1.Enabled = enabled
		firework.ParticleEmitter2.Enabled = enabled
		firework.SurfaceLight.Brightness = enabled and 10 or 0 --10 if true, 0 if false
		firework.Sound[enabled and "Play" or "Pause"](firework.Sound) --Changes which function to call based on enabled.  Has to pass the sound into the first parameter since I'm calling through this notation rather than colon. This is just personal preference
	end
end

local newLoopTime, fireworksEnabled
script.Parent.MouseButton1Down:connect(function()
	fireworksEnabled = not fireworksEnabled
	
	newLoopTime = tick()
	local loopTime = newLoopTime --This is all setup to allow the loop to only continue on the last hit
	
	while fireworksEnabled and loopTime == newLoopTime do
		for _,animatables in pairs(fireworkAnimationOrder) do
			if not (fireworksEnabled and loopTime == newLoopTime) then break end --terminate if changed while in for loop
			setFireworkEnabled(animatables)
			wait(0.5)
			setFireworkEnabled(animatables, false)
		end
		wait(0.5)
	end
end)

If you ever need to change what order the animation is in, you would just need to adjust the animation order table at the top

Try this:

local Firing = game.Workspace.Firing
local Fire=workspace.Fire

local function FireFireWork(FireWorkTable)
	
	for _, v in ipairs(FireWorkTable) do
		v.ParticleEmitter1.Enabled = true
		v.ParticleEmitter2.Enabled = true
		v.SurfaceLight.Brightness = 10
		v.Sound:Play()
	end
	wait(0.5)
	for _, v in ipairs(FireWorkTable) do
		v.ParticleEmitter1.Enabled = false
		v.ParticleEmitter2.Enabled = false
		v.SurfaceLight.Brightness = 0
		v.Sound:Pause()
	end
	
end

local function FireOn()
	
	while Firing.Value==true do 
		FireFireWork({Fire.S4})
		FireFireWork({Fire.S3, Fire.S5})
		FireFireWork({Fire.S2, Fire.S6})
		FireFireWork({Fire.S1, Fire.S7})
	end
	
end

script.Parent.MouseButton1Down:Connect(function()
	Firing.Value = true
	
end)

-- OFF Button Script
script.Parent.MouseButton1Down:Connect(function()
	Firing.Value = false
	
end)

Firing.Changed:Connect(function() if Firing.Value==true then FireOn()  end end)
1 Like

Here’s what I tried, hopefully this works.

local Firing = game.Workspace.Firing
local Fire = game.Workspace.Fire
local Folders = {Fire.S1, Fire.S2, Fire.S3, Fire.S4, Fire.S5, Fire.S6, Fire.S7}

local function FireOn()
	if Firing.Value == false then
		Firing.Value = true
		
	for i = 1, 1000 do		
		local CurFolder = Folders[i]
		CurFolder.ParticleEmitter1.Enabled = true
		CurFolder.ParticleEmitter2.Enabled = true
		CurFolder.SurfaceLight.Brightness = 10
		CurFolder.Sound:Play()
		wait(0.5)
		CurFolder.ParticleEmitter1.Enabled = false
		CurFolder.ParticleEmitter2.Enabled = false
		CurFolder.SurfaceLight.Brightness = 0
		CurFolder.Sound:Pause()
	end
		Firing.Value = false
		
	elseif Firing.Value == true then
		local CurFolder = Folders
		CurFolder.ParticleEmitter1.Enabled = false
		CurFolder.ParticleEmitter2.Enabled = false
		CurFolder.SurfaceLight.Brightness = 0
		CurFolder.Sound:Pause()
	end
end

script.Parent.MouseButton1Down:Connect(function()
	FireOn()
end)

Yes, I used Zombie’s original script and modified it a bit.

This would only run 1000 times. Since there is only a delay of about 0.5 seconds each time, it would stop running in about 8 minutes and 20 seconds. Depending on use case it might not be an issue, but if those can be left alone for a while you would need to click the disable button to restart it. Also, since there is no break condition in the for loop, when you click disable it will turn off all of the fireworks, but then turn them right back on because the for loop would still be running. Additionally you can’t reenable it unless you wait for the 8 minute 20 second loop to finish.

This would probably work. There is just one area that could cause concern.

That is telling the mouseButton1Down thing to both turn it on and off on the same click. So it won’t have the expected behaviour. You can simplify it down to this

script.Parent.MouseButton1Down:Connect(function()
    Firing.Value = not Firing.Value
    if Firing.Value then
        FireOn()
    end
end

One other thing to keep in mind is since the loop takes time to complete it’s technically possible to break the button. Since the while loop only checks that the value is true every 2 seconds if you press the button 2 times between that you will actually end up starting another loop and they would run at the same time.

1 Like

Yeah, I’m guessing he has another script for the off button so I just added it there so he could see what to add to his actual off button script - I should’ve made that clearer.

2 Likes

Hi, thank you so much for this, unfortunately the output window gives me this error:



Not sure how to handle this, I’m still trying to understand your code since I don’t like to just copy-paste without learning. The comments are much appreciated btw. x

for i, firework. You forgot the i in the i,v in pairs.

That’s because I made a dumb mistake.

This is the fixed code

fireworks = typeof(fireworks) == "table" and fireworks or {fireworks}

So what I was trying to do there was set fireworks equal to itself if it was in a table, or put it in a table if it was passed without it. That means that line is actually not necessary, it just lets you make a simple error and overlook it. The reason it errors is because I forgot something. I have fixed the code there in the original post. But I’ll explain the issue

This code turns the variable fireworks into a table if it isn’t in a table already. Or at least that’s what it should do.

fireworks = typeof(fireworks) == "table" or {fireworks}

It’s supposed to work by taking advantage of the fact that lua returns the last variable checked in a boolean operation. So if you were to say something like:

a = number or 1

It number exists, it will return number because if the first argument of an or is true, the statement returns true so it doesn’t bother to look at the next thing. However, if it’s false or nil it will go to the next argument and return that instead if it’s truthy (not false or nil). In this case it would return 1.

ands are a bit different

a = number and 1

In the case of an and, it has to check everything. Well everything until it hits one that is false. Once it hits a false value, the and statement is false so it will stop checking. So in this case if number is truthy it will return 1 since it had to check to see if the 1 is truthy, but if it was false, it would have immediatly stopped and returned false.

My mistake is the fact I used and

typeof(fireworks) == "table" or {fireworks}

So it first checks that the type of fireworks is table. That returns true, so it doesn’t check the other side of the or making it return a boolean instead of useful information. The fix is to tell it what to be when it is true

typeof(fireworks) == "table" and fireworks or {fireworks}

Now after checking that fireworks is a table it goes onwards with the and if fireworks is a table it will move to the other side of the and to see if that’s truthy. In this case since fireworks exists, it will be truthy and returns that since the next check is an or it knows that the first check passed and skips the next check of the or.
If fireworks isn’t equal to a table though, it will skip the next argument of the and and go straight to the other side of the or since the first part returned a falsy value. So if fireworks is either not a table, false, or nil it would return itself inside a table.

Here are some examples to help showcase it a bit more

print(4 and 5)         --> 5
print(nil and 13)      --> nil
print(false and 13)    --> false
print(4 or 5)          --> 4
print(false or 5)      --> 5

examples were taken from here because I’m lazy.
https://www.lua.org/pil/3.3.html

Wow that was lengthy. Hopefully that made sense, if not feel free to ask about anything confusing.

1 Like