CollectionService is just a way to maintain sets of Instances. It is sort of like a folder, but more useful, and probably more efficient.
Benefits
Checking if a specific Instance is in a Collection, for example, is going to be quicker than looping through all the children of a folder to check if that Instance is a child.
It also lets you separate “where an object is” from “what an object is” – if you had a Tag that meant “this part can kill people who touch it”, you can throw those parts wherever you want in your workspace hierarchy and give them the Tag. If you used a folder, well, all of those parts now need to be in one specific place in your hierarchy, which might not make much sense.
OOP
The CollectionService page has good info about why OOP is useful to use with Collections:
The pattern goes like this: when a tag is found ( CollectionService:GetTagged
and CollectionService:GetInstanceAddedSignal
), create a Lua object with the Roblox instance. When it is removed ( CollectionService:GetInstanceRemovedSignal
), call a cleanup/destroy method within the Lua object. See the code samples for a better idea of how this can be done.
Tags are a way to extend the functionality of Instances. That might mean making a part into a kill-brick, for example.
Since you can’t create literally create new Instance types like Instance.new("KillBrick")
, the next-best option is to create the Part
, and then attach additional functionality with a Lua-only object that handles the “killing” part. Of course, you could do this without OOP:
local CollectionService = game:GetService("CollectionService")
local TAG_NAME = "KillBrick"
-- non-OOP version: just maintain a list of the connections to cleanup
local touchedConnections = {}
-- called when a new brick is added to the set
local function onKillBrickAdded(part)
-- create the touched connection
local connection = part.Touched:Connect(function(hit)
print("KILLING", hit.Parent)
-- etc.
end)
-- save the connection so we can clean it up if the part is removed
touchedConnections[part] = connection
end
local function onKillBrickRemoved( part )
if (touchedConnections[part]) then
-- cleanup connection
touchedConnections[part]:Disconnect()
touchedConnections[part] = nil
end
end
-- add existing parts
for _, inst in pairs(CollectionService:GetTagged(TAG_NAME)) do
onKillBrickAdded(inst)
end
-- listen for new ones (add Connection to the list)
CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(onKillBrickAdded)
-- listen for removed ones (remove Connection from the list)
CollectionService:GetInstanceRemovedSignal(TAG_NAME):Connect(onKillBrickRemoved)
And that’s fine, but what if you have more than a single Touched
connection to clean up? What if, if a brick is a kill-brick, you also want to add a particle effect of flames to the Part, and you have a SurfaceGui on the side that shows a skull and crossbones? What if you create a coroutine that spins the part in a circle forever (if it’s a kill brick)?
Now, your touchedConnections
table is going to start to look like this:
{
Part1 = {
touchedConnection = ...,
particles = ...,
surfaceGUI = ...,
spinCoroutine = ...
},
Part2 = {
touchedConnection = ...,
particles = ...,
surfaceGUI = ...,
spinCoroutine = ...
}
...
}
, allowing you to remove all of those things from a part when it’s tag is removed. Call one of those sub-tables (with touchedConnection
, particles
, etc. in it) the properties of a class that you created, like KillBrick
, and you’ve just invented OOP!
Edit: Also, maid classes come in pretty handy for this sort of thing.