CFrame::ToOrientation is an alias for CFrame::ToEulerAnglesYXZ so you should be doing rY, rX, rZ = CFrame:ToOrientation()
I think it is easiest to just fork the CameraModule and edit the MIN_Y and MAX_Y locals.
Do a play test and copy the PlayerModule from your player’s PlayerScripts then stop the play test and paste it into StarterPlayerScripts. From here you can make changes to the CameraModule.
And if you want to also limit the X axis you need to add some new locals like MIN_X and MAX_X and apply the following changes to BaseCamera::CalculateNewLookCFrameFromArg
function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): CFrame
local currLookVector: Vector3 = suppliedLookVector or self:GetCameraLookVector()
local currPitchAngle = math.asin(currLookVector.Y)
local yTheta = math.clamp(rotateInput.Y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
local currYawAngle = math.asin(currLookVector.X)
local xTheta = math.clamp(rotateInput.X, -MAX_X + currYawAngle, -MIN_X + currYawAngle)
local constrainedRotateInput = Vector2.new(xTheta, yTheta)
local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.X, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.Y,0,0)
return newLookCFrame
end
Forking the camera module isn’t an option in my case because I need to be able to switch between the player’s normal camera and a ball camera (hence why I’m using BindToRenderStep).
You could try limiting it before and after the camera. If done at the correct time your code should work, it uses the same principle as every camera shake module I’ve read.