Ok, after a lot of playing around i've come up with a workable solution. It seems to be the case that CFraming your character locally and even moving it locally with a BodyPosition replicate to the server just fine even with filtering enabled on. This seems weird to me and an easy vector for exploit, but it does make creating the functionality you want fairly easy. It isn't exactly like the code you had posted, as you can't look up or down to move up/down - you have to use space/left ctrl, but adding that functionality introduced other problems so I thought it was better this way. The character is also playing the falling animation when no clip is active, but you can always add to the code below to play a custom animation if it is important to you.
This should be in a local script in StarterPlayerScripts. It handles input, messaging the server to turn on/off collision, the BodyPosition that controls hovering, and I also rotate the character in a renderstep that runs after camera update to get smooth rotations. I have commented the two variables you can change here to control noclip speed.
local UserInputService = game:GetService("UserInputService")
local player = game.Players.LocalPlayer
local MasterControl = require(player:WaitForChild("PlayerScripts"):WaitForChild("ControlScript"):WaitForChild("MasterControl"))
local noClipEvent = game.ReplicatedStorage:WaitForChild("NoClipEvent")
local isNoClipActive = false
local isSpaceHeld = false
local isCtrlHeld = false
local hoverHeight
local lastUpdate
local bodyPosition = Instance.new("BodyPosition")
bodyPosition.Name = "NoClipBodyPosition"
bodyPosition.MaxForce = Vector3.new(500000000, 500000000, 500000000)
bodyPosition.D = 400
local noClipSpeed = 40
local hoverIncrementSpeed = 1
UserInputService.InputBegan:connect(function(input, event)
if not event then
if input.KeyCode == Enum.KeyCode.ButtonB or input.KeyCode == Enum.KeyCode.V then
if player.Character and player.Character.Parent then
lastUpdate = tick()
isNoClipActive = not isNoClipActive
noClipEvent:FireServer(isNoClipActive)
MasterControl:SetNoClip(isNoClipActive, noClipSpeed)
if isNoClipActive then
local root = player.Character:FindFirstChild("HumanoidRootPart")
if root then
bodyPosition.Parent = player.Character.HumanoidRootPart
bodyPosition.Position = root.Position
hoverHeight = root.Position.Y
end
else
bodyPosition.Parent = nil
end
end
end
if input.KeyCode == Enum.KeyCode.Space then
isSpaceHeld = true
end
if input.KeyCode == Enum.KeyCode.LeftControl then
isCtrlHeld = true
end
end
end)
UserInputService.InputEnded:connect(function(input, event)
if not event then
if input.KeyCode == Enum.KeyCode.Space then
isSpaceHeld = false
end
if input.KeyCode == Enum.KeyCode.LeftControl then
isCtrlHeld = false
end
end
end)
game:GetService("RunService"):BindToRenderStep("updateLoop", Enum.RenderPriority.Camera.Value + 1, function()
if isNoClipActive and player.Character and player.Character.Parent then
local root = player.Character:FindFirstChild("HumanoidRootPart")
if root then
local cam = workspace.CurrentCamera
local camForward = cam.CFrame.lookVector
camForward = Vector3.new(camForward.X, 0, camForward.Z).Unit
root.CFrame = CFrame.new(root.CFrame.p, root.CFrame.p + camForward)
if isSpaceHeld then
hoverHeight = hoverHeight + hoverIncrementSpeed
end
if isCtrlHeld then
hoverHeight = hoverHeight - hoverIncrementSpeed
end
if bodyPosition.Parent then
bodyPosition.Position = Vector3.new(root.CFrame.p.X, hoverHeight, root.CFrame.p.Z)
end
end
end
end)
This is a server script that should be in ServerScriptService. It's job is to toggle player collision on/off when it receives a message from the local script.
local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")
local noClipEvent = Instance.new("RemoteEvent")
noClipEvent.Name = "NoClipEvent"
noClipEvent.Parent = game.ReplicatedStorage
local noClipPlayers = "Players"
PhysicsService:CreateCollisionGroup(noClipPlayers)
PhysicsService:CollisionGroupSetCollidable("Default", noClipPlayers, false)
local function setCollisionGroupRecursive(object, groupName)
if object:IsA("BasePart") then
PhysicsService:SetPartCollisionGroup(object, groupName)
end
for _, child in ipairs(object:GetChildren()) do
setCollisionGroupRecursive(child, groupName)
end
end
noClipEvent.OnServerEvent:connect(function(player, isNoClipActive)
local character = player.Character
if character then
if isNoClipActive then
setCollisionGroupRecursive(character, noClipPlayers)
else
setCollisionGroupRecursive(character, "Default")
end
end
end)
Finally, we have the code that updates player movement, which I have put in a modified ControlScript. This same code can be run within the local script and still work, but having it in the control script speeds things up a lot. You will see i'm also rotating the character here before moving it. You can get away with only having the character rotation updated here, and not also in the local script, but as this is bound to the input render priority, only having it updated here gives you jittery rotations.
To set this up, you will want to press play in studio, and in the explorer navigate to Players -> [your player name] -> PlayerScripts and copy the ControlScript you find in there (Ctrl+C). Then press stop to exit the game, and paste the ControlScript into StarterPlayer -> StarterPlayerScripts. Then expand the ControlScript in the explorer and you will find the MasterControl module. Open MasterControl up for editing.
On lines 82 to 99 is the code for the updateMovement function. You need to replace these 18 lines with the following
local getHumanoid = MasterControl.GetHumanoid
local moveFunc = LocalPlayer.Move
local shouldUpdate = false
local lastUpdate
local updateMovement = function()
if not areControlsEnabled then return end
local humanoid = getHumanoid()
if not humanoid then return end
if isJumpEnabled and isJumping and not humanoid.PlatformStand then
local state = humanoid:GetState()
if state ~= STATE_JUMPING and state ~= STATE_FREEFALL and state ~= STATE_LANDED then
humanoid.Jump = isJumping
end
end
if allowNoClip then
if shouldUpdate then
shouldUpdate = false
moveFunc(LocalPlayer, Vector3.new(), true)
lastUpdate = tick()
end
local root = LocalCharacter:FindFirstChild("HumanoidRootPart")
if root then
local cam = workspace.CurrentCamera
local camForward = cam.CFrame.lookVector
camForward = Vector3.new(camForward.X, 0, camForward.Z).Unit
local currentTime = tick()
local delta = currentTime - lastUpdate
root.CFrame = CFrame.new(root.CFrame.p, root.CFrame.p + camForward) * CFrame.new(moveValue * delta * noClipSpeed)
lastUpdate = currentTime
end
else
moveFunc(LocalPlayer, moveValue, true)
shouldUpdate = true
end
end
Above what you just pasted, you want to insert this code
local allowNoClip = false
local noClipSpeed = 40
function MasterControl:SetNoClip(isNoClipActive, speed)
allowNoClip = isNoClipActive
noClipSpeed = speed
end
So the area you just modified should now look like this
if LocalCharacter then
characterAdded(LocalCharacter)
end
LocalPlayer.CharacterAdded:connect(characterAdded)
local allowNoClip = false
local noClipSpeed = 40
function MasterControl:SetNoClip(isNoClipActive, speed)
allowNoClip = isNoClipActive
noClipSpeed = speed
end
local getHumanoid = MasterControl.GetHumanoid
local moveFunc = LocalPlayer.Move
local shouldUpdate = false
local lastUpdate
local updateMovement = function()
if not areControlsEnabled then return end
local humanoid = getHumanoid()
if not humanoid then return end
if isJumpEnabled and isJumping and not humanoid.PlatformStand then
local state = humanoid:GetState()
if state ~= STATE_JUMPING and state ~= STATE_FREEFALL and state ~= STATE_LANDED then
humanoid.Jump = isJumping
end
end
if allowNoClip then
if shouldUpdate then
shouldUpdate = false
moveFunc(LocalPlayer, Vector3.new(), true)
lastUpdate = tick()
end
local root = LocalCharacter:FindFirstChild("HumanoidRootPart")
if root then
local cam = workspace.CurrentCamera
local camForward = cam.CFrame.lookVector
camForward = Vector3.new(camForward.X, 0, camForward.Z).Unit
local currentTime = tick()
local delta = currentTime - lastUpdate
root.CFrame = CFrame.new(root.CFrame.p, root.CFrame.p + camForward) * CFrame.new(moveValue * delta * noClipSpeed)
lastUpdate = currentTime
end
else
moveFunc(LocalPlayer, moveValue, true)
shouldUpdate = true
end
end
And that's it. I apologize for the long post, but I wanted to be as clear as possible with everything. If you have any questions feel free to ask.
6 comments
I haven’t had time to test your script, but I’m not sure if it functions the same. I would want players to freely go through any object in the game and be able to fly around. Is that possible when using Collision Groups? Do you mind testing the script I posted to see how it works and tell me if it’s possible to make it less laggy?
You are right, sorry. The script I posted only let's players go through objects. I didn't realize you wanted flight as well. I will have another look when I get some free time this afternoon
Looking at your scripts, you are calling the remote event 30 times a second when noclip is toggled on. That's bad for one client to be doing, but even worse if multiple clients are all doing it at once. Combined with that you will have the latency of handling movement on the server. There should be a way to modify the control script to make this workable, but i'll have to dig into it and see...
...what I can come up with. I will post here if I can find a workable solution
Ok, i've come up with a solution that I hope works for you. I have replaced my old answer above with the new code
That isn’t what I’m looking for though. I need the player to move when they look up/down while noclipping. What problems would it create if you did add it? I also tried to remove that falling animation but it’s just breaking the script.