So we all know about those classic group only doors that kill all players who touch them, should they not be in the relevant group.
This is 2020! As scripters, we have some incredible mechanics at our disposal to create some really well-developed assets that carry out the same purpose. And that’s what we’ll do.
In this thread, we will be utilising the Collisions section of the Physics Service to create a swinging door that is still group-only. Something that I personally haven’t seen a lot of.
Understanding Collisions
To begin with, we need to get our head around how Roblox deals with collisions. Firstly, we’re more professional than the standard “CanCollide” property attached to the BasePart class. Below you can see a video by Roblox demonstrating some of the collisions we’re going to be working with:
Notice how only some of the spheres pass through the block. We will be using this exact logic to create our team only door, substituting spheres for player characters and blocks for our doors.
If you’re unfamiliar with collisions on Roblox (using the PhysicsService), please familiarise yourself with the following article before we continue: Collision Filtering
Our Algorithm
Let’s tackle how we can stop some players going through a door and how to let some through. Well, we can create a single block that stops all players and manually set the collisons to off for players in the right group.
So our algorithm looks something like this:
Luckily, Roblox takes care of this for us through the use of the Collisons grouping. So all us developers need to do it tell Roblox which characters to let through, and which to not.
Now we have the basic premise of how our script is going to work, we can start dealing with things like data structures and planning exactly how our algorithm is going to work - considering all the cases we can think of: two or more doors per group, player’s in more than one applicable group etc.
Setting out exactly what we need to start up the collisions for the doors:
And setting out exactly what we need to start up the collisions for the players:
We are now ready to develop our doors, knowing exactly what we need.
Setting Up Our Door
As always, before we can start scripted we need to make sure our explorer is in order and it has everything we need. I’m going to go ahead and grab the “Door” official model created by Roblox which has our swinging functionality. If you would like to create this door yourself, you can see the following videos created by Roblox:
See videos
1. Making a Door: Vertical Hinge
2. Making a Door: Limits
3. Making a Door: Closing with Springs
So we have our door in studio now.
My explorer
Now let’s make our edits. We need to consider lots of cases to make our script truly robust, so let’s say that we’re publishing our model so anyone can use it. We’re going to need some configurations then - the only editable property other developers really need to change will be the GroupId.
My explorer
Finally, let’s create that part that will stop all players from walking through the door.
By making this part transparent and anchored, we can ensure that only whitelisted players can travel through this by scripting the collisions as per our algorithm above.
My explorer
To incorporate the case of multiple doors, it’s necessary to organise the doors such that they have a common parent in the explorer. To make things neat, I created a folder called “TeamDoors” which also allows us to easily iterate through all the doors in a single for
loop.
Scripting
Finally we can start writing our code. We will firstly tackle setting up our collision groups for our doors; checking whether the group assigned to the door already has a collision group created before another one is made.
-- Roblox Services
local PhysicsService = game:GetService("PhysicsService")
-- Check to see whether a Collision Group already exists
function CollisionGroupExists(ID)
local CollisionGroups = PhysicsService:GetCollisionGroups()
for i = 1, #CollisionGroups do
if CollisionGroups[i].name == "GroupDoor_" .. ID then
return true
end
end
return false
end
-- Initialise Group Only Door Collisions
for _, Door in pairs (workspace:FindFirstChild("TeamDoors", true):GetChildren()) do
local Configuration = Door.Configuration
if not CollisionGroupExists(Door.Configuration.GroupId.Value) then
PhysicsService:CreateCollisionGroup("GroupDoor_" .. Configuration.GroupId.Value)
end
end
Next, we need to set up the player characters to have their own collision group:
-- Array to store all groups that have a group-door
local IDs = {}
-- Initialise Player Collisions
game.Players.PlayerAdded:Connect(function(Player)
for i, ID in pairs (IDs) do
if Player:IsInGroup(IDs[i]) then
Player.CharacterAdded:Connect(function(Character)
for _, Part in pairs (Character:GetDescendants()) do
if Part:IsA("BasePart") then
PhysicsService:SetPartCollisionGroup(Part, "Players_" .. IDs[i])
end
end
end)
end
end
end)
You’ll notice that we just added a player’s character to an existing Collisions group without first initialising it, this is because we need to make an edit to the original function:
for _, Door in pairs (workspace:FindFirstChild("TeamDoors", true):GetChildren()) do
local Configuration = Door.Configuration
if not CollisionGroupExists(Door.Configuration.GroupId.Value) then
PhysicsService:CreateCollisionGroup("GroupDoor_" .. Configuration.GroupId.Value)
PhysicsService:CreateCollisionGroup("Players_" .. Configuration.GroupId.Value)
PhysicsService:CollisionGroupSetCollidable("GroupDoor_" .. Configuration.GroupId.Value, "Player_" .. Configuration.GroupId.Value, false)
end
PhysicsService:SetPartCollisionGroup(Door.CollisonsHandler, "GroupDoor_" .. Configuration.GroupId.Value)
end
Conclusion
That’s it! In less than 50 lines of code, we have created a really sleek and modern take on the classic group-only door mechanic. Feel free to add any and all features such as killing the player when they’re not allowed through or teleport pads or anything you can think of!
I hope you enjoyed this tutorial,
-Tom
Full code
-- Script by ThomasMGardiner
-- Roblox services
local PhysicsService = game:GetService("PhysicsService")
-- System variables
local IDs = {}
-- Check to see whether a Collision Group already exists
function CollisionGroupExists(ID)
local CollisionsGroups = PhysicsService:GetCollisionGroups()
for i = 1, #CollisionsGroups do
if CollisionsGroups[i].name == "GroupDoor_" .. ID then
return true
end
end
return false
end
-- Initialise Group Only Door Collisions
for i, Door in pairs (workspace:FindFirstChild("TeamDoors", true):GetChildren()) do-- Initialise Door-Player Collisions
local Configuration = Door.Configuration
if not CollisionGroupExists(Door.Configuration.GroupId.Value) then
IDs[i] = Configuration.GroupId.Value
PhysicsService:CreateCollisionGroup("GroupDoor_" .. Configuration.GroupId.Value)
PhysicsService:CreateCollisionGroup("Players_" .. Configuration.GroupId.Value)
PhysicsService:CollisionGroupSetCollidable("GroupDoor_" .. Configuration.GroupId.Value, "Players_" .. Configuration.GroupId.Value, false)
end
game.PhysicsService:SetPartCollisionGroup(Door.TeamHandler, "GroupDoor_" .. Configuration.GroupId.Value)
end
-- Initialise Player Collisions
game.Players.PlayerAdded:Connect(function(Player)
for i, ID in pairs (IDs) do
if Player:IsInGroup(IDs[i]) then
Player.CharacterAdded:Connect(function(Character)
print("hi")
for _, Part in pairs (Character:GetDescendants()) do
if Part:IsA("BasePart") then
PhysicsService:SetPartCollisionGroup(Part, "Players_" .. IDs[i])
end
end
end)
end
end
end)