Camera Bobbing Changes Character Orientation

Hello, for a little-bit now, I’ve been stumped on a problem where a first person camera bob would cause my character to turn relative to the bobbing. Fixes I know about is using the Humanoid’s CameraOffsetproperty to manipulate the camera, but this severely limits the extent I can manipulate the view-bobbing. Another fix I do know about is disabling the Humanoid’s AutoRotate property and manually updating it but I do also want to avoid that if at all possible.

My main method of changing the camera’s viewbobbing is by changing the camera’s CFrame with a trig function:

local SwayX = SWAY_X_AMPLITUDE * math.cos(tick() * SWAY_X_FREQUENCY)
local SwayY = SWAY_Y_AMPLITUDE * math.sin(tick() * SWAY_Y_FREQUENCY) + SWAY_Y_OFFSET

Camera.CFrame = Camera.CFrame + Vector3.new(SwayX, SwayY, 0)

Your camera bob is being interpreted as camera rotation, so the Humanoid turns to follow it. Apply the camera bob after the default camera runs and by translating the camera in it’s local space without changing rotation.

local RS  = game:GetService("RunService")
local cam = workspace.CurrentCamera

local SWAY_X_AMPLITUDE, SWAY_X_FREQ   = 0.15, 9
local SWAY_Y_AMPLITUDE, SWAY_Y_FREQ   = 0.10, 13
local SWAY_Y_OFFSET                   = 0

local function onRenderStep(dt)
	local base = cam.CFrame
	local t    = tick()

	local swayX = SWAY_X_AMPLITUDE * math.cos(t * SWAY_X_FREQ)
	local swayY = SWAY_Y_AMPLITUDE * math.sin(t * SWAY_Y_FREQ) + SWAY_Y_OFFSET

	-- translate in camera-space (no new yaw/pitch/roll)
	local worldOffset = base:VectorToWorldSpace(Vector3.new(swayX, swayY, 0))
	local pos         = base.Position + worldOffset

	-- preserve look vector + up vector exactly
	cam.CFrame = CFrame.lookAt(pos, pos + base.LookVector, base.UpVector)
end

-- run after Roblox camera finishes (prevents feedback into character facing)
RS:BindToRenderStep("ViewBob", Enum.RenderPriority.Camera.Value + 1, onRenderStep)
2 Likes

The line Enum.RenderPriority.Camera.Value + 1 doesn’t really fix the issue, I’ve already had this in my code. Secondly, one issue was with me offsetting the X value, that seemed to cause the issue for some reason.
Also this code still has the same issue, but thanks for the reply!

1 Like

Anim10

I managed to replicate this functionality using this LocalScript in StarterPlayer > StarterPlayerScripts (for R15):

local RS  = game:GetService("RunService")
local cam = workspace.CurrentCamera

local SWAY_X_AMPLITUDE, SWAY_X_FREQ = 0.08, 9
local SWAY_Y_AMPLITUDE, SWAY_Y_FREQ = 0.12, 13
local SWAY_Y_OFFSET                  = 0

local cachedPos, Xv, Yv, Zv

RS:BindToRenderStep("VB_Cache", Enum.RenderPriority.Camera.Value, function()
	local base = cam.CFrame
	cachedPos = base.Position
	Xv, Yv, Zv = base.XVector, base.YVector, base.ZVector
end)

RS:BindToRenderStep("VB_Apply", Enum.RenderPriority.Camera.Value + 2, function(dt)
	if not cachedPos then return end

	local t     = tick()
	local swayX = SWAY_X_AMPLITUDE * math.cos(t * SWAY_X_FREQ)
	local swayY = SWAY_Y_AMPLITUDE * math.sin(t * SWAY_Y_FREQ) + SWAY_Y_OFFSET

	local pos = cachedPos + (Xv * swayX) + (Yv * swayY)

	cam.CFrame = CFrame.fromMatrix(pos, Xv, Yv, Zv)
end)

-- (When disabling): RS:UnbindFromRenderStep("VB_Cache"); RS:UnbindFromRenderStep("VB_Apply")

Use this for R6:

local RS  = game:GetService("RunService")
local cam = workspace.CurrentCamera

local SWAY_X_AMPLITUDE, SWAY_X_FREQ = 0.08, 9
local SWAY_Y_AMPLITUDE, SWAY_Y_FREQ = 0.12, 13
local SWAY_Y_OFFSET                  = 0

local cachedPos, Xv, Yv, Zv

RS:BindToRenderStep("VB_Cache", Enum.RenderPriority.Camera.Value + 1, function()
	local base = cam.CFrame
	cachedPos  = base.Position
	Xv, Yv, Zv = base.XVector, base.YVector, base.ZVector
end)

RS:BindToRenderStep("VB_Apply", Enum.RenderPriority.Camera.Value + 2, function(dt)
	if not cachedPos then return end
	local t     = tick()
	local swayX = SWAY_X_AMPLITUDE * math.cos(t * SWAY_X_FREQ)
	local swayY = SWAY_Y_AMPLITUDE * math.sin(t * SWAY_Y_FREQ) + SWAY_Y_OFFSET

	local pos = cachedPos + (Xv * swayX) + (Yv * swayY)
	cam.CFrame = CFrame.fromMatrix(pos, Xv, Yv, Zv)
end)
2 Likes

Not exactly a solution, but thought it was fun lol

1 Like