Unable to break while loop

I’m making a ModuleScript which toggles an alarm feature, which is just playing an alarm sound and looping the part’s material.

It works when I toggle the alarm on, but when I try to turn it off, it keeps looping the materials. Is there any way I could break this loop?

function Main.ToggleAlarm(part)
	local alarmOff = true
	
	if alarmOff then
		part.Alarm:Play()
		while true do
			part.Material = Enum.Material.Neon
			wait(part.Alarm.TimeLength/2)
			part.Material = Enum.Material.SmoothPlastic
			wait(part.Alarm.TimeLength/2)
		end
		alarmOff = false
	else
		if alarmOff == false then
			part.Alarm:Stop()
			part.Material = Enum.Material.SmoothPlastic
			alarmOff = true
		end
	end
end
1 Like

You have a while true loop which doesn’t have a break statement in it. This means you will never exit this loop.

Edit: You may also simplify your code a bit:

function Main.ToggleAlarm(part)
	local alarmOff = not part.Alarm.IsPlaying -- If alarm is playing, then the alarm is on.

	if alarmOff then
		alarmOff = false -- You turn on the alarm here
		part.Alarm:Play()
		while part.Alarm.IsPlaying do -- Loop until it stops playing
			part.Material = Enum.Material.Neon
			wait(part.Alarm.TimeLength/2)
			part.Material = Enum.Material.SmoothPlastic
			wait(part.Alarm.TimeLength/2)
		end
	else -- You will only end up in the else statement if alarmOff == false
		part.Alarm:Stop()
		part.Material = Enum.Material.SmoothPlastic
		alarmOff = true
	end
end
1 Like

Create another bool variable set to true, and set it to false whenever you want to stop the loop

local val = true
while val do
end

Once the val is false, it will stop

1 Like

Hi r_aikkonen - the easiest way to break this loop would be to create a bindable event, fire that event in the else section of your if statement, and then put this line at the end of your while loop:

Event.Event:Connect(function()
break
end)

Alternatively you could change the loop to a repeat until loop and just repeat until the event id fired.

Well I take it back, this way is easier lol.

So I should use the alarmOff val and put while alarmOff == false

Exactly, as long as it changes from true to false, you can use any variable

	if alarmOff then
		part.Alarm:Play()
		alarmOff = false
		while alarmOff == false do
			part.Material = Enum.Material.Neon
			wait(part.Alarm.TimeLength/2)
			part.Material = Enum.Material.SmoothPlastic
			wait(part.Alarm.TimeLength/2)
		end

I’ve done this and it hasn’t changed.

Is there a line that changes the alarmOff to true once you want to stop it? Also, I would recommend putting the while loop inside a coroutine like this

coroutine.wrap(function()
	while alarmOff == false do
		part.Material = Enum.Material.Neon
		wait(part.Alarm.TimeLength/2)
		part.Material = Enum.Material.SmoothPlastic
		wait(part.Alarm.TimeLength/2)
	end
end)()

If there is no coroutine, the code will just keep the loop going, without being able to do anything else. If you put it in a coroutine, it will be able to execute other codes inside the same script, while the loop still runs.

@m_orbidlyfat please consider unmarking this as Solved as your issue remains. The issue you have with your code at the moment is the following:

You always set alarmOff to true, this means that in your if statement it will always start the alarm but never turn it off.

Also, you could replace AlarmOff with part.Alarm.IsPlaying() as I partly did in my code example.

Yes, this is the code now,

local Main = {}

function Main.ToggleAlarm(part)
	local alarmOff = true
	
	if alarmOff then
		part.Alarm:Play()
		alarmOff = false
		coroutine.wrap(function()
			while alarmOff == false do
				part.Material = Enum.Material.Neon
				wait(part.Alarm.TimeLength/2)
				part.Material = Enum.Material.SmoothPlastic
				wait(part.Alarm.TimeLength/2)
			end
		end)()
		
	else
		if alarmOff == false then
			part.Alarm:Stop()
			part.Material = Enum.Material.SmoothPlastic
			alarmOff = true
		end
	end
end

return Main

If you set the alarmOff true at the beginning, there is no way that the second part of the if statement could run, and it won’t set the alarmOff to true again.

local alarmOff = true

function Main.ToggleAlarm(part)
	if alarmOff then
		part.Alarm:Play()
		alarmOff = false
		coroutine.wrap(function()
			while not(alarmOff) do
				part.Material = Enum.Material.Neon
				wait(part.Alarm.TimeLength/2)
				part.Material = Enum.Material.SmoothPlastic
				wait(part.Alarm.TimeLength/2)
			end
		end)()
		
	else
		part.Alarm:Stop()
		part.Material = Enum.Material.SmoothPlastic
		alarmOff = true
	end
end

I moved the local alarmOff = true to the outside of the function. Now it’s able to run the second part too.

Also, I removed the if alarmOff == false from the second part, because it had no meaning. If you check if it’s true, and it’s not, you don’t need to check if it’s false again, because you already know that it is. That’s how the if statement works

I’m aware that that’s how it works, I’ve just had trust issues with if else statements, so I put another condition just to double check it.

Now what happens is if I click an alarm to enable it, it works fine, but if I click another alarm, It stops the enabled one blinking and continues to beep.

1 Like

I believe my code snippet doesn’t have that problem. The code snippet I provided checks if the alarm for the part provided is active.

1 Like

if you have multiple alarms, you have to store their “alarmOff” state individually. Create a BoolValue inside each of the alarms, set them true by default, and then do this

function Main.ToggleAlarm(part)
	local alarmOff = part.alarmOff
	if alarmOff.Value then
		part.Alarm:Play()
		alarmOff.Value = false
		coroutine.wrap(function()
			while not(alarmOff.Value) do
				part.Material = Enum.Material.Neon
				wait(part.Alarm.TimeLength/2)
				part.Material = Enum.Material.SmoothPlastic
				wait(part.Alarm.TimeLength/2)
			end
		end)()
		
	else
		part.Alarm:Stop()
		part.Material = Enum.Material.SmoothPlastic
		alarmOff.Value = true
	end
end

EDIT: Use @ifkpop 's answer, it’s far simpler

1 Like