While loop not breaking properly

I’ve been trying to create the system where if you start running your energy decreases continuously until you stop running.

My problem is that the loop which causes the energy value to decrease keeps on going even after you stop running. It also does actually break for like a millisecond or so, but then apparently the entire loop repeats right after breaking, causing the loop to keep on decreasing the energy value.

Here is the server script in which I’m actually having problems:

local runService = game:GetService("RunService")

local changeAmount = .125

script.Parent:WaitForChild("ChangeEnergyRun").OnServerEvent:Connect(function(plr, action)
	local char = plr.Character
	local energy = plr:FindFirstChild("Energy")
	
	if action == "Start" then
		if char:FindFirstChild("Decreasing") then
		else
			local decreasing = Instance.new("BoolValue")
			decreasing.Name = "Decreasing"
			decreasing.Parent = char
		end
		
		if action == "Stop" then
			if char:FindFirstChild("Decreasing") then
				char:FindFirstChild("Decreasing"):Destroy()
			end
		end
	end
	
	spawn(function()
		while runService.Heartbeat:Wait() do
			if action == "Start" then
				energy.Value -= changeAmount
			elseif action == "Stop" then
				break
			end
		end
	end)
end)

I would really appreciate some help on this as I’ve spent too long trying to fix this issue myself.

try this

local decreasingCo = coroutine.create(function()
	while runService.Heartbeat:Wait() do
		if action == "Start" then
			energy.Value -= changeAmount
		end
	end
end)

and replace that with this

if action == "Stop" then
    if decreasingCo then
		coroutine.yield(decreasingCo)
	end
end

It didn’t change the energy value at all, but what does coroutine do?

does the output say anything?

and a coroutine is basically like spawn but you can do a loop and pause/yield it and start it again whenever you want to (thats the best way i can explain it lol)

??? character limit

Nope, unfortunately there are no outputs

Because action is created every time the event fires. Try this

local runService = game:GetService("RunService")

local changeAmount = .125
local updateThread;

script.Parent:WaitForChild("ChangeEnergyRun").OnServerEvent:Connect(function(plr, action)
	local char = plr.Character
	local energy = plr:FindFirstChild("Energy")
	
	if action == "Start" then
		if char:FindFirstChild("Decreasing") then
		else
			local decreasing = Instance.new("BoolValue")
			decreasing.Name = "Decreasing"
			decreasing.Parent = char
		end
		
		if action == "Stop" then
			if char:FindFirstChild("Decreasing") then
				char:FindFirstChild("Decreasing"):Destroy()
			end
		end
	end
	
    if not updateThread and action == "Start" then
		updateThread = task.spawn(function()
			while runService.Heartbeat:Wait() do
				energy.Value -= changeAmount
			end
		end)
    elseif action == "Stop" then
        task.cancel(updateThread)
    end
end)

oh yeah, i forgot about that

oh yeah i forgot about that character limit

That almost worked and it did stop the energy from decreasing, but then it printed this error:

Line 32: Invalid argument #1 to ‘cancel’ (thread expect, got nil)

local runService = game:GetService("RunService")

local changeAmount = .125
local updateThread;

script.Parent:WaitForChild("ChangeEnergyRun").OnServerEvent:Connect(function(plr, action)
	local char = plr.Character
	local energy = plr:FindFirstChild("Energy")
	
	if action == "Start" then
		if char:FindFirstChild("Decreasing") then
		else
			local decreasing = Instance.new("BoolValue")
			decreasing.Name = "Decreasing"
			decreasing.Parent = char
		end
		
		if action == "Stop" then
			if char:FindFirstChild("Decreasing") then
				char:FindFirstChild("Decreasing"):Destroy()
			end
		end
	end
	
    if not updateThread and action == "Start" then
		updateThread = task.spawn(function()
			while runService.Heartbeat:Wait() do
				energy.Value -= changeAmount
			end
		end)
    elseif updateThread and action == "Stop" then
        task.cancel(updateThread)
    end
end)

Is your intention only having this event connect once? if so you can try using :Once() instead of :Connect() to avoid multiple threads creating

It needs to be fired everytime the player starts running and stops running as it’s meant to alter the energy value.

That didn’t give a big error which is good, but then it stopped working from this small error:
cannot resume dead coroutine

thats impossible because its creating the coroutine each time…

show us your script

Here:

local runService = game:GetService("RunService")

local changeAmount = .125
local updateThread;

script.Parent:WaitForChild("ChangeEnergyRun").OnServerEvent:Connect(function(plr, action)
	local char = plr.Character
	local energy = plr:FindFirstChild("Energy")

	if action == "Start" then
		if char:FindFirstChild("Decreasing") then
		else
			local decreasing = Instance.new("BoolValue")
			decreasing.Name = "Decreasing"
			decreasing.Parent = char
		end
	end
	
	if action == "Stop" then
		if char:FindFirstChild("Decreasing") then
			char:FindFirstChild("Decreasing"):Destroy()
		end
	end
	
	if not updateThread and action == "Start" then
		updateThread = task.spawn(function()
			while runService.Heartbeat:Wait() do
				energy.Value -= changeAmount
			end
		end)
	elseif updateThread and action == "Stop" then
		task.cancel(updateThread)
	end
end)

It’s the same one made by Jqck

try this while i think of something

(not updateThread and action == "Start")

(updateThread and action == "Stop")

Sorry, still gives that same error

Still giving same error for some reason

Ahh mb, I forgot threads don’t get set to nil automatically

local runService = game:GetService("RunService")

local changeAmount = .125
local updateThread;

script.Parent:WaitForChild("ChangeEnergyRun").OnServerEvent:Connect(function(plr, action)
	local char = plr.Character
	local energy = plr:FindFirstChild("Energy")

	if action == "Start" then
		if char:FindFirstChild("Decreasing") then
		else
			local decreasing = Instance.new("BoolValue")
			decreasing.Name = "Decreasing"
			decreasing.Parent = char
		end
	end
	
	if action == "Stop" then
		if char:FindFirstChild("Decreasing") then
			char:FindFirstChild("Decreasing"):Destroy()
		end
	end
	
	if not updateThread and action == "Start" then
		updateThread = task.spawn(function()
			while runService.Heartbeat:Wait() do
				energy.Value -= changeAmount
			end
		end)
	elseif updateThread and action == "Stop" then
		task.cancel(updateThread)
        updateThread = nil
	end
end)

Ooh it’s actually working now without any problems, but it keeps giving that same error on the output each time.