Custom center-locked mouse camera control toggle

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)
131 Likes

NOW THIS… is an absolute lifesaver, thank you so much for making this, helped me alot ;D

Bumping this for everyones sake

8 Likes

Hi, sorry for the bump, but this is important!

Just wanted to make a note here in this code:

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

Otherwise, this solution is good!

4 Likes

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.

2 Likes

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!

1 Like

This code is lifesaving! I just shortened my sub-par shoulder cam from 60 lines to ~10. Thanks!

2 Likes

Oh my what a life saver!!!

Apologies for the bump

2 Likes

For some reason this is no longer working,
“attempt to index local ‘MouseLockController’(a nil value)” playing around with the Camera is very frustrating.

2 Likes

After messing around a bit I have successfully recreated this by setting the conditions of the toggle within the camera module.

1 Like

Can you share how here?
As that will help the people who stumble across this now.

1 Like

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.

1 Like

This saved me a lot of time. Thanks

2 Likes

It doesn’t work, maybe a fix? Or anyone?

Yeah, basically Roblox removed this API so you’re going to probably need to fork tne camera scripts to disable this.

Pinging @Fractality @Fractality_alt

5 Likes

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.

9 Likes

Where can I find the toggleable setting for your module?

Follow the original post example but replace the camera module with the one I provided and it should work

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.