Wall run system, camera Rotation not working as expected

Ah okay I believe what’s likely happening is the roblox camera script is overwriting the orientation right before RenderStepped fires. Would you be able to do me a favor and rather than lerp the camera cframe, save a global rotation value, and lerp that every frame, and set the camera to the resulting value?

could you give me a example of what you mean?

local rotationSpeed = 5
local cameraRoll = 45

local rotationCframe = CFrame.Angles()
runService.RenderStepped:Connect(function(deltaTime)

	-- // Calculate Wall Directions
	local wallNormal = result.Normal
	local wallFowardDir = wallNormal:Cross(Vector3.new(0, 1, 0))
	local dot = root.CFrame.LookVector:Dot(wallFowardDir)
	local lookDir = dot < 0 and -wallFowardDir.Magnitude or wallFowardDir.Magnitude
			
	-- // Camera Rotation
	local currentRot = camera.CFrame * CFrame.Angles(0, 0, math.rad(lookDir * cameraRotation))
	local rx, ry, rz = currentRot:ToOrientation()
	local rotClamp = math.clamp(math.deg(rz), -cameraRotation, cameraRotation)
	local newCFrame = CFrame.new(camera.CFrame.Position) * CFrame.fromOrientation(rx, ry, math.rad(rotClamp))

    rotationCFrame = rotationCFrame:Lerp(currentRot, math.min(rotationSpeed * deltaTime, 1))
	camera.CFrame = rotationCFrame

end)

try this out and let me know if it worked!

nope doesn’t work, It bugs the camera out

ah my bad it’s because i initialize the rotation to 0,0,0 when really you should be setting it to the camera’s rotation.

Try this on initialization and lmk
local rotationCFrame = CFrame.Angles(camera.CFrame:ToOrientation())

Try this instead

-- // Calculate Wall Directions
local wallNormal = result.Normal
local wallFowardDir = wallNormal:Cross(Vector3.new(0, 1, 0))
local dot = root.CFrame.LookVector:Dot(wallFowardDir)
local lookDir = dot < 0 and -wallFowardDir.Magnitude or wallFowardDir.Magnitude
			
-- // Camera Rotation
local currentRot = camera.CFrame * CFrame.Angles(0, 0, math.rad(lookDir * cameraRotation))
local rx, ry, rz = currentRot:ToOrientation()
local rotClamp = math.clamp(math.deg(rz), -cameraRotation, cameraRotation)
local newCFrame = CFrame.fromOrientation(rx, ry, math.rad(rotClamp))

local _, _, z = rotationCFrame:ToOrientation()
rotationCFrame = CFrame.FromEulerAnglesYXZ(rx, ry, z)

rotationCFrame = rotationCFrame:Lerp(newCFrame, math.min(rotationSpeed * deltaTime, 1))
camera.CFrame = CFrame.new(camera.CFrame.Position) * rotationCFrame

im getting a error

attempt to call a nil value

local _, _, z = rotationCFrame:ToOrientation()
rotationCFrame = CFrame.FromEulerAnglesYXZ(rx, ry, z) -- this lines errors

my bad it’s supposed to be a lowercase f for CFrame.fromEulerAnglesYXZ
i’m a little tired rn haha

ah. its still buggy

ah weird! do you minds sending your code again? i want to see what you have so far.

yeah

        local rotationCFrame = CFrame.Angles(0, 0, 0)

		-- // Camera Rotation
		local currentRot = camera.CFrame * CFrame.Angles(0, 0, math.rad(lookDir * cameraRotation))
		local rx, ry, rz = currentRot:ToOrientation()
		local rotClamp = math.clamp(math.deg(rz), -cameraRotation, cameraRotation)
		local newCFrame = CFrame.new(camera.CFrame.Position) * CFrame.fromOrientation(rx, ry, math.rad(rotClamp))
		--camera.CFrame = camera.CFrame:Lerp(newCFrame, rotationSpeed * deltaTime)
		
		local _, _, z = rotationCFrame:ToOrientation()
		rotationCFrame = CFrame.fromEulerAnglesYXZ(rx, ry, z)

		rotationCFrame = rotationCFrame:Lerp(newCFrame, math.min(rotationSpeed * deltaTime, 1))
		camera.CFrame = CFrame.new(camera.CFrame.Position) * rotationCFrame

ah you must’ve missed it but i made some changes that weren’t copied over.
more specifically, local newCFrame = CFrame.fromOrientation(rx, ry, math.rad(rotClamp))

oh whoops, so it smoothly rotates the first time but then after stop wall running, it snaps back its default rotation, and doesn’t smoothly rotate anymore when you start wall running again

You’ll need to set the rotationCFrame to an empty CFrame every wallrun otherwise it’ll remember the angle it was previously at and start from there. So what you’re seeing is it trying to roll the camera in the direction it was the last time you did the wall run.

thats not working either, it just snaps everytime

heres the code that I have

		rotationCFrame = CFrame.Angles(0, 0, 0)
		
		-- // Camera Rotation
		local currentRot = camera.CFrame * CFrame.Angles(0, 0, math.rad(lookDir * cameraRotation))
		local rx, ry, rz = currentRot:ToOrientation()
		local rotClamp = math.clamp(math.deg(rz), -cameraRotation, cameraRotation)
		local newCFrame = CFrame.fromOrientation(rx, ry, math.rad(rotClamp))		
		--camera.CFrame = camera.CFrame:Lerp(newCFrame, rotationSpeed * deltaTime)
		
		
		local _, _, z = rotationCFrame:ToOrientation()
		rotationCFrame = CFrame.fromEulerAnglesYXZ(rx, ry, z)

		rotationCFrame = rotationCFrame:Lerp(newCFrame, math.min(rotationSpeed * deltaTime, 1))
		camera.CFrame = CFrame.new(camera.CFrame.Position) * rotationCFrame

that’s because you’re resetting it in the loop. what you need to do is only reset it once at the start of every wallrun. this could be done either by storing a boolean checking whether we were wallrunning last frame and setting it to true whenever we start, and false when we end. unless you already have a way to check if the player has just started wallrunning elsewhere that i can’t see.

ohh okay, its works now, but is there anyway to stop the camera from snaping back? or do I need a custom camera system? which I already have that set up

if you already have a custom camera system what you could do is expose a method to set the roll of the camera, and in the camera update loop you lerp the current cframe to the roll cframe.

This would mean that when you start wallrunning you call Controller:SetRoll(45)
and when you stop you call Controller:SetRoll(0)

ohh okay ill try that but would I need to set the roll cframe back to 0 somewhere or no?

Yeah that’s what i mentioned at the end. When you want to stop, just call Controller:SetRoll(0)

hold on Im gonna need help moving the code over into the custom camera module