How to clamp the player's camera rotation on one axis?

Hello, Internet!

I want to clamp the player’s camera on the X axis while keeping all of the freedom of the Y axis and Z axis, as well as still allow the player to zoom in and out.

The best I’ve been able to come up with so far is this, a Local Script in the StarterGui:

local camera = workspace.CurrentCamera
RunService:BindToRenderStep("UpdateLoop", Enum.RenderPriority.Camera.Value, function()
	local camX, camY, camZ = camera.CFrame:ToEulerAnglesXYZ()
	camera.CFrame = CFrame.new(camera.CFrame.Position) * CFrame.Angles(math.clamp(camX, -.5, .5), camY, camZ)
end)

This doesn’t work well at all. It does limit the ability to turn on the X axis, but the camera stuters like crazy once it reaches those clamp points.

Does anyone know of a much better way to clamp the camera’s rotation on the X axis?

4 Likes

Nevermind! I solved it with an entirely new Local Script in StarterPlayerScripts:

local RunService = game:GetService("RunService")
local camera = workspace.CurrentCamera
RunService:BindToRenderStep("UpdateLoop", Enum.RenderPriority.Camera.Value, function()
	local rX, rY, rZ = camera.CFrame:ToOrientation()
	local limX = math.clamp(math.deg(rX), -45, 45)
	camera.CFrame = CFrame.new(camera.CFrame.p) * CFrame.fromOrientation(math.rad(limX), rY, rZ)
end)

21 Likes

Hi, I know I’m a bit late, but I found this solution and it worked almost perfectly with what I was trying to achieve. The only problem, however, is that the camera tends to rotate slightly in an off direction on the y-axis while you are rotating the x-axis. I have tried using additional resources to fix this, but to no avail. Do you have any idea what’s causing this problem and how to fix it?

I just found this while trying to constrain vertical camera movement myself, in order to prevent looking underneath the world.
I solved it in my case by editing a camera script in the Player Module directly, to override the limit values.

In order to edit the Player Module, you’ll need to do what I believe is called ‘forking’, where you run the game, copy the Player Module from: Players → Player (You) → PlayerScripts → PlayerModule, and paste it in: StarterPlayer → StarterPlayerScripts after exiting play mode.

Then, to limit vertical camera rotation, go to: PlayerModule → CameraModule → BaseCamera, and in the BaseCamera script, right at the top are the values “MIN_Y, MAX_Y”, which determine the angle your camera is smoothly constrained to at all times. Going above 80 breaks it, but any other values can be used to achieve what you want.

(When you say X axis, I assume you mean ‘up and down’, rather than ‘around the player’, but hopefully we both mean ‘up and down’, which I would assume is the Y axis)

2 Likes

For those trying to restrain on the X axis (left/right) make the following changes to the BaseCamera module:

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 currPitchAngleX = math.asin(currLookVector.X) --ADD PITCH ANGLE FOR X
	local xTheta = math.clamp(rotateInput.X, -MAX_X + currPitchAngleX, -MIN_X + currPitchAngleX) --ADD X THETA
	local constrainedRotateInput = Vector2.new(xTheta, yTheta) --SWAP ROTATEINPUT.X FOR X THETA
	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

Then you just have to add a MIN X and MAX X at the top of the script

To reset it, make MAX_X and MIN X 90 and -90 respectively

MAX_X and MIN_X are degrees right?