Camera tilt goes beyond expected

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I’m trying to make a wallrun code and whenever the player starts wallrunning, I want the camera to rotate to give some sense of immersion.

  2. What is the issue? Include screenshots / videos if possible!
    At times the code works just as intended. I try to clamp the Z-rotation between -11.25 degrees and 11.25 degrees. But, some times, it randomly goes beyond -180;180, turning the camera upside down? Here’s a video snippet:
    https://youtu.be/uQqV5SmQlRA

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub?
    I tried to look for solutions online, but none of them worked with Roblox’s Lua. And none of the past issues about camera tilting gave me some new information to work with.

this is a snippet of the code. yes, the side == "Right"/"Left" always is correct, I skipped over the raycasting portion since that isn’t relevant to the issue.

local function onWallrunCameraTiltingRenderStep(delta:number)

	local pos:Vector3 = camera.CFrame.Position
	local xRotation:number, yRotation:number = camera.CFrame:ToEulerAnglesXYZ()

--CodeCodeCode

if math.abs(wallrunCameraTilt) < maxWallrunCameraTilt 
		and character.Humanoid:GetState() == Enum.HumanoidStateType.Freefall
		and side == "Right" 
	then
		wallrunCameraTilt += delta*maxWallrunCameraTilt
		wallrunCameraTilt = math.clamp(wallrunCameraTilt, 0, 90)

	elseif math.abs(wallrunCameraTilt) < maxWallrunCameraTilt 
		and character.Humanoid:GetState() == Enum.HumanoidStateType.Freefall
		and side == "Left" 
	then
		wallrunCameraTilt -= delta*maxWallrunCameraTilt
		wallrunCameraTilt = math.clamp(wallrunCameraTilt, -90, 0)
	end
	
	
	
	if wallrunCameraTilt > 0 
		and side == "Nil"
	then
		wallrunCameraTilt -= delta*maxWallrunCameraTilt*2
		if wallrunCameraTilt < .5 then
			wallrunCameraTilt = 0
		end
		
		
	elseif wallrunCameraTilt < 0 
		and side == "Nil"
	then
		wallrunCameraTilt += delta*maxWallrunCameraTilt*2
		if wallrunCameraTilt > .5 then
			wallrunCameraTilt = 0
		end
	end
	
	if wallrunCameraTilt ~= 0 then
		camera.CFrame = CFrame.new(pos)*CFrame.Angles(xRotation,yRotation,math.rad(wallrunCameraTilt))
		local _:number,_:number,adjustedZ:number = camera.CFrame:ToEulerAnglesXYZ()
		print("So, Z",adjustedZ)
		adjustedZ = math.clamp(adjustedZ, -math.rad(maxWallrunCameraTilt), math.rad(maxWallrunCameraTilt)) --maxWallrunCameraTilt = 11.25

		camera.CFrame = CFrame.new(pos)*CFrame.Angles(xRotation,yRotation,adjustedZ)

	end
end --End of renderstepped

-- some other code here
RunService:BindToRenderStep("WallrunCameraTilting", Enum.RenderPriority.Camera.Value + 1, onWallrunCameraTiltingRenderStep)

I am really inexperienced when it comes to camera manipulation, let alone anything that requires rotations :slightly_frowning_face:. I’d be grateful for anyone who has any idea on what could be the issue and on how to fix it.

Small bump: I somehow need to keep the camera between angle -x and x (for example, -11.25 and 11.25), in other words, I need the game to take the shortest rotation route. Got any clue on how I could make that? :face_with_diagonal_mouth:. I’m willing to rework my entire code

After some searching, I learnt that Roblox cameras used to have these “Roll” functions (SetRoll and GetRoll). And after doing research about them I figured out a way simpler way to handle my issue with way less lines:

local wallrunAdditionalRotation = CFrame.new()
local rad = math.rad
local function getDotProduct()

	if not character then
		return
	end
	local humanoid:Humanoid? = character:FindFirstChildOfClass("Humanoid")
	if not humanoid then return end
	local dotProduct:number = camera.CFrame.RightVector:Dot(humanoid.MoveDirection)
	
	return dotProduct
end

local function onTiltCamera()
	
	if character:GetAttribute("Wallrunning") then 
		local Roll = getDotProduct()*20
		wallrunAdditionalRotation = wallrunAdditionalRotation:Lerp(CFrame.Angles(0, 0, rad(Roll)),0.075)
		
	else
		wallrunAdditionalRotation = wallrunAdditionalRotation:Lerp(CFrame.Angles(0,0,0),0.075)
	end

	camera.CFrame *= wallrunAdditionalRotation
end

RunService:BindToRenderStep("TiltCameraDuringWallrun", Enum.RenderPriority.Camera.Value + 1, onTiltCamera)

I don’t know how I didn’t think of this earlier, it’s really a smart way to make it work (lemme explain this shortly that way other people in the future won’t suffer as much as I did :sob:. Also because I really dislike it when people mark their posts as “solved”, yet don’t provide any details on how they solved it, destroying half of the purpose of his devforum, the “sharing information on how to solve problem X” part) :

  • since the camera always faces in the same direction the player moves, the camera’s right vector and MoveDirection’s Z axis are always 90°, meaning that the camera won’t react to the player moving forwards or backwards.
  • HOWEVER, if the player goes to, lets say right, , MoveDirection’s X axis slowly grows towards 1. dot product also slowly becomes 1. Since it becomes 1, we then multiply it with our desired maximal angle. And to make the roll smooth, we use lerping. And finally, we’re using math.rad cuz… idk, at times the bug STILL happens but converting a radian to… radian again, somehow fixes it? Please lemme know if you have more information regarding this, or maybe it’s just me imagining things.
  • If it goes left, it’s the same reasoning, just that it’ll be negative instead.

That’s what I was able to gather, if I’m wrong or someone wants to add more information, feel free to reply here :smile:.
P.S.: You can loop up Roblox studio CFrame:Lerp in google to learn more about lerping. as for dot product, feel free to check out here for a simple introduction. Cya! :wave:

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.