How to correctly use collection service?

I created buttons in my game that can be clicked on and will self-destruct after a certain amount of time. Right now, each button has its own script, and it gets cloned into the workspace from the server storage. But, I’m looking for ways to put all the button logic into one script, so it’s easier to manage.

I looked into collection service and got a plugin to tag all the button models in the server storage. I was hoping I could just copy-paste the code into this for loop, but it doesn’t work. I tried fixing it by adding a while true loop so that the “CollectionService: GetTagged(“Button”)” can update the list of buttons, but that doesn’t work also. I’m not sure what to do, so any help and feedback are appreciated!

local CollectionService = game:GetService("CollectionService")

while true do
	for _,buttonModel in ipairs(CollectionService:GetTagged("Button")) do
		local hitbox = buttonModel:WaitForChild("Hitbox")
		local clickDetector = hitbox:WaitForChild("ClickDetector")

		-- [[ Configuration ]] --
		local configuration = buttonModel:WaitForChild("Configuration")
		local POINT_VALUE = configuration:WaitForChild("PointValue").Value
		local EXPLOSION_CHANCE = configuration:WaitForChild("ExplosionChance").Value
		local EXPLOSION_DELAY = configuration:WaitForChild("ExplosionDelay").Value
		local BLAST_PRESSURE = configuration:WaitForChild("BlastPressure").Value
		local BLAST_RADIUS = configuration:WaitForChild("BlastRadius").Value
		local SELF_DESTRUCT_TIME_MIN = configuration:WaitForChild("SelfDestructTimeMin").Value
		local SELF_DESTRUCT_TIME_MAX = configuration:WaitForChild("SelfDestructTimeMax").Value

		local function explode()	
			print("explode code here")
		end
		
		local function pressButton(player)
			print("clicked code here")
		end

		clickDetector.MouseClick:Connect(function(player)
			pressButton(player)
		end)
		
		-- Self-destruct
		local delayTime = 
math.random(0,10)
		wait(delayTime)
		explode()
	end
	wait()
end
1 Like

Have you printed out what this is returning? Any errors? Seems like there could be a syntax error since there’s a lot of wait for childs in the script.

Here is some edited code. The changes and reasons for them are detailed in the comments.

local CollectionService = game:GetService("CollectionService")

--while true do --With events you only need to connect them once.
for _,buttonModel in ipairs(CollectionService:GetTagged("Button")) do
	--Gets the instances of the tagged "Button"
	local hitbox = buttonModel:WaitForChild("Hitbox")
	local clickDetector = hitbox:WaitForChild("ClickDetector")

	-- [[ Configuration ]] --
	local configuration = buttonModel:WaitForChild("Configuration")
	local POINT_VALUE = configuration:WaitForChild("PointValue").Value
	local EXPLOSION_CHANCE = configuration:WaitForChild("ExplosionChance").Value
	local EXPLOSION_DELAY = configuration:WaitForChild("ExplosionDelay").Value
	local BLAST_PRESSURE = configuration:WaitForChild("BlastPressure").Value
	local BLAST_RADIUS = configuration:WaitForChild("BlastRadius").Value
	local SELF_DESTRUCT_TIME_MIN = configuration:WaitForChild("SelfDestructTimeMin").Value
	local SELF_DESTRUCT_TIME_MAX = configuration:WaitForChild("SelfDestructTimeMax").Value
	--Functions
	local function explode()	
		print("explode code here")
	end
		
	local function pressButton(player)
		print("clicked code here")
	end
	--Connections
	clickDetector.MouseClick:Connect(function(player)
		pressButton(player)
		-- Self-destruct --I assume you want it to self destruct AFTER the button is pressed, not when the game starts.
		local delayTime = math.random(0,10)
		wait(delayTime)
		explode()
	end)
end
--wait()
--end

If you used while true to run the code each time a tag is added, let me know and I can show you a better way. I’d highly recommend using CollectionService:GetInstanceAddedSignal instead.

You could also change math.random(1, 10) to math.random(SELF_DESTRUCT_TIME_MIN, SELF_DESTRUCT_TIME_MAX).

2 Likes

It returns all the buttons that have that tag, but it doesn’t update as soon as new buttons are spawned in. When I printed it out there were ~50 buttons in the table, but in game there were already ~90 buttons.

The button actually explodes in two ways: There is a chance the button might explode in the pressButton function, and all buttons (even if it hasn’t been clicked) will self destruct after a certain amount of time, which is why the code is outside the click handler.

I tried the code without the while true loop, but when I print out “CollectionService:GetTagged(“Button”)” it will only include the buttons that are in the server storage.

Wait are you running this from the client or from the server? If you’re running it from the server you shouldn’t need to use :WaitForChild unless you add the child in game.

Are you adding more buttons in game (aka after the game starts with code)? If so then you need something like this:


local CollectionService = game:GetService("CollectionService")

--Initiates exploding button "buttonModel"
local function addExplodingButton(buttonModel)
	--Instances
	local hitbox = buttonModel.Hitbox
	local clickDetector = hitbox.ClickDetector

	-- [[ Configuration ]] --
	local configuration = buttonModel.Configuration
	local POINT_VALUE = configuration.PointValue.Value
	local EXPLOSION_CHANCE = configuration.ExplosionChance.Value
	local EXPLOSION_DELAY = configuration.ExplosionDelay.Value
	local BLAST_PRESSURE = configuration.BlastPressure.Value
	local BLAST_RADIUS = configuration.BlastRadius.Value
	local SELF_DESTRUCT_TIME_MIN = configuration.SelfDestructTimeMin.Value
	local SELF_DESTRUCT_TIME_MAX = configuration.SelfDestructTimeMax.Value
	
	local function explode()	
		print("explode code here")
	end

	local function pressButton(player)
		print("clicked code here")
	end
	--Connections
	clickDetector.MouseClick:Connect(function(player)
		pressButton(player)
	end)
	
	-- Self-destruct
	spawn(function()
		local delayTime = math.random(0,10)
		wait(delayTime)
		explode()
	end)
end

--Adds buttons for every already existing button
for _,buttonModel in ipairs(CollectionService:GetTagged("Button")) do
	addExplodingButton(buttonModel)
end

--Adds a button when the "Button" tag is added.
CollectionService:GetInstanceAddedSignal("Button"):Connect(addExplodingButton)

I also just noticed that your code yielded whenever it created a button. You should use spawn() or coroutines to avoid that.

2 Likes

Thank you so much! I didn’t fully understand what CollectionService:GetInstanceAddedSignal() was when I was reading the documentation until you commented on what it did.

Wait, really? I thought :WaitForChild was the safest thing to do when referencing different things. (because everyone says that the script will run before certain things are loaded in) Why isn’t it needed server-side?

So on the server you know that things should be there (unless you add them later with a script—note that statements run in order, so if you add something in a statement that runs before another one you can be sure it exists there). When you clone something it will always have its children.

On the client what happens is the instances are replicated, which basically means they are copied over time to the client (replication generally only takes a few seconds).

:WaitForChild should be used when you know that something will be a child eventually. You should reference it directly if it’s always there when the code runs.

:FindFirstChild is useful when you don’t want to wait for something but you aren’t sure if it’s there and don’t want to throw an error (to avoid an error you’d need to check if it exists then continue).

Generally if you aren’t sure you should use wait for child or find first child with existence checking (depending on the situation).

2 Likes

Ah, so that’s why we would use :WaitForChild for GUI because it replicates from StarterGui to the client’s PlayerGui, but it doesn’t load right away.

I got lots of code to clean up now hehe. Thank you for the explanation!

1 Like