I’m currently working on a game where high-speed cars move along a set path, but I’m encountering a strange issue. Even though the player’s character is assigned to a collision group that does not collide with the cars’ collision group, the cars seem to stutter or act as if they are colliding when they pass close to the player.
When this happens, the cars sometimes break apart due to a script I’ve added to handle collisions. However, the player is not directly interacting with the cars—there’s no actual physical collision happening. Here’s what I’ve checked so far:
Collision Groups:
I verified that the player and car collision groups are properly configured using CollisionGroupsAreCollidable, and they indeed shouldn’t interact.
All parts of the player (including accessories) and the car are assigned to their respective groups.
Network Ownership:
The cars’ network ownership is assigned to the server to prevent client-side physics issues.
Custom Scripts:
The script that breaks the cars apart uses proximity or collision detection, but it doesn’t intentionally include the player. It seems like the player’s presence is triggering unexpected behavior, possibly through indirect physics interactions.
Yes, the cars do have checks on Touched events. It’s possible that the Touched events are still triggering despite the collision settings. If that’s the case, it might explain the strange behavior. Do you know if there’s a way to prevent Touched events from firing entirely for specific parts or objects, even if they don’t collide?
Apologies if this isn’t clear, as I’m still getting the hang of this, but here’s the script I’m using in ServerScriptService. I’ll also be looking for other scripts that might be involved in this issue. I hope this is helpful!
local breakParts = {}
for _, part in pairs(workspace:GetDescendants()) do
if part:IsA("BasePart") and part.Name == "Break" then
table.insert(breakParts, {part = part, parent = part.Parent})
end
end
return breakParts
end
local function haveDifferentParents(part1, part2)
return part1.parent ~= part2.parent
end
local function breakJoints(part)
part:BreakJoints()
end
local function disableCanCollideInModel(model)
for _, child in pairs(model:GetDescendants()) do
if child:IsA("BasePart") then
child.CanCollide = false
end
end
end
local barrierMap = {
["F1"] = {"Secondinv", "Secondinv 2"},
["F2"] = {"Barrierinv", "Barrierinv 2"},
["F3"] = {"Wallinv", "Wallinv 2"},
["F4"] = {"Fourthinv", "Fourthinv 2"}
}
local function disableBarriersForCars(parent1, parent2)
local parents = {parent1, parent2}
for _, parent in pairs(parents) do
if barrierMap[parent.Name] then
for _, barrierName in pairs(barrierMap[parent.Name]) do
local barrierModel = workspace:FindFirstChild(barrierName)
if barrierModel then
disableCanCollideInModel(barrierModel)
end
end
end
end
end
local function isPrivateServer()
return game.PrivateServerId ~= ""
end
while true do
local breakParts = findBreakParts()
for _, breakPartInfo1 in pairs(breakParts) do
breakPartInfo1.part.Touched:Connect(function(hit)
for _, breakPartInfo2 in pairs(breakParts) do
if breakPartInfo1.part ~= breakPartInfo2.part and
haveDifferentParents(breakPartInfo1, breakPartInfo2) and
hit == breakPartInfo2.part then
breakJoints(breakPartInfo1.part)
breakJoints(breakPartInfo2.part)
if not isPrivateServer() then
disableBarriersForCars(breakPartInfo1.parent, breakPartInfo2.parent)
end
end
end
end)
end
wait(1)
end
I’m noticing that there’s no checks at the breakPartInfo1.part.Touched for a players character, could try adding a return if hit is a part of one (you could use hit:FindFirstAncestorOfClass("Model") with the combination of Players:GetPlayerFromCharacter(model) to check for a character)
you do not need to set a .touched event every second, rather, just set a touched event at the start, and add an if statement to check if the part should have its joints broken (whether it has different parents and isn’t a breakpart)
also, you do not need two for loops for this case, using a loop inside of a loop can often be inefficient unless you know what you are doing
for your case with the cars stuttering, it is because the network ownership of the cars are being transferred to the player when close, so the client simulates them instead of the server. this is automatic, and can be negated by setting the network ownership manually to the server or client (idk why u wouldn’t use the client)
I noticed your while loop is re-adding items that are already added to the table.
while true do
local breakParts = findBreakParts() — this add multiple copies of the same item since there is not a check to see if the item is already in the table
for _, breakPartInfo1 in pairs(breakParts) do
breakPartInfo1.part.Touched:Connect(function(hit)
for _, breakPartInfo2 in pairs(breakParts) do
if breakPartInfo1.part ~= breakPartInfo2.part and
haveDifferentParents(breakPartInfo1, breakPartInfo2) and
hit == breakPartInfo2.part then
breakJoints(breakPartInfo1.part)
breakJoints(breakPartInfo2.part)
if not isPrivateServer() then
disableBarriersForCars(breakPartInfo1.parent, breakPartInfo2.parent)
end
end
end
end)
end
wait(1)
end
I think this will cause a touched function to fire for each part each time the loop runs. If that’s the case, then multiple touch events will fire exponentially for every part named break.
You might consider checking if the part is already in the table. You might also want to clear any destroyed items from the table.
Something like:
local brakeParts = {}
local function findBrakeParts()
-- remove destroyed items from table
for _, part in pairs(breakParts) do
if not workspace:FindFirstChild(part) then
table.remove(breakParts, part)
end
end
-- add new items to table
for _, part in pairs(workspace:GetDescendants()) do
if part.Name == "Break" and part:IsA("BasePart") then
if not table.find(breakParts, part) then
table.find(breakParts, part)
end
end
end
return breakParts
end
I made some adjustments to the code to reduce lag by checking for breakable parts every 12 seconds instead of every 1 second. This should help reduce the stuttering when the player is near, and the lag spikes are less frequent. However, there’s still a slight delay when the breakable parts update every 12 seconds. The next step would be to fine-tune it further, but this version should be better in terms of performance.
local workspace = game:GetService("Workspace")
local breakParts = {}
local function findBreakParts()
for i = #breakParts, 1, -1 do
local part = breakParts[i]
if not workspace:FindFirstChild(part) then
table.remove(breakParts, i)
end
end
for _, part in pairs(workspace:GetDescendants()) do
if part.Name == "Break" and part:IsA("BasePart") then
if not table.find(breakParts, part) then
table.insert(breakParts, part)
end
end
end
return breakParts
end
local function breakJoints(part)
part:BreakJoints()
end
local function disableCanCollideInModel(model)
for _, child in pairs(model:GetDescendants()) do
if child:IsA("BasePart") then
child.CanCollide = false
end
end
end
local barrierMap = {
["F1"] = {"Secondinv", "Secondinv 2"},
["F2"] = {"Barrierinv", "Barrierinv 2"},
["F3"] = {"Wallinv", "Wallinv 2"},
["F4"] = {"Fourthinv", "Fourthinv 2"}
}
local function disableBarriersForCars(parent1, parent2)
local parents = {parent1, parent2}
for _, parent in pairs(parents) do
if barrierMap[parent.Name] then
for _, barrierName in pairs(barrierMap[parent.Name]) do
local barrierModel = workspace:FindFirstChild(barrierName)
if barrierModel then
disableCanCollideInModel(barrierModel)
end
end
end
end
end
local function isPrivateServer()
return game.PrivateServerId ~= ""
end
while true do
local breakParts = findBreakParts()
for _, breakPartInfo1 in pairs(breakParts) do
breakPartInfo1.part.Touched:Connect(function(hit)
for _, breakPartInfo2 in pairs(breakParts) do
if breakPartInfo1.part ~= breakPartInfo2.part and
breakPartInfo1.parent ~= breakPartInfo2.parent and
hit == breakPartInfo2.part then
breakJoints(breakPartInfo1.part)
breakJoints(breakPartInfo2.part)
if not isPrivateServer() then
disableBarriersForCars(breakPartInfo1.parent, breakPartInfo2.parent)
end
end
end
end)
end
wait(12)
end
Getting all Descendants of the workspace will cause lag unless the workspace in mostly empty.
Can you put the items in a folder and just check the folder?
Also, I noticed a problem. In the findBreakParts function, you are looking for FirstChild when removing items from the table and Descendants when adding items to the table.
My bad. I didn’t notice earlier.
You will need to look for parts they same way each time.
So you need to use GetDescendants when removing items.
It seems the stuttering issue when the player is in the cars’ path persists with this script. I’m starting to run out of ideas on how to resolve it.
local workspace = game:GetService("Workspace")
local breakParts = {}
local function findBreakParts()
-- Clear the table before re-adding parts
local updatedParts = {}
for _, model in pairs(workspace:GetChildren()) do
if model:IsA("Model") then
for _, part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") and part.Name == "Break" then
-- Only add the part if it's not already in the table
table.insert(updatedParts, {part = part, parent = model})
end
end
end
end
-- Update the global breakParts table with the new parts
breakParts = updatedParts
end
local function breakJoints(part)
part:BreakJoints()
end
local function disableCanCollideInModel(model)
for _, child in pairs(model:GetDescendants()) do
if child:IsA("BasePart") then
child.CanCollide = false
end
end
end
local barrierMap = {
["F1"] = {"Secondinv", "Secondinv 2"},
["F2"] = {"Barrierinv", "Barrierinv 2"},
["F3"] = {"Wallinv", "Wallinv 2"},
["F4"] = {"Fourthinv", "Fourthinv 2"}
}
local function disableBarriersForCars(parent1, parent2)
local parents = {parent1, parent2}
for _, parent in pairs(parents) do
if barrierMap[parent.Name] then
for _, barrierName in pairs(barrierMap[parent.Name]) do
local barrierModel = workspace:FindFirstChild(barrierName)
if barrierModel then
disableCanCollideInModel(barrierModel)
end
end
end
end
end
local function isPrivateServer()
return game.PrivateServerId ~= ""
end
local function handleCollisions(breakPartInfo1, hit)
for _, breakPartInfo2 in pairs(breakParts) do
if breakPartInfo1.part ~= breakPartInfo2.part and
breakPartInfo1.parent ~= breakPartInfo2.parent and
hit == breakPartInfo2.part then
breakJoints(breakPartInfo1.part)
breakJoints(breakPartInfo2.part)
if not isPrivateServer() then
disableBarriersForCars(breakPartInfo1.parent, breakPartInfo2.parent)
end
end
end
end
local function setupCollisionHandlers()
for _, breakPartInfo1 in pairs(breakParts) do
breakPartInfo1.part.Touched:Connect(function(hit)
local hitModel = hit:FindFirstAncestorOfClass("Model")
local hitPlayer = Players:GetPlayerFromCharacter(hitModel)
if hitPlayer then return end
handleCollisions(breakPartInfo1, hit)
end)
end
end
local function initialize()
-- Initially find all breakable parts and set up collision handlers
findBreakParts()
setupCollisionHandlers()
end
while true do
initialize() -- Initialize on start or whenever needed
wait(12) -- Wait before refreshing
end
Thanks for pointing that out! I’ve added the network ownership part, and it seems to be working now. Here’s the updated code:
local workspace = game:GetService("Workspace")
local breakParts = {}
local function setNetworkOwnershipToServer(model)
for _, part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part:SetNetworkOwner(nil)
end
end
end
local function findBreakParts()
local updatedParts = {}
for _, model in pairs(workspace:GetChildren()) do
if model:IsA("Model") then
setNetworkOwnershipToServer(model) -- Ensure server controls the physics
for _, part in pairs(model:GetDescendants()) do
if part:IsA("BasePart") and part.Name == "Break" then
table.insert(updatedParts, {part = part, parent = model})
end
end
end
end
breakParts = updatedParts
end
local function breakJoints(part)
part:BreakJoints()
end
local function disableCanCollideInModel(model)
for _, child in pairs(model:GetDescendants()) do
if child:IsA("BasePart") then
child.CanCollide = false
end
end
end
local barrierMap = {
["F1"] = {"Secondinv", "Secondinv 2"},
["F2"] = {"Barrierinv", "Barrierinv 2"},
["F3"] = {"Wallinv", "Wallinv 2"},
["F4"] = {"Fourthinv", "Fourthinv 2"}
}
local function disableBarriersForCars(parent1, parent2)
local parents = {parent1, parent2}
for _, parent in pairs(parents) do
if barrierMap[parent.Name] then
for _, barrierName in pairs(barrierMap[parent.Name]) do
local barrierModel = workspace:FindFirstChild(barrierName)
if barrierModel then
disableCanCollideInModel(barrierModel)
end
end
end
end
end
local function isPrivateServer()
return game.PrivateServerId ~= ""
end
local function handleCollisions(breakPartInfo1, hit)
for _, breakPartInfo2 in pairs(breakParts) do
if breakPartInfo1.part ~= breakPartInfo2.part and
breakPartInfo1.parent ~= breakPartInfo2.parent and
hit == breakPartInfo2.part then
breakJoints(breakPartInfo1.part)
breakJoints(breakPartInfo2.part)
if not isPrivateServer() then
disableBarriersForCars(breakPartInfo1.parent, breakPartInfo2.parent)
end
end
end
end
local function setupCollisionHandlers()
for _, breakPartInfo in pairs(breakParts) do
if not breakPartInfo.part.TouchedConnected then
breakPartInfo.part.Touched:Connect(function(hit)
local hitModel = hit:FindFirstAncestorOfClass("Model")
local hitPlayer = Players:GetPlayerFromCharacter(hitModel)
if hitPlayer then return end
handleCollisions(breakPartInfo, hit)
end)
breakPartInfo.part.TouchedConnected = true
end
end
end
local function initialize()
findBreakParts()
setupCollisionHandlers()
end
while true do
initialize() -- Refresh breakable parts and reapply network ownership
wait(12)
end
To my knowledge, the workspace instance has a bool property named TouchesUseCollisionGroups.
Check if the mentioned property is set to true and let me know if it resolved your issue
local phs=game:GetService("PhysicsService")
phs:RegisterCollisionGroup("Car")
phs:RegisterCollisionGroup("Player")
phs:CollisionGroupSetCollidable("Car","Car",false)
phs:CollisionGroupSetCollidable("Car","Player",false)
phs:CollisionGroupSetCollidable("Player","Player",false)
--and this part. Got this linked into too much to post here.
function setCollisionGroup(model, groupName)
for _, part in ipairs(model:GetDescendants()) do
if part:IsA("BasePart") then
part.CollisionGroup = groupName
end
end
end
Mine is pretty long as it has to account for respawns and the car spawn events. But nothing touches not even the players. I will PM you the whole script.