I don't get why I should use Collection Service

I have recently picked up on collection service, but from my understanding I don’t really have a use for it. From what I know Collection Service is like a invisible folder. You can make different folders by setting different tags on them. My question is why can’t you just make a regular folder.

For example with stuff like kill bricks, you can set a tag for all of them. When you run getTagged it returns all the parts with the tag. Why can’t you just make a regular folder for the kill bricks, loop through them and put a touched event on that way?

Also I need a explanation of OOP and Collection service together

2 Likes

You could easily make a folder full of parts and just iterate through them, but depending on your game’s organization some of the same objects may have different parents. For example, lets say you have multiple maps and you have kill bricks in each map… You want to designate the kill bricks as a child of the map it’s in, yet all kill bricks do the same thing.
I’m not sure where OOP and Collection Service relate to each other.

1 Like

Thats a good point about the different maps thing but for OOP this is what it says on the wiki and I’m a bit confused. I guess for the killbricks I can use different methods with OOP?

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.

16 Likes

Wow thanks for posting such a detailed article, I wish I could give you more hearts

2 Likes

I have a question for this for example If I added a new tag this would fire

CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(onKillBrickAdded)

and then it would fire onKillBrickAdded, would the part parameter just come automatically because right now theirs no part parameter attached to the onkillbrickadded.

Yes. Connecting a function to a signal means “when this signal is fired, this function will be called with any arguments the signal provides.”

For the signal returned by GetInstanceAddedSignal(TAG_NAME), it fires the connected function with the instance which was added.

You might have seen anonymous functions before, which do the same thing I did but let you define the connection and function in the same line:

CollectionService:GetInstanceAddedSignal(TAG_NAME):Connect(
    function(partThatWasAdded) -- defining a function without a name directly in the Connect method
        print("Part added to collection!", partThatWasAdded.Name)
    end
)
2 Likes