Skip to content

Camera

The camera controls how the game world is presented on screen. It handles virtual resolution, viewport management, smooth movement, and coordinate conversion. Tecs supports multiple cameras for effects like minimaps, split-screen, and render-to-texture.

Configuration

Camera settings are provided via the render table in tecs2d.run. These configure the primary camera, which renders fullscreen by default.

teal
local tecs2d = require("tecs2d")

-- Retro pixel art game (integer-scaled, no pixel crawl)
love.run = tecs2d.run({
    fps = 60,
    game = gamePlugin,
    render = {
        virtualHeight = 270,
        pixelMode = true,
    },
})

-- HD game with virtual resolution for consistent world view
love.run = tecs2d.run({
    fps = 60,
    game = gamePlugin,
    render = {
        virtualHeight = 270,         -- always show 270 world units of height
        -- virtualWidth computed from aspect ratio automatically
    },
})

All camera settings are optional. Additional options include:

teal
render = {
    cameraPosition = {0, 0},   -- Initial position (default: {0, 0})
    zoom = 1.0,                -- Initial zoom level (default: 1.0)
    lerpingEnabled = true,     -- Enable smooth camera movement (default: true)
    lerpSpeed = 8.0,           -- Lerp speed factor (default: 8.0)
    clampToBounds = false,     -- Clamp camera to world bounds (default: false)
    worldBounds = {0, 0, 1000, 1000},  -- {minX, minY, maxX, maxY}
}

Accessing the Camera

Access the primary camera through the pipeline resource:

teal
local gfx = require("tecs2d.gfx")

local pipeline = world.resources[gfx.PIPELINE]
local cam = pipeline:getCamera()

All camera methods are called on the camera object directly, not on the pipeline.

Pixel Modes

The pixelMode boolean controls how rendering is scaled to the screen:

ValueDescription
falseSmooth full-resolution rendering (default)
trueInteger-scaled low-res canvas with nearest-neighbor filtering. Eliminates pixel crawl via blit-shift.
teal
-- Change pixel mode at runtime
pipeline:setPixelMode(true)   -- enable retro
pipeline:setPixelMode(false)  -- disable retro

-- Get current pixel mode
local isRetro = pipeline:getPixelMode()

How retro mode works (blit-shift)

Retro mode uses integer scaling with a blit-shift technique that completely eliminates pixel crawl:

  1. Camera snaps to the virtual pixel grid: snappedPos = floor(camPos / worldPixelSize) * worldPixelSize
  2. The render pass uses zero sub-pixel offset, so every pixel is perfect at virtual resolution
  3. The sub-pixel remainder is applied as an integer screen-pixel offset when blitting the final canvas
  4. Every virtual pixel maps to exactly intScale x intScale screen pixels
  5. A scissor clips to a fixed viewport so letterbox bars remain stable

Because the sub-pixel shift happens at the screen-pixel level (after integer scaling), virtual pixels never straddle screen pixel boundaries and there is no crawl or shimmer.

When pixelMode is false, rendering is smooth at full resolution with no snap — the camera scrolls at fractional positions and bilinear filtering handles any sub-pixel motion.

Retro resolution

The integer scale factor and virtual dimensions are computed as follows:

  • intScale = floor(screenHeight / virtualHeight) (e.g., floor(720 / 270) = 2)
  • virtualWidth = floor(screenWidth / intScale) fills the screen width (e.g., floor(1280 / 2) = 640)
  • Only small vertical letterbox bars appear from the integer constraint
  • The camera FOV adapts to the screen aspect ratio: wider screens see more of the world

Coordinate conversion

cam:toWorld() and cam:toScreen() work correctly regardless of pixelMode. After rendering, the pipeline updates the camera's view-projection matrix to account for the render-to-screen mapping, so mouse coordinates always convert to the correct world positions.

Camera Controls

Position

The camera uses center-based positioning: the position represents the center of the camera's view.

teal
local cam = pipeline:getCamera()

-- Set position immediately (ignores lerping)
cam:setPosition(100, 200)

-- Move to position with smooth lerping
cam:move(100, 200)

-- Nudge by delta amount (useful for WASD input)
cam:nudge(dx, dy)

-- Get current position
local x, y = cam:getPosition()

Zoom

Zoom controls how much of the world is visible. Values greater than 1 zoom in (less visible area); values less than 1 zoom out (more visible area).

teal
-- Set zoom level (1 = normal, 2 = 2x zoom in, 0.5 = 2x zoom out)
cam:setZoom(0.5)

-- Get current zoom
local zoom = cam:getZoom()

Smooth Movement

When lerping is enabled, calls to move() and nudge() smoothly interpolate to the target position.

teal
-- Enable/disable smooth movement
cam:setLerpingEnabled(true)
cam:setLerpingEnabled(false)

-- Adjust lerp speed (higher = snappier, lower = smoother)
cam:setLerpSpeed(12.0)  -- Snappy response
cam:setLerpSpeed(4.0)   -- Very smooth but slower

Input-driven camera movement:

teal
local input = require("tecs2d.input")
local cam = pipeline:getCamera()
local speed = 200

world:addSystem({
    name = "CameraControls",
    phase = tecs.phases.FixedUpdate,
    run = function(dt)
        if input.isKeyDown("w") then cam:nudge(0, -speed * dt) end
        if input.isKeyDown("a") then cam:nudge(-speed * dt, 0) end
        if input.isKeyDown("s") then cam:nudge(0, speed * dt) end
        if input.isKeyDown("d") then cam:nudge(speed * dt, 0) end
    end
})

World Bounds

Clamp the camera so it cannot scroll past the edges of the world:

teal
cam:setWorldBounds(0, 0, 3200, 2400)
cam:setClamp(true)

Rotation

teal
-- Set rotation in radians (uses lerp if enabled)
cam:setRotation(math.pi / 4)

-- Get current rotation
local angle = cam:getRotation()

Screenshake

The camera includes a trauma-based screenshake system. Trauma is a 0-1 value that decays over time; shake intensity is proportional to trauma squared (trauma^2). Perlin noise produces smooth, continuous rumble for both translational and rotational shake. The shake offsets are applied to the rendered position without affecting the camera's logical position.

teal
local cam = pipeline:getCamera()

-- Trigger screenshake (e.g., on hit or explosion)
cam:shake(0.5)       -- Add 0.5 trauma (stacks with existing)
cam:shake(1.0)       -- Full-intensity shake

-- Configure shake parameters
cam:setShakeIntensity(12)    -- Max pixel offset at full trauma (default: 8)
cam:setShakeRotation(0.05)   -- Max rotation in radians at full trauma (default: 0.03)
cam:setTraumaDecay(2.0)      -- Decay rate in trauma/second (default: 1.5)

-- Query current state
local trauma = cam:getTrauma()

Screenshake works independently of lerping: it applies to both lerped and non-lerped cameras. The visible bounds are padded by the max shake intensity (a constant), not the current trauma, to prevent entities at screen edges from popping in and out as the shake decays.

Coordinate Conversion

Convert between world and screen coordinates:

teal
-- Convert screen coordinates to world coordinates (e.g., for mouse input)
local worldX, worldY = cam:toWorld(love.mouse.getPosition())

-- Convert world coordinates to screen coordinates
local screenX, screenY = cam:toScreen(entity.x, entity.y)

Visibility

Query the camera's visible area:

teal
-- Get visible world bounds
local left, top, right, bottom = cam:getVisibleCorners()

-- Get visible dimensions
local width, height = cam:getVisibleDimensions()

-- Check if a rectangle is visible (useful for culling)
if cam:isVisible(x, y, width, height) then
    -- Entity is on screen
end

CameraTarget Component

The CameraTarget component makes the primary camera automatically follow an entity. Add it to any entity with a Transform.

teal
local gfx = require("tecs2d.gfx")

-- Make the player the camera target
world:spawn(
    tecs.builtins.Transform(100, 100),
    gfx.Sprite.fromAseprite("player.png", "idle"),
    gfx.CameraTarget()
)

The camera uses its lerping settings to smoothly follow the target.

Configuration

PropertyTypeDefaultDescription
offsetXnumber0Horizontal offset from entity center
offsetYnumber0Vertical offset from entity center
activebooleantrueWhether the camera should follow this target
deadZoneWnumber0Half-width of dead zone in world units (0 = disabled)
deadZoneHnumber0Half-height of dead zone in world units (0 = disabled)
lookAheadXnumber0Horizontal look-ahead factor (0 = disabled)
lookAheadYnumber0Vertical look-ahead factor (0 = disabled)
teal
-- Camera focuses above and to the right of the entity
gfx.CameraTarget({ offsetX = 20, offsetY = -30 })

-- Camera target that starts inactive
gfx.CameraTarget({ active = false })

Dead Zone and Look-Ahead

The dead zone defines a rectangular area around the screen center where the target can move without the camera following. The camera only moves when the target exits the dead zone, keeping it at the edge. This reduces camera motion for small movements, making gameplay feel smoother.

Look-ahead shifts the camera in the direction the target is moving, giving the player more visibility ahead. The look-ahead offset is smoothed with framerate-independent exponential decay.

teal
-- Dead zone: camera stays still for small movements
gfx.CameraTarget({
    deadZoneW = 40,   -- 40 world units half-width
    deadZoneH = 30,   -- 30 world units half-height
})

-- Look-ahead: camera leads the target's movement
gfx.CameraTarget({
    lookAheadX = 3.0,   -- 3x velocity look-ahead horizontally
    lookAheadY = 2.0,   -- 2x velocity look-ahead vertically
})

-- Both together
gfx.CameraTarget({
    deadZoneW = 32,
    deadZoneH = 24,
    lookAheadX = 2.5,
    lookAheadY = 1.5,
})

Teleport detection: If the target moves more than 500 world units from the camera in a single frame (e.g., map transition or respawn), the camera snaps immediately instead of lerping.

Pausing and Resuming

Toggle active to pause or resume camera following:

teal
-- Pause following (e.g., during cutscene)
local target = world:get(playerId, gfx.CameraTarget)
target.active = false

-- Pan camera manually during cutscene
cam:move(cutsceneX, cutsceneY)

-- Resume following
target.active = true

Multiple Targets

If multiple entities have active CameraTarget components, the camera follows whichever one is processed last. For predictable behavior, ensure only one entity has an active CameraTarget at a time.

Multiple Cameras

Create additional cameras for minimaps, split-screen, or render-to-texture effects using pipeline:newCamera() (e.g., a security camera rendered in-game). Each camera renders the scene independently with its own position, zoom, and viewport.

Camera Config

When creating a camera with pipeline:newCamera(), you can pass an optional config table:

FieldTypeDefaultDescription
position{x, y}{0, 0}Initial camera position
zoomnumber1.0Initial zoom level
lerpingEnabledbooleantrueEnable smooth movement
lerpSpeednumber8.0Lerp speed factor
viewport{x, y, w, h}fullscreenScreen-space viewport rectangle for split-screen
canvasCanvasnoneRender to this canvas instead of blitting to screen
layerMaskinteger0xFFFF16-bit bitmask of visible layers
layers{integer}allList of visible layer numbers (builds mask)
layerRange{min, max}allContiguous range of visible layers (builds mask)
disableDrawPhasebooleanfalseDisable CPU Draw-phase systems for this camera

Minimap Camera

A minimap camera renders the entire world to a small off-screen texture. Pass a canvas to render off-screen without blitting to the display, then draw that canvas wherever you want (screen overlay, in-game TV, etc.).

teal
local MINIMAP_W, MINIMAP_H = 200, 150
local WORLD_W, WORLD_H = 3200, 2400
local VIRTUAL_HEIGHT = 360

local minimapPixelScale = MINIMAP_H / VIRTUAL_HEIGHT
local zoomX = MINIMAP_W / WORLD_W
local zoomY = MINIMAP_H / WORLD_H

local minimapCam = pipeline:newCamera("minimap", {
    canvas = love.graphics.newCanvas(MINIMAP_W, MINIMAP_H),
    lerpingEnabled = false,
    position = {WORLD_W / 2, WORLD_H / 2},
    zoom = math.min(zoomX, zoomY) / minimapPixelScale,
})

Draw the minimap as a screen overlay in PostRender:

teal
world:addSystem({
    name = "MinimapOverlay",
    phase = tecs.phases.PostRender,
    run = function()
        local canvas = minimapCam:getCanvas()
        if not canvas then return end

        -- Draw minimap in top-right corner
        local mx = love.graphics.getWidth() - MINIMAP_W - 10
        local my = 10
        love.graphics.setColor(1, 1, 1, 1)
        love.graphics.draw(canvas, mx, my)

        -- Draw viewport indicator
        local cx, cy = cam:getPosition()
        local vw, vh = cam:getVisibleDimensions()
        local x1 = mx + (cx - vw / 2) / WORLD_W * MINIMAP_W
        local y1 = my + (cy - vh / 2) / WORLD_H * MINIMAP_H
        local w = vw / WORLD_W * MINIMAP_W
        local h = vh / WORLD_H * MINIMAP_H
        love.graphics.setColor(1, 1, 1, 0.9)
        love.graphics.setLineWidth(1)
        love.graphics.rectangle("line", x1, y1, w, h)
    end
})

Draw the minimap as an in-game TV in the Draw phase (world coordinates):

teal
world:addSystem({
    name = "TVDraw",
    phase = tecs.phases.Draw,
    run = function()
        local canvas = minimapCam:getCanvas()
        if canvas then
            love.graphics.setColor(1, 1, 1, 1)
            love.graphics.draw(canvas, 400, 400, 0, 300 / MINIMAP_W, 225 / MINIMAP_H)
        end
    end
})

Split-Screen

A split-screen setup uses viewport to assign each camera a portion of the screen:

teal
local screenW = love.graphics.getWidth()
local screenH = love.graphics.getHeight()
local halfW = math.floor(screenW / 2) - 1

-- Deactivate primary camera
local primaryCam = pipeline:getCamera()
primaryCam:setActive(false)

-- Left viewport
local leftCam = pipeline:newCamera("left", {
    viewport = {0, 0, halfW, screenH},
    lerpingEnabled = true,
    lerpSpeed = 8,
    position = {WORLD_W * 0.3, WORLD_H / 2},
})
leftCam:setWorldBounds(0, 0, WORLD_W, WORLD_H)
leftCam:setClamp(true)

-- Right viewport
local rightCam = pipeline:newCamera("right", {
    viewport = {halfW + 2, 0, halfW, screenH},
    lerpingEnabled = true,
    lerpSpeed = 8,
    position = {WORLD_W * 0.7, WORLD_H / 2},
})
rightCam:setWorldBounds(0, 0, WORLD_W, WORLD_H)
rightCam:setClamp(true)

Each camera can be controlled independently:

teal
-- Left camera: WASD
if input.isKeyDown("w") then leftCam:nudge(0, -speed * dt) end
if input.isKeyDown("a") then leftCam:nudge(-speed * dt, 0) end

-- Right camera: arrow keys
if input.isKeyDown("up") then rightCam:nudge(0, -speed * dt) end
if input.isKeyDown("left") then rightCam:nudge(-speed * dt, 0) end

Draw a divider between viewports in PostRender:

teal
world:addSystem({
    name = "Divider",
    phase = tecs.phases.PostRender,
    run = function()
        love.graphics.setColor(0.2, 0.2, 0.2, 1)
        love.graphics.rectangle("fill", halfW, 0, 2, screenH)
    end
})

See the example

See the multi-cam example for a complete implementation that combines minimap, in-game TV, and split-screen cameras.

Pipeline Camera API

pipeline:getCamera

Get a camera by name. Called with no arguments, returns the primary camera.

teal
function Pipeline:getCamera(name?: string): Camera

Parameters:

  • name (optional): The camera name. If omitted, returns the primary camera.

Returns:

  • The Camera instance, or nil if no camera with that name exists.

Example:

teal
local cam = pipeline:getCamera()            -- primary camera
local mini = pipeline:getCamera("minimap")  -- named camera

pipeline:newCamera

Create and register a new camera.

teal
function Pipeline:newCamera(name: string, config?: CameraConfig): Camera

Parameters:

  • name: Unique name for the camera.
  • config (optional): A CameraConfig table. Screen dimensions and virtual height default to the pipeline's current values.

Returns:

  • The newly created Camera.

Notes:

  • Throws an error if a camera with the same name already exists.
  • The camera starts active. Call setActive(false) to defer rendering.

Example:

teal
local minimapCam = pipeline:newCamera("minimap", {
    canvas = love.graphics.newCanvas(200, 150),
    position = {1600, 1200},
})

pipeline:removeCamera

Remove a registered camera by name.

teal
function Pipeline:removeCamera(name: string)

Parameters:

  • name: The camera name to remove.

Notes:

  • The primary camera cannot be removed.
  • No-op if the name is not found.

Camera API

setPosition

Set the camera position immediately, bypassing lerp interpolation. Both the current and target positions are updated.

teal
function Camera:setPosition(x: number, y: number)

Parameters:

  • x: The x position (world coordinates).
  • y: The y position (world coordinates).

move

Move the camera to a target position. If lerping is enabled, the camera smoothly interpolates to the target. Otherwise, the position is set immediately.

teal
function Camera:move(x: number, y: number)

Parameters:

  • x: The target x position (world coordinates).
  • y: The target y position (world coordinates).

nudge

Move the camera by a delta amount relative to its current target position. Useful for input-driven movement.

teal
function Camera:nudge(dx: number, dy: number)

Parameters:

  • dx: Horizontal delta.
  • dy: Vertical delta.

Example:

teal
-- Move camera right at 200 units/sec
cam:nudge(200 * dt, 0)

getPosition

Get the current interpolated camera position.

teal
function Camera:getPosition(): number, number

Returns:

  • x: Current x position.
  • y: Current y position.

setZoom

Set the camera zoom level. Values greater than 1 zoom in; values less than 1 zoom out. Clamped to a minimum of 0.001.

teal
function Camera:setZoom(zoom: number)

Parameters:

  • zoom: The zoom level.

Notes:

  • If bounds clamping is enabled, the position is re-clamped after the zoom change to account for the new visible area.

getZoom

Get the current zoom level.

teal
function Camera:getZoom(): number

Returns:

  • The current zoom level.

setRotation

Set the camera rotation. If lerping is enabled, the rotation smoothly interpolates via the shortest angular path.

teal
function Camera:setRotation(rotation: number)

Parameters:

  • rotation: Rotation angle in radians.

getRotation

Get the current camera rotation.

teal
function Camera:getRotation(): number

Returns:

  • The current rotation in radians.

setLerpingEnabled

Enable or disable smooth camera interpolation. When disabled, the camera snaps to its target position immediately.

teal
function Camera:setLerpingEnabled(enabled: boolean)

Parameters:

  • enabled: true to enable lerping, false to disable.

Notes:

  • Disabling lerp immediately snaps the camera to its target position.

setLerpSpeed

Set the lerp speed factor. Higher values produce snappier movement; lower values produce smoother, slower tracking.

teal
function Camera:setLerpSpeed(speed: number)

Parameters:

  • speed: Lerp speed factor, clamped to the range 1-20.

setClamp

Enable or disable clamping the camera position to world bounds.

teal
function Camera:setClamp(enabled: boolean)

Parameters:

  • enabled: true to clamp, false to allow free scrolling.

setWorldBounds

Set the world bounds used for clamping. The camera will not scroll past these edges when clamping is enabled.

teal
function Camera:setWorldBounds(minX: number, minY: number, maxX: number, maxY: number)

Parameters:

  • minX: Left edge of the world.
  • minY: Top edge of the world.
  • maxX: Right edge of the world.
  • maxY: Bottom edge of the world.

toWorld

Convert screen-space coordinates to world-space coordinates, accounting for camera position, zoom, and rotation.

teal
function Camera:toWorld(screenX: number, screenY: number): number, number

Parameters:

  • screenX: X position on the screen.
  • screenY: Y position on the screen.

Returns:

  • worldX: X position in the world.
  • worldY: Y position in the world.

Example:

teal
local wx, wy = cam:toWorld(love.mouse.getPosition())

toScreen

Convert world-space coordinates to screen-space coordinates.

teal
function Camera:toScreen(worldX: number, worldY: number): number, number

Parameters:

  • worldX: X position in the world.
  • worldY: Y position in the world.

Returns:

  • screenX: X position on the screen.
  • screenY: Y position on the screen.

getVisibleCorners

Get the world-space bounding box of the camera's visible area.

teal
function Camera:getVisibleCorners(): number, number, number, number

Returns:

  • x1: Left edge.
  • y1: Top edge.
  • x2: Right edge.
  • y2: Bottom edge.

Notes:

  • When the camera is rotated, this returns the axis-aligned bounding box of the rotated viewport, which is slightly larger than the actual visible area.

getVisibleDimensions

Get the width and height of the camera's visible area in world units.

teal
function Camera:getVisibleDimensions(): number, number

Returns:

  • width: Visible width in world units.
  • height: Visible height in world units.

isVisible

Check if a world-space rectangle is within the camera's visible area. Useful for culling expensive draw calls.

teal
function Camera:isVisible(x: number, y: number, w: number, h: number): boolean

Parameters:

  • x: Left edge of the rectangle.
  • y: Top edge of the rectangle.
  • w: Width of the rectangle.
  • h: Height of the rectangle.

Returns:

  • true if any part of the rectangle overlaps the visible area.

setActive

Enable or disable this camera. Inactive cameras are skipped during rendering.

teal
function Camera:setActive(active: boolean)

Parameters:

  • active: true to enable rendering, false to skip.

isActive

Check if this camera is currently active.

teal
function Camera:isActive(): boolean

Returns:

  • true if the camera will render.

setViewport

Set the screen-space viewport rectangle. This also updates the camera's internal screen dimensions to match the viewport aspect ratio.

teal
function Camera:setViewport(x: number, y: number, w: number, h: number)

Parameters:

  • x: Left edge in screen pixels.
  • y: Top edge in screen pixels.
  • w: Width in screen pixels.
  • h: Height in screen pixels.

Notes:

  • Use 0, 0, 0, 0 to reset to fullscreen rendering.

getViewport

Get the current viewport rectangle.

teal
function Camera:getViewport(): number, number, number, number

Returns:

  • x, y, w, h: The viewport rect. 0, 0, 0, 0 means fullscreen.

setLayerMask

Set the 16-bit layer visibility bitmask. Bit N-1 corresponds to layer N. For example, 0x0003 makes layers 1 and 2 visible.

teal
function Camera:setLayerMask(mask: integer)

Parameters:

  • mask: 16-bit bitmask. 0xFFFF makes all layers visible.

getLayerMask

Get the current layer visibility bitmask.

teal
function Camera:getLayerMask(): integer

Returns:

  • The 16-bit layer mask.

setLayers

Set visible layers from a list of layer numbers. Replaces the current mask.

teal
function Camera:setLayers(layers: {integer})

Parameters:

  • layers: List of layer numbers (1-16).

Example:

teal
cam:setLayers({1, 2, 5})  -- Only layers 1, 2, and 5 visible

setLayerRange

Set visible layers from a contiguous range. Replaces the current mask.

teal
function Camera:setLayerRange(minLayer: integer, maxLayer: integer)

Parameters:

  • minLayer: First visible layer (inclusive).
  • maxLayer: Last visible layer (inclusive).

setLayer

Toggle visibility of a single layer without affecting other layers.

teal
function Camera:setLayer(layer: integer, enabled: boolean)

Parameters:

  • layer: Layer number (1-16).
  • enabled: true to make visible, false to hide.

isLayerVisible

Check if a specific layer is visible to this camera.

teal
function Camera:isLayerVisible(layer: integer): boolean

Parameters:

  • layer: Layer number (1-16).

Returns:

  • true if the layer is visible.

setRunDrawPhase

Enable or disable the CPU Draw phase for this camera. When enabled, systems registered in tecs.phases.Draw will run during this camera's render pass with the camera's transform applied.

teal
function Camera:setRunDrawPhase(enabled: boolean)

Parameters:

  • enabled: true to run Draw-phase systems.

Notes:

  • The primary camera always runs the Draw phase. Secondary cameras only run it when explicitly enabled.
  • Required for texture cameras that need custom CPU drawing (e.g., minimap with in-game TV overlay).

getRunDrawPhase

Check if the CPU Draw phase is enabled for this camera.

teal
function Camera:getRunDrawPhase(): boolean

Returns:

  • true if Draw-phase systems run for this camera.

getName

Get the camera's name as set during creation.

teal
function Camera:getName(): string

Returns:

  • The name string. The primary camera's name is "__primary".

getCanvas

Get the rendered canvas for render-to-texture cameras (created with canvas).

teal
function Camera:getCanvas(): love.graphics.Canvas

Returns:

  • The canvas containing the camera's rendered output, or nil for viewport cameras.

Notes:

  • Returns the canvas passed to the constructor, so getCanvas() returns a valid canvas even before the first render frame.
  • Draw this canvas in a PostRender system (screen overlay) or a Draw system (in-game display).

shake

Add trauma to trigger screenshake. Stacks with existing trauma, clamped to 0-1.

teal
function Camera:shake(amount: number)

Parameters:

  • amount: Trauma amount to add (0-1). Values above 1 are clamped.

Example:

teal
cam:shake(0.5)   -- Medium shake
cam:shake(1.0)   -- Maximum shake

setShakeIntensity

Set the maximum pixel offset at full trauma (trauma = 1).

teal
function Camera:setShakeIntensity(intensity: number)

Parameters:

  • intensity: Max pixel offset (default: 8). Clamped to >= 0.

setShakeRotation

Set the maximum rotational shake in radians at full trauma.

teal
function Camera:setShakeRotation(maxAngle: number)

Parameters:

  • maxAngle: Max rotation in radians (default: 0.03, approximately 1.7 degrees). Clamped to >= 0.

setTraumaDecay

Set the trauma decay rate.

teal
function Camera:setTraumaDecay(decay: number)

Parameters:

  • decay: Decay rate in trauma per second (default: 1.5). Higher values make the shake end faster.

getTrauma

Get the current trauma level.

teal
function Camera:getTrauma(): number

Returns:

  • The current trauma level (0-1).

setScreenSize

Update the camera's screen dimensions. Called automatically by the pipeline on window resize.

teal
function Camera:setScreenSize(sw: integer, sh: integer, vh: integer)

Parameters:

  • sw: Screen width in pixels.
  • sh: Screen height in pixels.
  • vh: Virtual height for resolution scaling.

TIP

You typically do not need to call this directly; the pipeline handles it on resize.