Introduction
Hello developers !
I have seen many games accomplish some kind of “Force shift locking”, and most threads I’ve seen change the PlayerModule.
This method accomplishes about the same result without changing the PlayerModule by CFraming.
I believe this method is simpler, and if the PlayerModule changes again, this will still work.
End Result:
This Method Utilizies:
UserInputService.MouseBehavior
Humanoid.CameraOffset
RunService.RenderStepped / RunService.PreRender
Tutorial:
Step 0 (Before Scripting)
Firstly, since we use MouseBehavior (A UserInputService “client” property), we use a LocalScript and not a script.
I prefer to put it in StarterCharacterScripts Since it’s easier to reference the character, but you can do this in every place where local scripts can run, just reference the Character accordingly.
Step 1 (The Mouse):
Let’s start writing the script, the following script will center the mouse.
We RunService.RenderStepped to center it on every frame so it won’t revert back to normal.
Local script that centers mouse:
--Local script in StarterPlayerCharacter
local plr = game:GetService("Players").LocalPlayer
function shiftLock(active) --Toggle shift lock function
if active then --Whether to enable or disable
---------------------------------------------------------
game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter --Set the mouse to center every frame.
end)
---------------------------------------------------------
end
end
Step 2 (The Camera):
After we centered the mouse, we now want the camera to be a bit above and right from the character, like the Shift-Lock effect.
For this, we use Humanoid.CameraOffset. It will move the camera farther from the Humanoid (X,Y,Z) Studs away.
Let’s assume the shift-lock moves the camera 1.75 Studs to the X.
The local script now looks like this:
Local script that centers the mouse and moves the camera:
--Local script in StarterPlayerCharacter
local plr = game:GetService("Players").LocalPlayer
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
function shiftLock(active) --Toggle shift lock function
if active then --Whether to enable or disable
---------------------------------------------------------
hum.CameraOffset = Vector3.new(1.75,0,0) -- Set the Camera's center distance from character
---------------------------------------------------------
game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter --Set the mouse to center every frame.
end)
end
end
Step 3 (The Character):
Now lastly, and most importantly, *the character isn’t facing to the mouse/camera’s direction.
As far as I’ve seen, this is why most people don’t use MouseBehavior for force-Enabling shift lock.
However, the solution for that is quite simple.
If we were to cancel the humanoid’s automatic rotation (Humanoid.AutoRotate), we can just CFrame the Character towards the Camera’s Rotation.
This will force the character to face the Camera’s rotation.
Here's how we'll do that:
Firstly, let’s define the HumanoidRootPart, since we will be altering its’ CFrame.
local root = hum.RootPart -- The HumanoidRootPart
Secondly, we’ll disable the humanoid’s automatic rotation so we can set it ourselves.
hum.AutoRotate = false --Disable the automatic rotation since we are the ones setting it.
Now let’s get the Camera’s angles. But since we want to rotate it only on one axis (the Y axis), we’ll use _ as a placeholder for the X axis.
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
Lastly, we got the angles, now all left is to rotate the HumanoidRootPart.
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0) --Set the root part's CFrame to the current position, but with the camera's rotation.
Local script:
Local script that centers the mouse, moves the camera and rotates the player to camera:
--Local script in StarterPlayerCharacter
--Variables:
local plr = game:GetService("Players").LocalPlayer
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
---------------------------------------------------------
local root = hum.RootPart -- The HumanoidRootPart
---------------------------------------------------------
--Toggle Function:
function shiftLock(active) --Toggle shift lock function
if active then
---------------------------------------------------------
hum.AutoRotate = false --Disable the automatic rotation since we are the ones setting it.
---------------------------------------------------------
hum.CameraOffset = Vector3.new(1.75,0,0)
game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter --Set the mouse to center.
---------------------------------------------------------
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0) --Set the root part to the camera's rotation
---------------------------------------------------------
end)
end
end
--Disable and Enable:
shiftLock(true) -- Toggle shift lock
print("Shift lock turned on!")
Step 4:(Disabling the Shift-Lock)
Now when you press play, you should be in the “Shift lock mode” that we created.
But now, how shall we disable it?
Very simple, just revert all the changes we’ve done, and stop altering the CFrame
Note: I’ve created variables for the services due to us using them more than once
All of our progress so far, with an option to disable:
--Local script in StarterPlayerCharacter
--Services:
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
--Variables:
local plr = game:GetService("Players").LocalPlayer
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
local root = hum.RootPart -- The HumanoidRootPart
--Toggle Function:
function shiftLock(active) --Toggle shift.lock function
if active then
hum.CameraOffset = Vector3.new(1.75,0,0) -- I assume this is about the right camera offset.
hum.AutoRotate = false --Disable the automatic rotation since we are the ones setting it.
RunService:BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter --Set the mouse to center every frame.
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0) --Set the root part to the camera's rotation
end)
else
hum.AutoRotate = true --Let the humanoid handle the camera rotations again.
hum.CameraOffset = Vector3.new(0,0,0) --Move the camera back to normal.
RunService:UnbindFromRenderStep("ShiftLock") -- Allow mouse to move freely.
UserInputService.MouseBehavior = Enum.MouseBehavior.Default -- Let the mouse move freely
end
end
--Disable and Enable:
shiftLock(true) -- Toggle shift lock
print("Shift lock turned on!")
task.wait(7)
shiftLock(false) --Toggle off shift Lock
print("Shift lock turned off!")
To disable it, we can now use shiftLock(false)
Congratulations! We're finished🥳!
You should now have a functioning script that forces the shift-lock.Final Script:
Open script...
--Local script in StarterPlayerCharacter
--Services:
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
--Variables:
local plr = game:GetService("Players").LocalPlayer
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
local root = hum.RootPart -- The HumanoidRootPart
--Toggle Function:
function shiftLock(active) --Toggle shift.lock function
if active then
hum.CameraOffset = Vector3.new(1.75,0,0) -- I assume this is about the right camera offset.
hum.AutoRotate = false --Disable the automatic rotation since we are the ones setting it.
RunService:BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter --Set the mouse to center every frame.
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0) --Set the root part to the camera's rotation
end)
else
hum.CameraOffset = Vector3.new(0,0,0) --Move the camera back to normal.
RunService:UnbindFromRenderStep("ShiftLock") -- Allow mouse to move freely.
UserInputService.MouseBehavior = Enum.MouseBehavior.Default -- Let the mouse move freely
hum.AutoRotate = true --Let the humanoid handle the camera rotations again.
end
end
--Disable and Enable:
shiftLock(true) -- Toggle shift lock
--[[
shiftLock(false) --Toggle off shift Lock
]]
Uses of Shift-Lock forcing:
- Mobile Phone Shift-Lock for Obbies
- Third Person guns.
- Tools in general
- Default in-game character view.
Final Notes:
This tutorial has been fully re-written (April 2022) due to the deprecation of BodyGyro.
Some of the replies/questions may have related to the previous very of the tutorial, so not all of them might be relevant to the current version.
Additional Credits:
@CrystalFlxme - For alerting me about the Humanoid.AutoRotate property to solve some rotation issues that occurred.
@SaturdayScandal - For alerting me of the deprecation of BodyGyro and the need to update this tutorial.
@xdeno - For alerting me about the behavior change of UserInputService.MouseBehavior and providing the accurate Shift Lock’s offset camera
I hope this tutorial helped you or taught you something new
Please do alert me if you notice any issues regarding the tutorial and its’ content
Have a wonderful rest of your day/night !