Humanoid.CameraOffset not working for some users

As part of a sliding mechanic, I’m interpolating the Humanoid’s CameraOffset property over time.

Whenever I playtest, both in Studio and in the live servers, the sliding mechanic seems to be working as intended. Nevertheless, another member of the development team, warned me that, when playing in live servers sometimes the camera doesn’t lower at all. He has showed me videos of this, and whenever I’ve added logs, they confirmed that, despite the CameraOffset property updating properly, for some reason the camera’s height seems to remain more or less consistent (it barely moves).

I’ve also confirm through logs that the function is being called and with the correct arguments.
Previously, I was using TweenService for this, but, in an attempt to solve the issue, I’ve moved to a custom RenderStepped implementation, which apparently changed nothing.

I’ve tried looking at similar posts but I haven’t found any instances of the camera offset not offsetting a player’s camera.

The script runs in a client RunContext so it’s not about that. There is also no other scripts writing to the Humanoid’s CharacterOffset property. Some other details I’ve confirmed through logs and tests:

  • CameraType is set Custom
  • CameraSubject is set to the Humanoid
  • The Humanoid has RootPart set.

I’ve also been told that whenever the slide camera offset stops working, the keybind I’ve set below for testing purposes also stops working, which seems to confirm that the issue is not with the solver itself, but with the CameraOffset property, something is causing it to stop affecting the camera’s Position.

For reference the player.CameraMode is set to LockFirstPerson. I’ve moved the entire camera offset solver onto a different module for clarity when I was debugging, you can check it out below:

--SERVICES--

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--VARIABLES--

local player = Players.LocalPlayer :: Player

local ACTION_ID = "CameraOffsetController"
local PRIORITY = Enum.RenderPriority.Camera.Value - 1

local CameraOffsetController = {}

--PRIVATE FUNCTIONS--

local function easeSineWave(t: number): number
	return -0.5 * (math.cos(t * math.pi) - 1)
end

local function interpolateCameraOffset(humanoid: Humanoid, goal: Vector3, fadeDuration: number): ()
	local startingOffset = humanoid.CameraOffset
	local timer = 0
	RunService:BindToRenderStep(ACTION_ID, PRIORITY, function(dt: number): ()
		timer += dt
		local camera = workspace.CurrentCamera
		if not camera or humanoid.Parent == nil then
			RunService:UnbindFromRenderStep(ACTION_ID)
			return
		elseif timer >= fadeDuration then
			RunService:UnbindFromRenderStep(ACTION_ID)
			humanoid.CameraOffset = goal
			print("ANIMATION FINISHED")
			return
		end
		local t = timer / fadeDuration
		local alpha = math.min(1, easeSineWave(t))
		local offset = startingOffset:Lerp(goal, alpha)
		humanoid.CameraOffset = offset
	end)
end

--PUBLIC FUNCTIONS--

function CameraOffsetController.offsetCamera(offset: Vector3, fadeDuration: number?): ()
	local character = player.Character
	local humanoid = character and character:FindFirstChildWhichIsA("Humanoid")
	if not humanoid then
		return
	end
	RunService:UnbindFromRenderStep(ACTION_ID)
	if not fadeDuration or fadeDuration <= 0 then
		humanoid.CameraOffset = offset
		return
	end
	interpolateCameraOffset(humanoid, offset, fadeDuration)
end

--INITIALIZATION--

function CameraOffsetController.init(): ()
	--test keybind that also does nothing whenever the CameraOffset property bugs
	local ContextActionService = game:GetService("ContextActionService")
	ContextActionService:BindAction("TestOffset", function(_, state: Enum.UserInputState)
		if state ~= Enum.UserInputState.Begin then
			return
		end
		CameraOffsetController.offsetCamera(Vector3.zero)
		CameraOffsetController.offsetCamera(-2 * Vector3.yAxis)
		task.delay(0.5, CameraOffsetController.offsetCamera, Vector3.zero)
	end, false, Enum.KeyCode.E)
end

return table.freeze(CameraOffsetController)
3 Likes