I’ve read a bit of the new camera and character control modules source code and came up with an implementation for center-locking the mouse while controlling the camera that also supports shift lock.
Shift lock has properties for developers to disable it in their games, but those properties are read-only in runtime, so you can’t use them to disable shift lock temporarily to stop it from affecting the camera. I’ve found a loophole, though: a StringValue named BoundKeys is available to change which keys will activate shift lock if it is enabled. Setting the value to an empty string disables the toggle.
The function SetIsMouseLocked in the CameraController module behaves just like shift lock. The mouse gets center-locked, moving it moves the camera, and the character faces in the direction the camera faces. SetMouseLockOffset sets the camera offset from the center; shift lock uses 1.75, 0, 0.
local PlayerModule = game.Players.LocalPlayer.PlayerScripts:WaitForChild("PlayerModule")
local cameras = require(PlayerModule):GetCameras()
local BoundKeys = PlayerModule.CameraModule.MouseLockController:WaitForChild("BoundKeys")
local OldBoundKeys = BoundKeys.Value
local CenterLocked = false
local function ToggleLock()
local CameraController = cameras.activeCameraController
local MouseLockController = cameras.activeMouseLockController
CenterLocked = not CenterLocked
if CenterLocked then
if MouseLockController:GetIsMouseLocked() then -- toggle shift lock off
MouseLockController:OnMouseLockToggled()
end
CameraController:SetMouseLockOffset(Vector3.new())
CameraController:SetIsMouseLocked(true)
BoundKeys.Value = "" -- disables shift lock toggle
else
CameraController:SetIsMouseLocked(false)
BoundKeys.Value = OldBoundKeys -- restores shift lock toggle
end
end
-- example use, press T to toggle center lock
game:GetService("UserInputService").InputBegan:Connect(function(input, gameProcessedEvent)
if gameProcessedEvent then return end
if input.KeyCode == Enum.KeyCode.T then
ToggleLock()
end
end)
MouseLockController:GetIsMouseLocked() will generally return false. You’re polluting a class structure, instead of using it as an actual service.
Thus, MouseLockController:OnMouseLockToggled() is used to fix the mouse icon, but in reality, this call should just be done by setting mouse.Icon = “” or mouse.Icon = the mouse shift lock icon
The purpose of having the if MouseLockController:GetIsMouseLocked() then block is to check whether the player is already using regular shift lock. MouseLockController:OnMouseLockToggled() takes them out of shift lock first before turning on the custom mouse lock. The mouse controller internally handles states and mouse icons, so I just tell it to turn itself off instead of hacking around it. If you were to remove this block and just set the mouse icon to "" then if you go into shift lock and press T to switch into this custom center lock state, when you press T again to turn off the custom center lock, if you try to press shift to enable regular mouse lock, you have to press it twice because it thinks it’s still on the first time and turns it off when it was already indirectly disabled. I wanted to support shift lock at the same time as this, and that’s what that’s for.
Here’s the OnMouseLockToggled function in the MouseLockController module:
function MouseLockController:OnMouseLockToggled()
self.isMouseLocked = not self.isMouseLocked
if self.isMouseLocked then
local cursorImageValueObj = script:FindFirstChild("CursorImage")
if cursorImageValueObj and cursorImageValueObj:IsA("StringValue") and cursorImageValueObj.Value then
self.savedMouseCursor = Mouse.Icon
Mouse.Icon = cursorImageValueObj.Value
else
if cursorImageValueObj then
cursorImageValueObj:Destroy()
end
cursorImageValueObj = Instance.new("StringValue")
cursorImageValueObj.Name = "CursorImage"
cursorImageValueObj.Value = DEFAULT_MOUSE_LOCK_CURSOR
cursorImageValueObj.Parent = script
self.savedMouseCursor = Mouse.Icon
Mouse.Icon = DEFAULT_MOUSE_LOCK_CURSOR
end
else
if self.savedMouseCursor then
Mouse.Icon = self.savedMouseCursor
self.savedMouseCursor = nil
end
end
self.mouseLockToggledEvent:Fire()
end
The first line internally switches the isMouseLocked state. If this function weren’t called and you switched into the custom center lock while shift lock is on then it would think it’s still in shift lock mode, even when you switch out. The rest of the code handles restoring the original mouse icon. isMouseLocked could have been written to instead of calling the function but I found it cleaner to just have the module do its thing instead of doing it for it.
Ah, I misread the original code. I thought the code was retrieving the module by requiring the module, but it’s using “cameras.activeMouseLockController”. Sorry about the confusion, it all makes sense!
For some reason this is no longer working,
“attempt to index local ‘MouseLockController’(a nil value)” playing around with the Camera is very frustrating.
I’m just going to make a whole new post explaining it when I get around to it, but you could message me on discord Drac#5808 if you needed a quick explanation.
Sorry about the bump but I ran into an error with this because Roblox had updated the Camera Module so this no longer worked luckily I was able to find a copy of the old Camera Module so by putting the old Camera Module in StarterPlayerScripts the above code works just fine. PlayerModule.rbxm (109.5 KB)
If you’re unable to find any different way to get the Shift Lock experience then try this.
Is there any way to specifically make it turn off/on instead of just toggling it? Like a function that turns it on and one that turns it off? I tried doing it myself but the code just got messed up.