--- name: roblox-camera description: "Use when scripting or fixing Roblox camera behavior: CFrame placement, screen raycasts, first/third-person, shoulder views, cutscenes." when_to_use: "Also when the camera clips through walls, jitters, aims wrong on touch or gamepad, or needs a death cam or spectate view." last_reviewed: 2026-10-14 sources: - https://create.roblox.com/docs/reference/engine/classes/Camera - https://create.roblox.com/docs/reference/engine/enums/CameraType - https://create.roblox.com/docs/reference/engine/datatypes/CFrame - original - https://raw.githubusercontent.com/Roblox/creator-docs/main/content/en-us/reference/engine/classes/WorldRoot.yaml --- # Roblox Camera ## When to Load Load when scripting custom camera behavior (cutscenes, third-person follow, custom rotation), manipulating `CFrame` for placement/rotation, raycasting from the screen, or building a non-default player view. Client-only. ## Quick Reference **The Camera**: `workspace.CurrentCamera`: one per client. Set `CameraType = Scriptable` to disable defaults and take full control. Without it, defaults overwrite your CFrame every frame. **CameraType**: `Fixed`, `Attach`/`Watch`/`Track`/`Follow` (subject-following), `Custom` (default), `Scriptable` (no default), `Orbital` (fixed Y, rotates around player). `CameraSubject` cannot be `nil`: setting it reverts. **Key properties**: `CFrame`, `CameraSubject`, `FieldOfView` (deg), `FieldOfViewMode` (`Vertical`/`Diagonal`), `NearPlaneZ`, `ViewportSize`, `HeadLocked`, `HeadScale`, `Focus`. **CFrame essentials**: | Operation | Luau | | --- | --- | | Position only | `CFrame.new(position)` | | Look at target | `CFrame.lookAt(position, target, upVector)` | | XYZ rotation | `CFrame.Angles(rx, ry, rz)` | | Local to world | `cf * Vector3.new(0, 0, 10)` | | Interpolate | `cf:Lerp(goal, alpha)` | | Unit axes | `cf.LookVector`, `cf.RightVector`, `cf.UpVector` | **Custom camera loop**: always `PreRender`, never `Heartbeat`: ```luau camera.CameraType = Enum.CameraType.Scriptable RunService.PreRender:Connect(function(dt) local desired = CFrame.lookAt(head.Position - offset, head.Position) camera.CFrame = camera.CFrame:Lerp(desired, math.min(dt * 10, 1)) end) ``` **Raycasting from camera**: `camera:ScreenPointToRay(mx, my)` (accounts for GUI inset) vs `camera:ViewportPointToRay(mx, my)` (raw, NO inset). `ScreenPointToRay` returns a unit Ray (1 stud); multiply `Direction` by length for the actual raycast. **Pitfalls**: - Client-only. Server sets silently dropped. - Without `Scriptable`, defaults overwrite your CFrame every frame. - `PreRender` for camera (visual sync). `Heartbeat` adds 1-frame lag. `RenderStepped` still works but is superseded by `PreRender`. - `Camera.CFrame` lacks VR head rotation; use `GetRenderCFrame()` for the true view. - `SetRoll` is outdated; apply roll via `CFrame.Angles(0, 0, roll)` on CFrame. - `CFrame.new(pos, lookAt)` is legacy (back-compat only); use `CFrame.lookAt(at, lookAt)` for new code. See `references/full.md` for first/third-person recipes, cutscenes, screen shake, mouse-look, full API.