Collision events occur when two BaseParts touch or stop touching in the 3D world. You can detect these collisions through the Touched and TouchEnded events which occur regardless of either part's CanCollide property value. When considering collision handling on parts, note the following:
The Touched event fires when a BasePart comes in contact with another, or with a Terrain voxel. It only fires as a result of physical simulation and will not fire when the part's Position or CFrame is explicitly set such that it intersects another part or voxel.
The following code pattern shows how the Touched event can be connected to a custom onTouched() function. Note that the event sends the otherPart argument to the function, indicating the other part involved in the collision.
local part = workspace.Partlocal function onTouched(otherPart)print(part.Name .. " collided with " .. otherPart.Name)endpart.Touched:Connect(onTouched)
Note that the Touched event can fire multiple times in quick succession based on subtle physical collisions, such as when a moving object "settles" into a resting position or when a collision involves a multi‑part model. To avoid triggering more Touched events than necessary, you can implement a simple debounce system which enforces a "cooldown" period through an instance attribute.
Part Collision With Cooldown
local part = workspace.Partlocal COOLDOWN_TIME = 1local function onTouched(otherPart)if not part:GetAttribute("Touched") thenprint(part.Name .. " collided with " .. otherPart.Name)part:SetAttribute("Touched", true) -- Set attribute to truetask.wait(COOLDOWN_TIME) -- Wait for cooldown durationpart:SetAttribute("Touched", false) -- Reset attributeendendpart.Touched:Connect(onTouched)
The TouchEnded event fires when the entire collision bounds of a BasePart exits the bounds of another BasePart or a filled Terrain voxel. It only fires as a result of physical simulation and will not fire when the part's Position or CFrame is explicitly set such that it stops intersecting another part or voxel.
The following code pattern shows how the TouchEnded event can be connected to a custom onTouchEnded() function. Like Touched, the event sends the otherPart argument to the function, indicating the other part involved.
local part = workspace.Partlocal function onTouchEnded(otherPart)print(part.Name .. " is no longer touching " .. otherPart.Name)endpart.TouchEnded:Connect(onTouchEnded)
Models such as player characters contain multiple parts. Since a Model object as a whole cannot be connected to the Touched or TouchEnded events, you'll need to loop through its children and connect the custom onTouched() and onTouchEnded() functions to each child BasePart.
The following code sample connects all BaseParts of a multi‑part model to collision events and tracks the total number of collisions with other parts.
local model = script.Parentlocal numTouchingParts = 0local function onTouched(otherPart)-- Ignore instances of the model intersecting with itselfif otherPart:IsDescendantOf(model) then return end-- Increase count of model parts touchingnumTouchingParts += 1print(model.Name, "intersected with ", otherPart.Name, "| Model parts touching:", numTouchingParts)endlocal function onTouchEnded(otherPart)-- Ignore instances of the model un-intersecting with itselfif otherPart:IsDescendantOf(model) then return end-- Decrease count of model parts touchingnumTouchingParts -= 1print(model.Name, "un-intersected from", otherPart.Name, "| Model parts touching:", numTouchingParts)endfor _, child in pairs(model:GetChildren()) doif child:IsA("BasePart") thenchild.Touched:Connect(onTouched)child.TouchEnded:Connect(onTouchEnded)endend
The CollisionFidelity property for a MeshPart, also available for PartOperation instances, determines how closely the part's physical hitbox matches its visual representation. In most cases, the Default setting is suitable, but other settings can be selected for best performance (Hull/Box) versus accuracy (PreciseConvexDecomposition).
Collision filtering defines which physical parts collide with others. You can configure filtering for numerous objects through collision groups or you can control collisions on a part‑to‑part basis with NoCollisionConstraint instances.
Collision groups let you assign BaseParts to dedicated groups and specify whether or not they collide with those in other groups. Parts within non‑colliding groups pass through each other completely, even if both parts have their CanCollide property set to true.
You can easily set up collision groups through Studio's Collision Groups Editor, accessible by clicking the Collision Groups button in the Model tab.
The editor functions in either List View which favors docking to the left or right side of Studio, or in a wider Table View, which favors docking to the top or bottom.
The editor includes one Default collision group which cannot be renamed or deleted. All BaseParts automatically belong to this default group unless assigned to another group, meaning that they will collide with all other objects in the Default group.
To create a new collision group:
Click the Add Group button along the top of the editor panel, enter a new group name, and press Enter. The new group appears in both columns of list view, or in both the left column and upper row of table view.
Repeat the process if necessary, choosing a unique and descriptive name for each group. Note that you can change a group's name during development by clicking in its field, or by selecting it and clicking the rename button.
To assign objects to groups you've created through the Studio editor:
Select one or more BaseParts that qualify as part of a collision group.
Assign them to the group by clicking the ⊕ button for its row. Objects can belong to only one collision group at a time, so placing them in a new group removes them from their current group.
Once assigned, the new group is reflected under the object's CollisionGroup property.
Under default configuration, objects in all groups collide with each other. To prevent objects in one group from colliding with objects in another group, uncheck the box in the respective row/column.
In the following example, objects in the Cubes group will not collide with objects in the Doors group.
- Collision groups and/or configuration scripts are not required, so you can easily create and share models with customized collision filtering.
- Connected parts will not collide with each other, but they can still collide with other objects.
Roblox player characters collide with each other by default. This can lead to interesting but unintended gameplay, such as characters jumping on top of each other to reach specific areas. If this behavior is undesirable, you can prevent it through the following Script in ServerScriptService.
Script - Disable Character Collisions
local PhysicsService = game:GetService("PhysicsService")local Players = game:GetService("Players")PhysicsService:RegisterCollisionGroup("Characters")PhysicsService:CollisionGroupSetCollidable("Characters", "Characters", false)local function onDescendantAdded(descendant)-- Set collision group for any part descendantif descendant:IsA("BasePart") thendescendant.CollisionGroup = "Characters"endendlocal function onCharacterAdded(character)-- Process existing and new descendants for physics setupfor _, descendant in pairs(character:GetDescendants()) doonDescendantAdded(descendant)endcharacter.DescendantAdded:Connect(onDescendantAdded)endPlayers.PlayerAdded:Connect(function(player)-- Detect when the player's character is addedplayer.CharacterAdded:Connect(onCharacterAdded)end)