How to restart/reset a coroutine?

I’ve got a script that creates a coroutine, and can start/stop that coroutine. However, how can I restart the coroutine (even when if it finishes)?

local event = script.Parent:WaitForChild("Reloading")
local val = script.Parent.Parent:WaitForChild("Reloaded")
local state = script.Parent.Parent:WaitForChild("State")
local handle = script.Parent.Parent:WaitForChild("Handle")

local co = coroutine.create(function()
	for i,v in pairs(handle:GetChildren()) do
		if v:IsA("Sound") == true then
			v.TimePosition = 0
		end
	end
	-- play anims
	wait(.5)
	handle.Tear:Play()
	handle.Tear.Ended:Wait()
	wait(.5)
	handle.SpitPaper:Play()
	handle.SpitPaper.Ended:Wait()
	wait(.7)
	handle.Pour:Play()
	handle.Pour.Ended:Wait()
	wait(.5)
	handle.Frizzen:Play()
	handle.Frizzen.Ended:Wait()
	wait(1)
	handle.TakeOut:Play()
	handle.TakeOut.Ended:Wait()
	wait(.7)
	handle.Ram:Play()
	handle.Ram.Ended:Wait()
	wait(.5)
	handle.Ram:Play()
	handle.Ram.Ended:Wait()
	wait(1)
	handle.TakeOut:Play()
	handle.TakeOut.Ended:Wait()
	val.Value = true
	
	state.Value = "Idle"
end)

event.OnServerEvent:Connect(function(plr)
	if state.Value == "Reloading" then
		coroutine.close(co)
		state.Value = "Idle"
	else
		if val.Value == false then
			coroutine.resume(co)
			state.Value = "Reloading"
		end
	end
end)

Why not create a new coroutine to restart it?

For a more organized approach I would consider looking at the Promise library when handling cancellable tasks but thats my preference.

how would i create a new coroutine that i can cancel outside of that function?

Why not just use the task library? That basically creates a more efficient coroutine for you.

This makes it easier to restart it like dthecoolest suggested already. Rather than coroutine.resume() which will not restart your coroutine, we can just use task.spawn() to make a new one.

And you don’t need to use any external module for this.

local event = script.Parent:WaitForChild("Reloading")
local state = script.Parent.Parent:WaitForChild("State")
local handle = script.Parent.Parent:WaitForChild("Handle")

local ANIMATIONS : {{delay:number, track : AnimationTrack}} = {
	{delay = 0.5, track = handle.Tear},
	{delay = 0.5, track = handle.SpitPaper},
	{delay = 0.7, track = handle.Pour},
	{delay = 0.5, track = handle.Frizzen},
	{delay = 1.0, track = handle.TakeOut},
	{delay = 0.7, track = handle.Ram},
	{delay = 0.7, track = handle.Ram},
	{delay = 0.5, track = handle.Ram},
	{delay = 1.0, track = handle.TakeOut},
}

local function hasAnimationFinished(thread : thread)
	return not thread or coroutine.status(thread) == "dead"
end

local function playAnimations()
	for _,child in handle:GetChildren() do
		if child:IsA("Sound") then
			child.TimePosition = 0
		end
	end

	-- play animations in order
	for _, animation in ipairs(ANIMATIONS) do
		task.wait(animation.delay)
		animation.track:Play()
		animation.track.Ended:Wait()
	end

	state.Value = "Idle"
end

local reloading_thread : thread;
event.OnServerEvent:Connect(function(plr)
	if state.Value == "Reloading" then
		if not hasAnimationFinished(reloading_thread) then
			task.cancel(reloading_thread)
		end
		state.Value = "Idle"
	else
		if hasAnimationFinished(reloading_thread) then
			reloading_thread = task.spawn(playAnimations)
			state.Value = "Reloading"
		end
	end
end)

PS: I also couldn’t help but improve your code a little bit, I hope that’s alright with you.


Best regards,
Pinker

the code you provided can start + restart the function perfectly, but for some reason it doesnt stop the function at task.cancel(thread)

1 Like

Okay that was just some roblox shenanigans. When you task.spawn a coroutine, it’s state is never actually running but instead just suspended for some reason.

I’ve updated my code above to fix this problem.


Best regards,
Pinker

1 Like