CollectionService in a nutshell


Hello! This is a post about CollectionService, in a nutshell.

What’s CollectionService? Well, CollectionService is a service which allows you to manage groups of instances using tags which are simple strings, but you can’t see them.

CollectionService is very useful since it allows us to run functions or change properties from large groups of parts with a single script. Instead of having 10 scripts that do the same, you only have 1. Learning how to use this service will save us tons of lanes of code.

Let’s start by learning the basics about CollectionService, let’s create our first tag and let’s tag our first part.


1. Learning the Basics

Before starting with the functions, let’s set up our variables

local CollectionService = game:GetService("CollectionService") 

--The service we're going to use

local instance0 = Instance.new("Part") 
instance0.Name = "instance0"
local instance1 = Instance.new("Folder") 
instance1.Name = "instance1"

-- The instances we're going to work with, a part and a folder, 
-- remember that any instance can be tagged.

:AddTag(instance, string)

Our first function is :AddTag() and it will basically tag an instance with the given tag. If the tag does not exist, :AddTag() will create a new tag and then will tag the given instance.

CollectionService:AddTag(instance0, "Tag_A")

But… we can’t know if the part is actually tagged, because the tag is invisible for us even in studio, so let’s see the next one.


:GetTags(instance)

:GetTags() will return a table with all the tags of the given instance, keep in mind that it returns tables and not strings.

local instance0_tags = CollectionService:GetTags(instance0)
local instance1_tags = CollectionService:GetTags(instance1)

print(instance0_tags) --> {"Tag_A"}
print(instance1_tags) --> {}

-- Keep in mind that :GetTags() returns an array of strings, not a string.

print(instance0_tags[1]) --> "Tag_A" 

:GetTagged(string)

:GetTagged() is pretty similar to :GetTags(), but instead of returning the tags of an instance, it will return all the instances with a given tag.

local tagged_instances_A = CollectionService:GetTagged("Tag_A")
local tagged_instances_B = CollectionService:GetTagged("Tag_B")

print(tagged_instances_A) --> {instance0}
print(tagged_instances_B) --> {}

-- Keep in mind that :GetTagged() returns an array of instances, not an instance,
-- neither a string.

print(tagged_instances_A[1])      --> instance0
print(tagged_instances_A[1].Name) --> "instance0"

:RemoveTag(instance, string)

:RemoveTag() removes a desired tag from an instance, and yup… that’s all it does.

CollectionService:AddTag(instance0, "Tag_A")
CollectionService:AddTag(instance0, "Tag_B")

print(CollectionService:GetTags(instance0)) --> {"Tag_A", "Tag_B"}

CollectionService:RemoveTag(instance0, "Tag_A")

print(CollectionService:GetTags(instance0)) --> {"Tag_B"}

:HasTag(instance, string)

This one is important, so watch out. :HasTag() checks if an instance has got a specified tag, the value in return will be true or false, depending on wherever the instance has got that tag or not.

CollectionService:AddTag(instance0, "Tag_B")

if (CollectionService:HasTag(instance0, "Tag_A")) then
	print("yeah cool, you won some beans")
else 
    print("uhhh you've been a bad boy")
end

--> Output: "uhhh you've been a bad boy"

:GetInstanceAddedSignal() & :GetInstanceRemovedSignal()

These 2 functions will fire a callback function when an instance is added/removed from a specified tag.

CollectionService:GetInstanceAddedSignal("Tag_A"):Connect(function(instance)
    print(instance.Name.." marked as Tag_A")
end)

CollectionService:GetInstanceRemovedSignal("Tag_A"):Connect(function(instance)
    print(instance.Name.." unmarked as Tag_A")
end)

CollectionService:AddTag(instance0, "Tag_A") --> "instance0 marked as Tag_A"
CollectionService:RemoveTag(instance0, "Tag_A") --> "instance0 unmarked as Tag_A"

2. Using CollectionService

Now that we know all the functions, let’s see what we can do with this.

As said before, with CollectionService we can run functions or change properties for big groups of instances, how? Let’s take a look on how to do it.

So imagine that you have 3 parts and you want them to kill the player if it touches them, what would you do?

Untitled (6)

No! Never do that again, that’s time consuming and can even lead to problems that we’ll see now. Just stop doing that now and do this:

Untitled (3)

Okay that’s better. In this situation we only have 3 parts, so it would be very easy to just put the same script inside the 3 parts and job done, but imagine that you wanted to change the script because it doesn’t work correctly but instead of 3 parts you have 500. In this case CollectionService is going to be a hero for us, by using CollectionService we can change the same script for all the parts without having to update one by one the scripts.

Oh thanks god, this could’ve saved my time a lot of times when I didn’t know about CollectionService.

local CollectionService = game:GetService("CollectionService")

--: The first thing we have to do is loop through the parts tagged as "killers". 

for _, part in pairs(CollectionService:GetTagged("killers")) do
	
	--[[
	Now that we've got our killer parts we're gonna bind them to a function,
    so when they get touched they will fire that function.
	--]]
	
	part.Touched:Connect(function(hit)
		--: Killing brick script, nothing new right?
		
		if (hit.Parent:FindFirstChild("Humanoid")) then
			local humanoid = hit.Parent.Humanoid
			
			humanoid.Health = 0
		end
	end)
end

And now we have our 3 working killer parts by just using a script and about 25 lines of code. The best thing is that we can easily change the script whenever we want and all the parts will still work. Again, this is a little example, just imagine having 300 killing bricks without CollectionService and noticing an error in the script, ouch…

Well, maybe while reading the previous code you were thinking about using for loops to iterate through all the parts and connect them to a function, actually, that is what we did in the code above us, but there’s one little difference between using for loops and using CollectionService. If you wanted to use for loops to do this you would have to give the parts a Key name or insert some sort of value into them so the script could know they are the correct parts, by using CollectionService you can avoid those steps.

If you’re going to use CollectionService for your game, I really, really (just do it) recommend you installing Tag Editor Plugin, you can find a detailed explanation about it in the next paragraph.


3. Editing tags easily

Tag Editor Plugin

As I said before, @Sweetheartichoke’s plugin Tag Editor Plugin is a plugin that makes it easier & faster to manipulate tags on Roblox Studio.

If you want detailed information about the Plugin and you want to get it, check the original post linked above, here I’m gonna show you the basics and how to use this plugin to make your life much more easier when using tags.

Let’s imagine I want to create a new tag for the killing parts, instead of creating a whole script using :AddTag() & looping through a folder or group of parts, I will open Tag Editor and I will type the name of my new tag

image

Press enter and… there it goes, your new tag without having to script nothing

image

Now that we have our tag, we just have to select the parts and apply that tag to the parts.

If you want to know if you really tagged your parts, you can check it out activating World View. (also from the Tag Editor Plugin).

image

And now you will be able to see an outline or a box surrounding all the parts tagged with the tag that you selected.

And now that you have your tags created you can freely use them in your scripts, amazing!


4. Common solutions for common problems

Common questions

  • Do tags replicate from server-to-client and vice versa?

    Yes, but only from server-to-client. Client tags will not replicate to something other than the client.

  • Can I use CollectionService in local scripts?

    Yes, as far as you’re trying to access tags that were replicated from the server to you or tags that your own client added.

  • Do Tag Editor Plugin tags replicate to everybody?

    In theory yes, but in some occasions (specially when assets are loaded/unloaded) its tags may not get replicated to the client nor the server.

  • Can I do x with CollectionService?

    As long as your task includes instances that should/could be grouped or so, yes. But be aware! CollectionService may not be useful in all cases.

    Managing DataStoreService values with tags would be a pretty bad idea, those things should be handled by dictionaries or arrays from the server with no services other than the needed ones.

  • Tags must be added in play-time?

    Tags can also be added in studio mode by using the command bar and tags will persist through sessions. There’s no guarantee that they’ll persist forever, specially if you modify or change tagged instances.


Well that’s all about CollectionService, hope It was useful. If you want to know more about the Tag Editor Plugin just go and check the official post and get it!

177 Likes
CollectionService vs Loops Which Is Better And In What Situations?
Disease System and Cures
How to turn all lights off from one button?
Rojo question: How to add Touch Event to Part
Need help converting these scripts into one script by CollectionService
How do i make it print when player touched one of certain part without making another script for every part
For loop detection, including things added after said loop is run (help)
Is it possible to optimize my scripts?
How to make one script act on several parts?
BrickColor.Random Exceptions
Performance worries: using .Heartbeat to run a function multiple times; contains a loop (for every individual vehicle)
Performance worries: using .Heartbeat to run a function multiple times; contains a loop (for every individual vehicle)
Performance worries: using .Heartbeat to run a function multiple times; contains a loop (for every individual vehicle)
BrickColor.Random Exceptions
Random teleport after reset
OOP enemy system cannot spawn more then one enemy at a time
Help understanding modules
How does the project format in Rojo compare to the standard format used within studio
Is there a problem with using a for loop to connect events for multiple objects?
RemoteEvents to do custom SFX?
How do I make scripts copy from a main script
How to make Humaoid move along specific path?
I need help with CollectionServer
Need help on kill detection/increase
How to detect walls around player
Need help with resource mining system
Too many parts that need scripts
AI Optimization issue

Thank you so much! You have no idea how useful this is to me, when my game has over 500 scripts…
A couple of suggestions for this tutorial:

  • Show us how to make a script for the parts (so not just an event)
  • Maybe check your grammar, but then again who cares?
7 Likes

Gonna look in adding more examples :wink:

Sorry, but I’m from Spain and I did my best to do not use google translate :sweat_smile:

8 Likes

Hola, para saber los ganadores al terminar una ronda , podrías enseñar como hacerlo aplicando Collection Service :nerd_face:

3 Likes

Well my problem is that how do I run this with a object that is added to the game while the for loop is running like if I already have kill parts then I add another one with the tag it won’t work. How would I do that??

What I do in my projects is making a function called “UpdateTags” so everytime I know a new part is added I fire that function which fires again the for loop to connect all the new part/s.

1 Like

CAn you show me an example of this and also I don’t want mine to fire again for the parts already tagged.

local CollectionService = game:GetService("CollectionService")

local TagNames = {
	PadTag = "PadObject",
	JumpPad = "JumpPad",
	SpeedPad = "SpeedPad",
	MagnetPad = "MagnetPad",
}

local TagHandler = {}

function TagHandler.UpdateTags(group)
	if (typeof(group) == "Instance") then
		for _, object in pairs(group:GetDescendants()) do
			if (object:IsA("BasePart") and object:FindFirstChild("PadConfiguration")) then
				CollectionService:AddTag(object, TagNames.PadTag)

				if (object.PadConfiguration:FindFirstChild("Type") and type(object.PadConfiguration.Type.Value) == "string") then
					for _, name in pairs(TagNames) do
						if (object.PadConfiguration.Type.Value:match(name)) then
							CollectionService:AddTag(object, object.PadConfiguration.Type.Value)
						end
					end
				end
			end

			if (object:IsA("BasePart") and object:FindFirstChild("CanKill") and object.CanKill:IsA("BoolValue")) then
				CollectionService:AddTag(object, "CanKill")
			end
		end
	end
end

return TagHandler

That’s the code that I use in my project, basically what the code does is checking all the parts and looking into them for some sort of value with a name, if the script finds the value then that part is tagged.

An easier example is something like this:

function UpdateTagFunction(Tag)
   if Tag == "killBrick" then
       for _, part in pairs(game:GetService("CollectionService"):GetTagged(Tag)) do
          part.Touched:Connect(...)
      end
   end
end

If you have multiple tags you can do

function UpdateTagFunction(Tag)
   if Tag == "killBrick" then
      for _, part in pairs(game:GetService("CollectionService"):GetTagged(Tag)) do
          part.Touched:Connect(...)
      end
   elseif Tag == "otherTag" then
       --other thing
   end
end

Then you must create some sort of array containing the parts you recently tagged and only tag & connect the parts into the array

2 Likes

Thanks for everything… I’ll use this to fix my stuff.

do tag changes replicate client → server?

Tags will only replicate from the server to client, not from client to server.

4 Likes

This was one explanation amongst many that I fully understood, thank you very much!

1 Like

Actually, his grammer was amazing considering hes from spain. Loved the post!!!

3 Likes

I feel like this module will be really helpful for some of my game mechanics!

This isn’t a module btw, it’s a service CollectionService | Roblox Creator Documentation.

3 Likes

image

Great tutorial! I was able to set this up easily for parts, but is it possible to use CollectionService for local scripts I put inside StarterGui? i.e. Within a player’s GUI there are exit buttons in multiple places with a local script that closes all screens. I’ve tagged all of these exit button instances, but I don’t understand what the next steps would be since all the examples I can find online only deal with server scripts. It doesn’t seem like the structure above would work if “killing brick script” was local right?

2 Likes

It should just be like a server script.

1 Like

Tags don’t replicate to the server, only from. Checking them client side is not an issue.

2 Likes

Tags can be added by the Server and the Client, but only server-made ones will replicate to all clients meaning that you could and should use a LocalScript for GUI tagging since they can only be seen and accessed by a specific client.

This is the replication scheme:

Server → :AddTag() → Tag is added → Tag replicates to other clients
Client → :AddTag() → Tag is added

Server-made tags can be accessed by: Server, Client, Other Clients
Client-made tags can be accessed by: Client

It would, but only for the client that was in control or had ownership of the LocalScript. In that case, the client would see them as tagged bricks and would die if it was to touch them, but other players would not die since they won’t even see the tag in a first place.

3 Likes

I would add, for security of our games, be very careful what you let the client monitor with regards to tags. For example, I add a GUID tag to all spawned items to prevent duplication, but only the server can see or access the master list.

1 Like