Local pushing boxes

The idea is to have a level be locally inserted, so, that every player can individually play that level without hindering one another. This level also contains a pushable part that’s subjected to physics. The players collisions are set to false as well.

The issue comes where another player touches someone else’s part, even though they can not see it (it’s local). It appears that, while the part is local, others can still move it around without seeing it or them knowing.

Is there any way these loally inserted, moveable parts can ONLY be pushed by the destined client?

1 Like

local script from a UI that inserts the level from the client:

script.Parent.TextButton.MouseButton1Click:Connect(function()
	local newLevel = game.ReplicatedStorage.Level:Clone()
	newLevel.Parent = workspace
end)

standard server script that disables player collisions:

local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")
 
local playerCollisionGroupName = "Players"
PhysicsService:CreateCollisionGroup(playerCollisionGroupName)
PhysicsService:CollisionGroupSetCollidable(playerCollisionGroupName, playerCollisionGroupName, false)
 
local previousCollisionGroups = {}
 
local function setCollisionGroup(object)
  if object:IsA("BasePart") then
    previousCollisionGroups[object] = object.CollisionGroupId
    PhysicsService:SetPartCollisionGroup(object, playerCollisionGroupName)
  end
end
 
local function setCollisionGroupRecursive(object)
  setCollisionGroup(object)
 
  for _, child in ipairs(object:GetChildren()) do
    setCollisionGroupRecursive(child)
  end
end
 
local function resetCollisionGroup(object)
  local previousCollisionGroupId = previousCollisionGroups[object]
  if not previousCollisionGroupId then return end 
 
  local previousCollisionGroupName = PhysicsService:GetCollisionGroupName(previousCollisionGroupId)
  if not previousCollisionGroupName then return end
 
  PhysicsService:SetPartCollisionGroup(object, previousCollisionGroupName)
  previousCollisionGroups[object] = nil
end
 
local function onCharacterAdded(character)
  setCollisionGroupRecursive(character)
 
  character.DescendantAdded:Connect(setCollisionGroup)
  character.DescendantRemoving:Connect(resetCollisionGroup)
end
 
local function onPlayerAdded(player)
  player.CharacterAdded:Connect(onCharacterAdded)
end
 
Players.PlayerAdded:Connect(onPlayerAdded)

Seems like a good plan and solution. However, even though the part is created on one client, another player can still somehow push it - very bizarre.

https://developer.roblox.com/en-us/api-reference/class/PhysicsService
" Creating, deleting and modifying collision relationships between collision groups is limited to server-side Script s. However, client-side LocalScript s may only set individual parts’ associated collision group."
I suggest you to create 3 different collision groups “Players” “LocalPlayer” and “LocalParts”.
You can keep the standard server script that disable player collisions and add the 2 other collision groups.

local phs = game:GetService("PhysicsService")
phs:CreateCollisionGroup("Players")
phs:CreateCollisionGroup("LocalPlayer")
phs:CreateCollisionGroup("LocalParts")
phs:CollisionGroupSetCollidable("Players","Players",false)
phs:CollisionGroupSetCollidable("Players","LocalPlayer",false)
phs:CollisionGroupSetCollidable("Players","LocalParts",false)

Then in that local script, you can locally set the character’s parts’ collision group to “LocalPlayer”. Problem is, at the same time, whenever the player respawns, the server is going to set the collision group to “Players”, so make sure you do it AFTER that happens.

local plr = game.Players.LocalPlayer
plr.CharacterAppearanceLoaded:Connect(function(char)
	for _, characterPart in pairs(char:GetChildren()) do
		if characterPart:IsA("BasePart") then
			repeat task.wait() until characterPart.CollisionGroupId == phs:GetCollisionGroupId("Players") --wait for the server to do it's job so that the local script can overwrite it
			phs:SetPartCollisionGroup(characterPart, "LocalPlayer")
		end
	end
end)

This crappy code i wrote probably isnt the best way to do it but too lazy to make a better one. If you want to make a better one you can kinda copy the the standard server script into the local script
Now, everytime you create a “local pushing box”, you can set it’s collision group to “LocalParts”

script.Parent.TextButton.MouseButton1Click:Connect(function()
	local newLevel = game.ReplicatedStorage.Level:Clone()
	newLevel.Parent = workspace
    for _,part in pairs(newLevel.LocalPushBoxes:GetChildren()) do --assuming you've put all the local pushable boxes inside a "LocalPushBoxes" folder
        phs:SetPartCollisionGroup(part,"LocalPart")
    end
end)

Now, collisions between the local push boxes and all the players should be disabled, except the local player since you’ve locally set it’s character parts to a new Collision group “LocalPlayer”

This post also greatly helped me so you should probably check it out if you haven’t yet

1 Like

Thank you for the insightful reply. It now - after a bit of messing around - appears to finally work!

Cheers! :smile:

1 Like