First person camera not rotating properly

I’m currently trying to make an FPS style camera

The main issue is that trying to rotate my camera on the X-axis gives unexpected results, as seen here

I’ve done many google searches and nothing has shown to be successful yet.

local nN = math.rad(-85)
local pN = math.rad(85)
-- stuffs
do
    local delta = UIS:GetMouseDelta()
    local x,y,z = camera.CFrame:ToOrientation()
    local hx,hy,hz = char.HumanoidRootPart.CFrame:ToOrientation()
    
    local ang = CFrame.Angles(
        math.clamp( -- I can confirm that math.clamp isn't the issue
            x + math.rad(-delta.Y/2),
            hx + nN,
            hx + pN
        ),
        math.clamp(
            y + math.rad(-delta.X/2),
            hy + nN,
            hy + pN
        ),
        0
    )
            
    camera.CFrame = CFrame.new(char.Head.Position) * ang
end
3 Likes

The problem is that rotating a CFrame that way rotates it relative to how it’s already rotated. If you pitch up, then try to yaw in either direction, you’ll be yawing relative to the current CFrame. That means rotating around the object-space Y axis of the current CFrame. It’s not pointing straight up, so you get a weird compound rotation.

You fix this by rotating around the correct axes. You can rotate about a specific axis by doing

--set position while leaving rotation unchanged
camera.CFrame = camera.CFrame - camera.CFrame.p + char.Head.Position

--pitch
local rightAxis = camera.CFrame.RightVector --valid since the camera should never have any roll / Z rotation, so the object-space RightVector never points a little bit up/down in world space
camera.CFrame *= CFrame.fromAxisAngle(rightAxis, delta.X)
--comes out to the exact same as camera.CFrame *= CFrame.Angles(delta.X, 0, 0)

--yaw
local upAxis = camera.CFrame:VectorToObjectSpace( Vector3.new(0, 1, 0) )
camera.CFrame *= CFrame.fromAxisAngle(upAxis, delta.Y)

Let me know if this works. I’m writing this from what I remember, since I recently had to do this in my own project. It might not be 100% correct tho

1 Like

Thanks for the answer, however, your code results in this

Instead of

local ang = CFrame.Angles(x,y,0)

try

local ang = CFrame.Angles(0,y,0) * CFrame.Angles(x,0,0)
1 Like

Oh, forgot to scale the delta to make it a lot smaller xD Try multiplying delta.X and delta.Y by like 0.01 to get something that makes more sense.

So, this does work, but how can I clamp the rotation on each axis with fromAxisAngle?

Figured it out, I needed to use CFrame.fromEulerAnglesYXZ instead of CFrame.Angles.