How do I end a thread from a script?

You should use CollectionService, or use a ChildAdded event.

That loop doesn’t seem like the right solution.

2 Likes

It looks to me that would just be another way to get a table with all the objects in. I need a way to end the threads of the v.ClickDetector.MouseClick Thread when it is on an outdated version of the object table.

I don’t understand what you want, you would need to supply that function.

Sorry if this doesn’t answer the question but I wrote up some code to use collectionservice (not tested):

local TAG_NAME = "ClickPart"
local CollectionService = game:GetService("CollectionService")

local function onClick(plr)
	--this will happen when any part is clicked
end

local function applyClick(obj)
	 obj.ClickDetector.MouseClick:connect(onClick)
end

CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(applyClick)
-- On remove you might want to do something

for _, obj in pairs(CollectionService:GetTagged(TAG_NAME)) do
	applyClick(obj)
end
1 Like

But then after a while, the folder of click parts will be way different than the table that you get from CollectionService:GetTagged because the click parts are constantly being destroyed or added.

Why are you creating new threads every 1/30th of a frame for a clickdetector? This is an event, simply doing

for i,v in pairs(workspace.ClickPartFolder:GetChildren()) do
    v.ClickDetector.MouseClick:connect(function(plr)
        --this will happen when any part is clicked
    end
end

is enough and will suit your use case.

On a sidenote, never use ipairs.

But then after a while, the folder of click parts will be way different than the table that you get from workspace.ClickPartFolder:GetChildren() because the click parts are constantly being destroyed or added.

On a side note, I thought ipairs were for arrays and pairs were for dictionaries?

You should use :GetChildren() in the function. If you don’t supply the code for it that’s all I can go off of. My code doesn’t use it past the start.

I said that I use to use this code:

while wait() do
      for i,v in ipairs(workspace.ClickPartFolder:GetChildren()) do
             v.ClickDetector.MouseClick:connect(function(plr)
            --this will happen when any part is clicked
             end
     end
end

But that code is not working so now I am trying to use this code:

workspace.CollectablesFolder.ChildAdded:connect(function(v)
     v.ClickDetector.MouseClick:connect(function(plr)
     end)
end)

But that randomly fails to work for reasons I do not know. So I am asking for a solution to this problem.

My code should cover what you did… You aren’t giving the click detector function. From what I can tell mine works.

I will try it…

pairs works as well, ipairs is significantly slower. Use a ChildAdded/DescendantAdded event for future objects.

I solved the first problem with moving the ClickDetector.MouseClick:connect to the same code that makes the ClickPart. However I still have the first problem.

Spawning a new thread every approximately 1/30th of a second because you’re trying to account for new parts in a folder is a terrible idea, especially when you have nothing checking if a previous event exists or not. Even then, this is a bad idea.

This comes off as an XY Problem, where your X is trying to determine how to make new ClickDetectors use your function but your Y is spawning a thread every approximately 1/30th of a second and your Y solution is to try and break the loop. Y solution wouldn’t even solve your problem.

You can simply write a handler function for each ClickDetector, iterate through the folder’s children and connect that function to the ClickDetector’s MouseClick, then do the same using ChildAdded and verifying that the new child is to standard (a BasePart with a ClickDetector or whatever).

local function ClickDetectorFunction(Player)
    -- Your code
end

local function HandleClickBrick(Part)
    if partHasClickerWriteThisPartYourself then
        part.ClickerWhatever.MouseClick:Connect(ClickDetectorFunction)
    end
end

Clickers.ChildAdded:Connect(function (Child)
    HandleClickBrick(Child)
end)

for _, Child in pairs(folderOfClickersOrSomething:GetChildren()) do
    HandleClickBrick(Child)
end

There’s alternate ways to a problem that you have to see first and experiment with when you try to tackle a problem. Loops are definitely not things you need to be using for one-off functions.

Sorry you had to write all of that, I was trying out you previous code and it randomly worked and randomly didn’t. I have the second problem solved but is there a way I can kill a script without it screaming? As in :Destroy() it without it still running?

This is not proper code, it’s a template intended to show you the workflow for an approach that’d be appropriate for this problem. The rest is up to you to write and debug as needed.

What do you mean? What is the problem associated with this statement?

It was the first problem that I mentioned in this topic description(I changed it now) I thought that knowing how to do this I would be able to end some of the thousands of threads I made by spawning every approximately 1/30th of a second. I fixed that problem with other means, but I still need to know a way to end a thread without it erroring with attempt to index field 'Parent' (a nil value) becuase the code is still running.

You’ll need to include relevant code examples for your problem. I don’t understand your problem, nor how you’re managing to achieve that error. The developer console exists and you can also do your own debugging to try and find the source of this issue.

When you destroy a script by going script:Destroy() it makes the scripts parent nil but the thread in the script is still running. That error is from a script that has been destroyed.

Well, yes. Destroying the physical script object isn’t going to implicitly terminate the thread. Scripts are LuaSourceContainers which just facilitate the running of code.

What are you destroying a script for? What’s it’s name, function, code and reason for needing to be deleted?

To answer your question, you can call, resume, and stop a thread by using coroutines. Similar to spawn, in that they both run a function in their own seperate threads. The difference being you can have more control over coroutines.

local thread = coroutine.create(function() --Create a coroutine
    --Put the loop here
end)

coroutine.resume(thread) --Play the coroutine

You can also just do

local thread = coroutine.wrap(function() --Creates and runs the coroutine
    --Put the loop here
end

Now, to stop or yield a coroutine do

local thread = coroutine.wrap(function()
     while wait() do
        if not script.Parent then
            coroutine.yield() --Yields the coroutine. You can still resume the thread if you want.
        else
            --Do stuff
        end
    end
end

On the other hand, you could just use the break keyword to break the loop if the parent of the script is equal to nil, but coroutines also work. :slight_smile:

Now, do I think this is the best way to go about connecting a click detector using a while loop? No, this is an inefficient way to do this. I recommend using a for loop and looping through the parent of the click detectors and connecting like that, and then using a ChildAdded event to connect other click detectors that are added.

4 Likes