Why does CameraOffset ruin shift lock? (Help Needed, bad.)

Third time is the charm, because this is a fatal flaw of my game and no help has been found yet.

I am making a forced Shift lock for my game. I accomplish this by setting MouseLocked to true in my CameraModule script:

function CameraModule:Update(dt)
	if self.activeCameraController then
		if game.Players.LocalPlayer.PlayerScripts.ShiftLockToggle.ShiftLockToggle.Value == true then
			self.activeCameraController:SetIsMouseLocked(true)

That works fine. I get this when I do that:

But we obviously want an offset for shiftlock, so you can look over your shoulder like this:

So since I also wanted a camera sway when you walked, I made a script in StarterCharacterScripts that updates every frame to add in the sway, and also create a camera offset as such:

local runService = game:GetService("RunService")
local players = game:GetService("Players")
local player = players.LocalPlayer
local humanoid = player.Character.Humanoid
local gameSettings = UserSettings().GameSettings
local mouse = player:GetMouse()
local head = player.Character:WaitForChild("Head")
local runService = game:GetService('RunService')
local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable
local character = player.Character or player.CharacterAdded:Wait()
runService.RenderStepped:Connect(function()
	local currentTime = tick()
	if camera.CameraType == Enum.CameraType.Follow and humanoid.MoveDirection.Magnitude > 0 and humanoid.WalkSpeed > 0 then
		local swayX = math.cos(currentTime * 15) * 0.3
		local swayY = math.abs(math.sin(currentTime * 15) * 0.3)
		local sway = Vector3.new(swayX + 4.5, swayY, 1)
		humanoid.CameraOffset = humanoid.CameraOffset:lerp(sway, 0.25)
	elseif camera.CameraType ~= Enum.CameraType.Follow or humanoid.MoveDirection.Magnitude == 0 or humanoid.WalkSpeed == 0 then
		local offsetStandard = Vector3.new(4.5, 0, 1)
		humanoid.CameraOffset = humanoid.CameraOffset:Lerp(offsetStandard, 0.25)
	end
end)

Now my camera would have an offset as expected, but also the character glitches pretty bad.

So just in case it was the sway, or the fact I was updating it every frame, I simplified the offset for troubleshooting purposes:

local runService = game:GetService("RunService")
local players = game:GetService("Players")
local player = players.LocalPlayer
local humanoid = player.Character.Humanoid
humanoid.CameraOffset = Vector3.new(4.5, 0, 1)

Yet even with this oversimplification, the glitched out character persists.

Some extra info if you need to replicate this:
In StarterPlayer, the CameraMovementMode is Follow
ForceFirstPerson is off
Camera Minimum and Maximum distance are both 4.

If you have anything of use please reply with it. I’ve been at this for a while and no other devForum threads have this issue or the solution.

1 Like

Ah yes, me and another user made a post on this glitch already. I used to do the exact same thing you did here by forcing shift lock for a third person camera. It has to do with combining Humanoid.CameraOffset with Humanoid.AutoRotate. I noticed it since my game has fly, and going backward was a lot glitchy. If you turn off the AutoRotate, and manually force the character to look towards the camera, it won’t glitch.

The character no longer gltiches on the clients screen, but obviously it does not rotate with the camera anymore and is forever stuck facing one way. What did you do once you set AutoRotate to false?

Manually rotating the HumanoidRootPart towards the camera in a RenderStepped loop seems to work best.

i see that you use RenderStepped in your code if you want smoother experience i reccomend using BindToRenderStepped

We’re getting somewhere. This is my code:

runService:BindToRenderStep("Before camera", Enum.RenderPriority.Camera.Value - 1, function()
	humanoid.AutoRotate = false
	local hitpoint = mouse.Hit.p
	local currentTime = tick()
	if camera.CameraType == Enum.CameraType.Follow and humanoid.MoveDirection.Magnitude > 0 and humanoid.WalkSpeed > 0 then
		local swayX = math.cos(currentTime * 15) * 0.3
		local swayY = math.abs(math.sin(currentTime * 15) * 0.3)
		local sway = Vector3.new(swayX + 4.5, swayY, 1)
		humanoid.CameraOffset = humanoid.CameraOffset:lerp(sway, 0.25)
	elseif camera.CameraType ~= Enum.CameraType.Follow or humanoid.MoveDirection.Magnitude == 0 or humanoid.WalkSpeed == 0 then
		local offsetStandard = Vector3.new(4.5, 0, 1)
		humanoid.CameraOffset = humanoid.CameraOffset:Lerp(offsetStandard, 0.25)
	end
	local newTween = tweenService:Create(rootPart,MyTweenInfo,
		{CFrame = CFrame.new(rootPart.Position,Vector3.new(hitpoint.x,rootPart.Position.y,hitpoint.z))})
	newTween:Play()
end)

It works fine if there is a bunch of empty space in front of me, but if my mouse is looking at a nearby object, my character glitches out really horrendously. If I stare at the floor too, my character rotates very fast around my mouse.

How would I fix this?

EDIT: It is because my character faces the actual place where my mouse hits, not the direction of it. What would I substitute hitpoint with to make it face the direction, not the mouse.Hit.p?

Oh, don’t point towards the mouse pointer, point it towards the camera:

Vector3.new(workspace.CurrentCamera.CFrame.LookVector.X,0,workspace.CurrentCamera.CFrame.LookVector.Z)

Also you need to add the RootPart position to the Vector3 or the character will glitch out.

This does the same thing as if I had never added the tween and only turned off AutoRotate, the character doesn’t rotate at all. What do you mean add the rootPart position to the Vector3?

Hold on, is this kind of what you’re trying to obtain? Cause if it’s not maybe I’m giving you some wrong infos.
https://gyazo.com/a54ce1ab55412695b05d9ef9d76dffaa
This is how I made it for my game ^

Kind of? I would prefer if there was no delay between camera movement and character movement, considering it’s a fast paced shooter, but the fundamentals are the same.

What’s currently happening is the character does not rotate at all, the camera just rotated around the character at a slight offset, and the character also doesn’t rotate when I have it walk forward.

Yeah no worries, I made the CFrame lerp cause I preferred it like that.

Well that’s weird. This is how I make it point towards the camera:

RootPart.CFrame = CFrame.new(RootPart.Position, RootPart.Position + Vector3.new(CurrentCamera.CFrame.LookVector.X, 0 , CurrentCamera.CFrame.LookVector.Z)
4 Likes

There we go!

The issue was I was tweening the CFrame for whatever reason instead of just setting it to that.

There was still a bit of glitch so I lerp’d it at about 0.5 to fix it, but I actually prefer the lerp now because it adds power to the turns and makes it feel natural.

Thanks!

1 Like

Hi, sorry to necrobump this topic, but I’ve been trying to solve the same problem, and have been reading this topic as one of the main ones to try solve my own shift lock problems.

I’ve come up with a different solution that I’d like to provide.

I would not use CameraOffset for anything to do with making a shiftlock, since it always makes the character glitch, and when I have tried the solution presented here, lerping it only hides the glitchy-ness, rather than solve it entirely.

The following code here does a similar fix, without using CameraOffset, so there is NO glitch at all. (Part of it was taken from the Developer Hub’s example code for “over-the-shoulder”.

local CameraAngleX = 0
local CameraAngleY = 0

local function CalculateCamera(ActionName, InputState, InputObject)
	-- This whole function is to calculate 2 values needed to set the Camera's rotation every frame.
	if InputState == Enum.UserInputState.Change then
		CameraAngleX = CameraAngleX - InputObject.Delta.X
		CameraAngleY = math.clamp(CameraAngleY-InputObject.Delta.Y*0.4, -75, 75)
	end
end

local function ShiftLock(Active)
	if Active then
		Humanoid.AutoRotate = false

		Humanoid.RootPart.CFrame = Humanoid.RootPart.CFrame:Lerp(CFrame.new(Humanoid.RootPart.Position, Humanoid.RootPart.Position + Vector3.new(workspace.CurrentCamera.CFrame.LookVector.X, 0 , workspace.CurrentCamera.CFrame.LookVector.Z)), 0.3) 
		game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
			Humanoid.RootPart.CFrame = Humanoid.RootPart.CFrame:Lerp(CFrame.new(Humanoid.RootPart.Position, Humanoid.RootPart.Position + Vector3.new(workspace.CurrentCamera.CFrame.LookVector.X, 0 , workspace.CurrentCamera.CFrame.LookVector.Z)), 0.3) 

			local ZoomMagnitude = (math.abs((workspace.CurrentCamera.CFrame.Position - Character.Head.Position).Magnitude))

			local startCFrame = workspace.CurrentCamera.CFrame
			local cameraCFrame = startCFrame:ToWorldSpace(CFrame.new(3, 0, (ZoomMagnitude/20)-0.5))
			local cameraFocus = startCFrame:ToWorldSpace(CFrame.new(3, 0, -10000))
			workspace.CurrentCamera.CFrame = CFrame.new(cameraCFrame.Position, cameraFocus.Position)
			game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter
		end)
	else
		Humanoid.AutoRotate = true
		game:GetService("RunService"):UnbindFromRenderStep("ShiftLock")
		game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.Default
	end
end

I just wanted to share this as some additional details, as well as for any future users who also have the same problem as we did.

7 Likes