Is it possible to assign every player in the server 1 specific collision group? Therefore, each player would collide with their assigned stuff (put in their group) but not with the other players and whatever they have?
If so, how can it be done?
Is it possible to assign every player in the server 1 specific collision group? Therefore, each player would collide with their assigned stuff (put in their group) but not with the other players and whatever they have?
If so, how can it be done?
Yes! That’s definitely possible, and quite easy too.
First, register a new CollisionGroup every time a player joins using PhysicsService with the method PhysicsService:RegisterCollisionGroup(). Then wait for their character to load and set all of their character to that collision group.
You can then use PhysicsService:CollisionGroupSetCollidable() method to make that player not be able to collide with anything else other than the default collision group and themselves.
Make sure to unregister the collision group after the player leaves:
local Players = game:GetService("Players")
local PhysicsService = game:GetService("PhysicsService")
-- registers collision group and turns off collision for other groups
function RegisterCollisionGroup(plr: Player)
PhysicsService:RegisterCollisionGroup(plr.Name) -- or user id
local groups = PhysicsService:GetRegisteredCollisionGroups()
for _, collisionGroup in pairs(groups) do
if collisionGroup.name ~= "Default" and collisionGroup.name ~= plr.Name then -- if you have more default collision groups then add them here
-- make them non-collidable both ways
PhysicsService:CollisionGroupSetCollidable(plr.Name, collisionGroup.name, false)
PhysicsService:CollisionGroupSetCollidable(collisionGroup.name, plr.Name, false)
end
end
end
-- applies to base parts (torso, head, legs, etc)
function ApplyCollisionGroupToCharacter(name: string, char: Model)
for _, part in pairs(char:GetDescendants()) do
if part:IsA("BasePart") then
part.CollisionGroup = name
end
end
end
Players.PlayerAdded:Connect(function(plr)
RegisterCollisionGroup(plr)
plr.CharacterAdded:Connect(function(char)
ApplyCollisionGroupToCharacter(plr.Name, char)
end)
end)
Players.PlayerRemoving:Connect(function(plr)
pcall(function()
PhysicsService:UnregisterCollisionGroup(plr.Name)
end)
end)
Then just register the objects they own to their collision group (player.Name)
Note that objects that used to be owned by a player will be able to be interacted with by other players after they leave!
Can you just tell me what methods I can use?
I plan on assigning each player to a certain thing, which they can move around. It’s already in the workspace, but I don’t really know how to assign every player to their thing, and reset it when they leave to keep it free for the next player. I’m really confused on making this system, and any tips are welcome
You mean like pre-defined collision groups orr?
Yes, I predefined 8 player groups which isn’t a bad number of max players per server
Then you can assign one of the pre-defined collision groups to the player using tables and a for loop to check for availability. I’m guessing you already have collidables assigned, so I’ll be removing the RegisterCollisionGroup() function and PhysicsService, as we don’t need it anymore:
local Players = game:GetService("Players")
-- Replace with your pre-defined collision groups:
local CollisionGroups = {
"Player1",
"Player2",
"Player3",
"Player4",
"Player5",
"Player6",
"Player7",
"Player8"
}
-- Keep track of occupied groups:
local PlayerAssignedGroup = {}
function SelectCollisionGroup()
for _, group in pairs(CollisionGroups) do
if not PlayerAssignedGroup[group] then
return group
end
end
return nil
end
function ApplyCollisionGroupToCharacter(name: string, char: Model)
for _, part in pairs(char:GetDescendants()) do
if part:IsA("BasePart") then
part.CollisionGroup = name
end
end
end
Players.PlayerAdded:Connect(function(plr)
local group = SelectCollisionGroup()
if not group then return end-- If there's no availability for some reason, then don't make the script error
PlayerAssignedGroup[group] = plr.Name
plr.CharacterAdded:Connect(function(char)
ApplyCollisionGroupToCharacter(group, char)
end)
end)
Players.PlayerRemoving:Connect(function(plr)
local assignedGroup
for group, username in pairs(PlayerAssignedGroup) do
if username == plr.Name then
assignedGroup = group
break
end
end
if assignedGroup then
PlayerAssignedGroup[plr.Name] = nil
end
end)
Now you can change the CollisionGroups table to have them be your pre-defined collision groups.
If you need anything else tell me!
Very bad example and i understand what OP meant.
First of all creating collision groups during runtime is bad;
Better solution would be create groups “Player_1” and “Player_2” and such
Assign group to corespoding player that are “free” aka pooling free groups.
Also dont use pairs iterator please;
Generic iteration > next iterator
So do you have a more efficient suggestion? Can you explain me how to apply it and how it works?
You’re only partly right. Creating collision groups at runtime is not inherently bad, and the first script handles it perfectly fine. It’s a small game supposed to allow only a few people.
This is exactly what I did in the second script.
Roblox can handle creating a handful of collision groups easily. Pooling only helps if you have hundreds of players joining/leaving constantly, which is extremely rare. Remember that this game is only supposed to have 8 players at most.
Pairs is literally a wrapper for next. The perfomance difference in this case is practically none.
You’re overcomplicating a simple system. The scripts are already efficient, readable, and perfectly reasonable for an 8 player game.
Not quite.
print(pairs({})==next) --false
Its more of a macro internally for Luau interpeter most likely.
When I said pairs is a wrapper for next, I meant that internally it returns next as the iterator function along with the table and initial key, so the iteration itself uses next to traverse the table. It’s not literally equal to next, but it relies on next.
pairs({}) == next is false because, as I said earlier, pairs returns three values: (iteratorFunction, table, startKey), not just the function itself.
Using pairs is 99.9% fine of the time. If you look at any script they’re probably either using pairs or ipairs. No one should be worrying about this micro-level nuance.
That’s not how tuple type works in Luau.
It grabs the first argument; if you want to grab the 2nd, you should shift it using the select() function.
next, and the thing pairs returns are 2 separate functions with different hashes, although the same behavior.
Because, as I said, pairs is not a wrapper but a macro.
Edecate yourself before confronting someone about such stuff.
ipairs is a macro for inext through inside bytecode.
I understand you’re talking about internals, but my point was never that pairs({}) == next. I specifically said that pairs returns next as the iterator function along with the table and start key, so the iteration itself uses next to traverse the table.
Whether Luau internally implements pairs as a macro, or ipairs as a macro using inext in the bytecode, or handles tuple types differently, or has different function hashes doesn’t change the practical point: the iteration relies on the iterator function, and for almost all Roblox scripting purposes, pairs and ipairs work as expected.
The main point: pairs is safe, readable, and widely used. The micro-level internals don’t affect how you should use it.
Also, the “educate yourself” comment is unnecessary. Pointing out internal implementation details doesn’t make the original statement about iteration incorrect, and the advice could be given without being dismissive.
pairs and ipairs are perfectly fine for scripting and are widely used in official examples. Using next directly is more verbose and offers no practical benefit in typical scripts.
That’s not true;
You are simply using legacy code/Lua code, and it does not suit 2025 standarts ![]()
Use of pairs and/or ipairs also requires reserving a register for that, which could be interpreted as micro-deoptimization for someone like me.
Moreover, generalized iteration uses absolutely different bytecode instruction for loops.
Generalized iteration:
pairs/next:
A little off-topic, but it turns out Luau bytecode compiler devs are pretty bad at optimizing bytecode.
for i,v in {} do
print(v)
end
local print = print
for i,v in {} do
print(v)
end
Not this prolefeed code dumb compiler produced in the first example ![]()
At least the compiler is smart enough to treat my local print = print as a forced localization and has put GETIMPORT here. That at least some respect it had earned from me.
The whole “2025 standards” claim is nonsense. There is no new rule that suddenly makes pairs or ipairs invalid. They are still part of Luau, still supported, and still used everywhere, including Roblox’s own CoreScripts and official examples. If they were actually outdated, Roblox would mark them as such, but they haven’t.
What you’re pointing out is just bytecode trivia. Yes, generalized iteration compiles slightly differently than pairs/ipairs, but the difference is microscopic and has no measurable impact in gameplay. Calling that a “micro-deoptimization” is just nitpicking for the sake of it.
It doesn’t matter whether you use pairs or next, the performance difference is almost nonexistent in typical scripts (proven by yourself). The script works perfectly fine, was tested, and behaves exactly as intended. Acting like this is a serious standard issue just misleads people and wastes time.
@v1xyypawz @Yarik_superpro
Soo about my issue, does any of you have any more suggestions or I should just figure out how the script works and apply it, leaving the rest completely up to me? I’ll start doing that
You can put the second script I gave you into ServerScriptService. There’s not much more to do other than change the table.
Obviously you can change some other stuff if you want or mix it with another script, but the base script already does what it’s intended to do.