Applying a script to items simultaneously

I was wondering whether or not it is possible to apply a script to multiple items without using a for loop. For example, would it be possible to apply a script to anything tagged with collection service without using a for loop?

In the following script an item is placed in the tagged model, then after 5 seconds the item is destroyed. The problem is that the wait() stops the entire script, and the for loop. This means that the items don’t appear in the tagged model simultaneously.

local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Item = ReplicatedStorage.Item
local ItemFolder = game.Workspace.ItemFolder

for _,model in pairs(CollectionService:GetTagged("models")) do
            local newItem = Item:Clone()
			newItem.Parent = ItemFolder
			newItem.CFrame = model.CFrame
			newItem.Anchored = true

			wait(5)
	
			newItem:Destroy()
end

U can use packages. Right click the scrit u want to apply and then click Convert To Package. Now to apply the script to a Thing u can go to toolbox and in My Packages u can insert the package into the Thing.

To update a package u can right click the Packaged Script and click Publish Changes.

Packages also work cross-place

More Info
1 Like

I think you’re looking for coroutines. Basically a coroutine makes a new thread (basically a script) that runs alongside the current thread. So to solve the wait problem I would make a new coroutine (which is made easy through Roblox’s builtin spawn() function) and put the wait code in there.

local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Item = ReplicatedStorage.Item
local ItemFolder = game.Workspace.ItemFolder

for _,model in pairs(CollectionService:GetTagged("models")) do
	local newItem = Item:Clone()
	newItem.Parent = ItemFolder
	newItem.CFrame = model.CFrame
	newItem.Anchored = true
	spawn(function()
		wait(5)
		newItem:Destroy()
	end)
end
1 Like

Using spawn() is bad since there is a delay before the script actually runs. And if there are a lot of things Tagged the delay can become longer.

The wait() is pretty much insignificant and the times won’t add up because it’s a coroutine.

I have read post where peope said it can take up to 15 seconds before the code actually runs

The wait definitely isn’t going to be that long (in this case), but this thread does recommend not using it.
https://devforum.roblox.com/t/coroutines-v-s-spawn-which-one-should-i-use/368966/2

i did read the same topic. thats why i mentioned the 15 seconds

I’m reading more if it and it seems that there’s a lot of debate about it. But I’m sure that in this usage case there will be no issues

Since you’re already using spawn instead of coroutines, you might as well replace the spawn + wait to just delay, which allows you to specify the amount of time to wait as an argument.

I think you’re looking for this though:
(You still need a for loop, but you only need one as new things that are tagged get the function applied to them automatically using the InstanceAddedSignal event.)

local CollectionService = game:GetService("CollectionService")

local tag = "thing"

local function doSomething(item)

end

for i, v  in ipairs(CollectionService:GetTagged(tag)) do
    doSomething(v)
end

CollectionService:GetInstanceAddedSignal(tag):Connect(doSomething)
1 Like

I was using coroutines before, but when you copy and paste the same sort of script into about 10 different coroutines you wonder if there is a more efficient way of doing things.

Sounds like it’s time for modules to me lol

1 Like

I’ve never heard of packages before, I’ll look into it.

Thanks for the reply. This seems like a good solution for looping and collection service generally. However, the wait() I have still means that only one ‘Item’ can be produced at a time, rather than many simultaneously.

Since it’s an event, it fires any time you add a tag, regardless of the wait between each time.

1 Like

So would I need to add the tags within the script? I’m currently using a tag editor.

All tags you add using the tag editor will automatically be covered in the for loop. All tags you add manually will automatically be covered by the :InstanceAddedSignal.

1 Like

Ah, but that’s what I mean. The for loop runs through individually, rather than simultaneously.

As of now there isn’t a way to run things at the same time without using multiple scripts (which is generally considered bad practice).

Coroutines emulate multithreadedness but only one of them run at a time, meaning it does the same as just using the for loop normally. You can validate this yourself: coroutine.running() only ever prints one thread.

I would do it without coroutines like this:

local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Item = ReplicatedStorage.Item
local ItemFolder = game.Workspace.ItemFolder

local models = CollectionService:GetTagged("models")
local newItems = {} -- use a table to reference the cloned items outside of the for loops scopes
-- use one for loop before the yield
for i,model in pairs(models) do
			local newItem = Item:Clone()
			newItem.Parent = ItemFolder
			newItem.CFrame = model.CFrame
			newItem.Anchored = true
			newItems[i] = newItem
end
--yield 5 seconds
wait(5)
--use a second for loop after the yield to do the destroying
for i,model in pairs(newItems) do
			model:Destroy()
end
--define variables nil for garbage collection
newItems=nil
models=nil
1 Like