Ending A Looping Script on the Client-Side

I am a new scripter, and I would like to know how to make this work:

I have a SurfaceGUI with an ImageLabel which has a script. This script has a looping mechanic which causes the ImageLabel to shake by tweening the rotation property at a random interval. When someone says a specific word in the chat, it should cause this shaking to stop. I have it so that it fires a RemoteEvent in ReplicatedStorage, which is received by a LocalScript in StarterGui. This LocalScript deletes the script in the ImageLabel and breaks the loop, all while being in the clientside. However, this doesn’t work.

The object that the SurfaceGUI is attached to plays a looping sound, and when the LocalScript detects the fired RemoteEvent, it causes the sounds to stop as intended, but the shaking still occurs.
I would like to know why, in detail, this isn’t working and how to fix it.

Could you please share the script?

I just did a slight update to the code system, which I had updated this topic to apply.

There are several scripts: 1. The message detector. 2. The Loop script. 3. the LocalScript.
If you could tell me which of these you want shared, I’ll share, this is to prevent confusion.

Could you please share the looping script?

local ts = game:GetService("TweenService")
local timed = math.random(0.1,0.5)
wait(timed)
repeat
	ts:Create(
		script.Parent,
		TweenInfo.new(timed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
		{ Rotation = math.random(0,5)/10 }
	):Play()
	wait(timed)
	ts:Create(
		script.Parent,
		TweenInfo.new(timed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
		{ Rotation = math.random(-5,0)/10 }
	):Play()
	wait(timed)
until nil

It’s a little weird but for a more natural feel I made it based on random timings.

Ok so your script uses Repeat until nil. Its more better or convenient to use while true do.
Also quick side note, your tweens are overlapping so im not sure if youd want them to be like that. Heres a script example:

local TweenService = game:GetService("TweenService")
local part = script.Parent

while true do
   local timed = math.random(1, 5) / 10 -- random between 0.1 and 0.5
   local tweenInfo = TweenInfo.new(timed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)

   -- Rotate slightly positive
   TweenService:Create(part, tweenInfo, { Rotation = math.random(0, 5) / 10 }):Play()
   wait(timed)

   -- Rotate slightly negative
   TweenService:Create(part, tweenInfo, { Rotation = math.random(-5, 0) / 10 }):Play()
   wait(timed)
end

I’m not necessarily worried about the tweens overlapping, I’m mainly focused on how to stop the loop clientside, so when I say the secret message, the loop will stop ONLY for me, and everyone else can still see it running.

I also believe that ‘while true do’ goes on infinitely even when the script is deleted, right?

I have a few things.
If the shaking is only for the client that says the secret message then why bother with sending the event over the network?

It should be possible to just bind a p.Chatted to the local player and if it is the secret message than you can remove the effects.

local player = game.Players.LocalPlayer
player.Chatted:Connect(function(p)
-- delete your script, or like @agentchicken865 suggested, you can use a while <condition> loop and update a variable to break the loop.
end)

Does that make sense? Also to answer your question about while loops, they should terminate when the script is deleted. I don’t really recommend deleting a script to stop a loop but if it works it works. Usually it’s better to define an escape condition so you can clean up side effects.

1 Like

It works, but the problem is is that the shaking still occurs even with the script deleted.
I assume this is because the script is still functional on the Server?

Could you send the LocalScript that is responsible for deleting the script in the ImageLabel?

When the RemoteEvent is fired, it is received to this:

game.ReplicatedStorage.SHIFT.OnClientEvent:Connect(function()
	workspace.GATEWAY.SOUND.Playing = false
	workspace.GATEWAY.SOUND1.Playing = false
	workspace.GATEWAY.SOUND2.Playing = false
	workspace.GATEWAY.SOUND3.Playing = false
	workspace.GATEWAY.SOUND4.Playing = false
	workspace.GATEWAY.SURFACEGUI.IMAGELABEL.SCRIPT:Destroy()
end)

The looping script is a server script then, since it is running in workspace no? Server scripts are not replicated to clients and thus deleting a server script will not have any effect for a client. The shaking effect is being performed by the server and being replicated to clients.

You’d need make the looping script local, maybe using CollectionService to find ImageLabel that it needs to make shake?

The looping script IS a ServerScript yes. I’m not familiar with CollectionService however.

No worries.

In most cases any UI (Including SurfaceGUIs) you want to run on the client, especially if the client has to interact with it.

You are able to just parent a SurfaceGUI to an instance and have it appear but then the server will be responsible for it and not any client. This is what you did.

The sound stops playing because clients can make modifications to instances locally, the GUI does not stop moving because any changes the server makes to an instance will be replicated to a client. The client is not told what code is running on the server it just sees the effects.

There’s kind of two approaches you can use from here.

  1. Move the SurfaceGUI into StarterGUI and use the Adornee property to attach it to workspace.GATEWAY

    • From there you’ll have to convert the scripts to localscripts.
  2. You can leave the SurfaceGUI in workspace, but you create a new local script for the shaking effect and you place it somewhere a local script can run. This script can either do workspace.GATEWAY.SURFACEGUI.IMAGELABEL and do all the shaking effects stuff it needs to do from there OR you can use CollectionService. CollectionService allows you to tag instances and then retrieve a list of those. Either way would work, CollectionService would be good for you to look into though even if you don’t use it.

I tried doing the first approach first, however it’s not working. It’s still shaking. Outside of the looping code in the loop script I added

game.ReplicatedStorage.SHIFT.OnClientEvent:Connect(function()
	script:Destroy()
end)

Which does not work.

FULL CODE:

local ts = game:GetService("TweenService")
local timed = math.random(0.1,0.5)
wait(timed)
repeat
	ts:Create(
		script.Parent,
		TweenInfo.new(timed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
		{ Rotation = math.random(0,5)/10 }
	):Play()
	wait(timed)
	ts:Create(
		script.Parent,
		TweenInfo.new(timed, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
		{ Rotation = math.random(-5,0)/10 }
	):Play()
	wait(timed)
until nil

game.ReplicatedStorage.SHIFT.OnClientEvent:Connect(function()
	script:Destroy()
end)

I figured it out. After doing what devmaxcat said, I partially did the second approach he mentioned. After making the looping script a localscript and moving that somewhere it can run, and then I put the code seen here:

game.ReplicatedStorage.SHIFT.OnClientEvent:Connect(function()
	script:Destroy()
end)

INSIDE of the loop, instead of outside it, which then solved the issue.

3 Likes

you shouldn’t mark your self as solution.

If @devmaxcat solved your solution, mark their reply as solution. Not yours.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.