Limiting camera angles with a SeatPart goes well UNTIL

I want to limit where the Player’s Camera can rotate, and my code does that, excluding when a Seat’s angle, added with the maximum angle configured above it, goes over 180°. I knew this was an issue and tried to solve it, but I’ve ended up with having it being able to turn anywhere BUT the intended direction. My code is both from my own brain and with a bit of help from a previous devforum post.

Here is the code I have so far in trying to brew a solution.

local hum = script.Parent:FindFirstChildOfClass("Humanoid")
local Camera = workspace:FindFirstChildOfClass("Camera")
local angleLim = 33.5
local hasSetAngle = false
local SeatPart = hum.SeatPart
hum:GetPropertyChangedSignal("SeatPart"):Connect(function()
	SeatPart = hum.SeatPart
end)

local function applyCameraConstraints()
	if not SeatPart then hasSetAngle = false return end
	local point = CFrame.new(Camera.CFrame.X,Camera.CFrame.Y,Camera.CFrame.Z) * SeatPart.CFrame.Rotation
	if not hasSetAngle then
		hasSetAngle = true
		Camera.CFrame = point
	else
		local totalRotation = SeatPart.Orientation.Y
		local rX, rY, rZ = Camera.CFrame:ToOrientation()
		local limX = math.clamp(math.deg(rX), -50, 20)
		local limY = math.clamp(math.deg(rY),-angleLim+totalRotation,angleLim+totalRotation)
		if totalRotation+angleLim > 180 then
			--dear god, his head's on BACKWARDS!
			local min = -totalRotation+angleLim
			local max = totalRotation-angleLim
			limY = math.clamp(math.deg(rY),min,max)
			print(min,max)
		end
		Camera.CFrame = CFrame.new(Camera.CFrame.Position) * CFrame.fromOrientation(math.rad(limX), math.rad(limY), rZ)
	end
end

game:GetService("RunService"):BindToRenderStep("CameraConstraint",Enum.RenderPriority.Camera.Value+1,applyCameraConstraints)

Expected result: the camera is locked to the set degrees based on the SeatPart’s Orientation.
Actual result: Works as intended until a Seat’s angle + the angle limit exceeds 180°, where the camera will then bug out when it goes to the left. This happens no other time in my testing.
Here is a video showing my issue. Any meaningful help would be appreciated. If I find my solution separate from this, I will make a post updating this.

1 Like

MAYBE the connections are stacking? idk do you disconnect the function when they hop off the seats?

2 Likes

:broken_heart:

I described how my issue is caused by the angle limit + the seat’s .Orientation going above 180°, so the connection is not stacking; this script is bound to runtime on spawn, and the issue happens even within first run. Thank you for trying to help, but this is not part of the issue. Have a nice day my friend.

2 Likes

The bug happens because Roblox caps orientations between -180° and 180°

When your seat’s rotation + the angle limit goes over 180° (e.g. 170° + 33.5° = 203.5°), it wraps around to a negative number (-156.5°). This flips your min and max limits, breaking math.clamp() and forcing the camera to turn the wrong way

To fix it, clamp the camera’s angle relative to the seat’s direction rather than calculating absolute world angles:

local hum = script.Parent:FindFirstChildOfClass("Humanoid")
local Camera = workspace:FindFirstChildOfClass("Camera")
local angleLim = 33.5
local hasSetAngle = false
local SeatPart = hum.SeatPart
hum:GetPropertyChangedSignal("SeatPart"):Connect(function()
	SeatPart = hum.SeatPart
end)

local function applyCameraConstraints()
	if not SeatPart then hasSetAngle = false return end
	local point = CFrame.new(Camera.CFrame.X,Camera.CFrame.Y,Camera.CFrame.Z) * SeatPart.CFrame.Rotation
	if not hasSetAngle then
		hasSetAngle = true
		Camera.CFrame = point
	else
		local totalRotation = SeatPart.Orientation.Y
		local rX, rY, rZ = Camera.CFrame:ToOrientation()
		local camDegY = math.deg(rY)
		
		local limX = math.clamp(math.deg(rX), -50, 20)
		
		local relativeY = camDegY - totalRotation
		relativeY = (relativeY + 180) % 360 - 180
		
		local clampedRelY = math.clamp(relativeY, -angleLim, angleLim)
		local limY = totalRotation + clampedRelY
		
		Camera.CFrame = CFrame.new(Camera.CFrame.Position) * CFrame.fromOrientation(math.rad(limX), math.rad(limY), rZ)
	end
end

game:GetService("RunService"):BindToRenderStep("CameraConstraint",Enum.RenderPriority.Camera.Value+1,applyCameraConstraints)
1 Like

This achieved my desired result, thank you Icy. Have a good day.