hump.camera

Camera = require "hump.camera"

A camera utility for LÖVE. A camera can “look” at a position. It can zoom in and out and it can rotate it’s view. In the background, this is done by actually moving, scaling and rotating everything in the game world. But don’t worry about that.

Example:

function love.load()
    camera = Camera(player.pos.x, player.pos.y)
end

function love.update(dt)
    local dx,dy = player.x - cam.x, player.y - cam.y
    camera:move(dx/2, dy/2)
end

Function Reference

Camera.new(x, y, zoom, rot)
Arguments:
  • x,y (numbers) – Point for the camera to look at. (optional)
  • zoom (number) – Camera zoom. (optional)
  • rot (number) – Camera rotation in radians. (optional)
Returns:

A new camera.

Creates a new camera. You can access the camera position using camera.x, camera.y, the zoom using camera.scale and the rotation using camera.rot.

The module variable name can be used at a shortcut to new().

Example:

camera = require 'hump.camera'
-- camera looking at (100,100) with zoom 2 and rotated by 45 degrees
cam = camera(100,100, 2, math.pi/2)
camera:move(dx, dy)
Arguments:
  • dx,dy (numbers) – Direction to move the camera.
Returns:

The camera.

Move the camera by some vector. To set the position, use camera:lookAt().

This function is shortcut to camera.x,camera.y = camera.x+dx, camera.y+dy.

Examples:

function love.update(dt)
    camera:move(dt * 5, dt * 6)
end
function love.update(dt)
    camera:move(dt * 5, dt * 6):rotate(dt)
end
camera:lookAt(x, y)
Arguments:
  • x,y (numbers) – Position to look at.
Returns:

The camera.

Let the camera look at a point. In other words, it sets the camera position. To move the camera by some amount, use camera:move().

This function is shortcut to camera.x,camera.y = x, y.

Examples:

function love.update(dt)
    camera:lookAt(player.pos:unpack())
end
function love.update(dt)
    camera:lookAt(player.pos:unpack()):rotate(player.rot)
end
camera:position()
Returns:x,y – Camera position.

Returns camera.x, camera.y.

Example:

-- let the camera fly!
local cam_dx, cam_dy = 0, 0

function love.mousereleased(x,y)
    local cx,cy = camera:position()
    dx, dy = x-cx, y-cy
end

function love.update(dt)
    camera:move(dx * dt, dy * dt)
end
camera:rotate(angle)
Arguments:
  • angle (number) – Rotation angle in radians
Returns:

The camera.

Rotate the camera by some angle. To set the angle use camera:rotateTo().

This function is shortcut to camera.rot = camera.rot + angle.

Examples:

function love.update(dt)
    camera:rotate(dt)
end
function love.update(dt)
    camera:rotate(dt):move(dt,dt)
end
camera:rotateTo(angle)
Arguments:
  • angle (number) – Rotation angle in radians
Returns:

The camera.

Set rotation: camera.rot = angle.

Example:

camera:rotateTo(math.pi/2)
camera:zoom(mul)
Arguments:
  • mul (number) – Zoom change. Should be > 0.
Returns:

The camera.

Multiply zoom: camera.scale = camera.scale * mul.

Examples:

camera:zoom(2)   -- make everything twice as big
camera:zoom(0.5) -- ... and back to normal
camera:zoom(-1)  -- mirror and flip everything upside down
camera:zoomTo(zoom)
Arguments:
  • zoom (number) – New zoom.
Returns:

The camera.

Set zoom: camera.scale = zoom.

Example:

camera:zoomTo(1) -- reset zoom
camera:attach()

Start looking through the camera.

Apply camera transformations, i.e. move, scale and rotate everything until camera:detach() as if looking through the camera.

Example:

function love.draw()
    camera:attach()
    draw_world()
    camera:detach()

    draw_hud()
end
camera:detach()

Stop looking through the camera.

Example:

function love.draw()
    camera:attach()
    draw_world()
    camera:detach()

    draw_hud()
end
camera:draw(func)
Arguments:
  • func (function) – Drawing function to be wrapped.

Wrap a function between a camera:attach()/camera:detach() pair. Equivalent to:

camera:attach()
func()
camera:detach()

Example:

function love.draw()
    camera:draw(draw_world)
    draw_hud()
end
camera:worldCoords(x, y)
Arguments:
  • x, y (numbers) – Point to transform.
Returns:

x,y – Transformed point.

Because a camera has a point it looks at, a rotation and a zoom factor, it defines a coordinate system. A point now has two sets of coordinates: One defines where the point is to be found in the game world, and the other describes the position on the computer screen. The first set of coordinates is called world coordinates, the second one camera coordinates. Sometimes it is needed to convert between the two coordinate systems, for example to get the position of a mouse click in the game world in a strategy game, or to see if an object is visible on the screen.

camera:worldCoords() and camera:cameraCoords() transform points between these two coordinate systems.

Example:

x,y = camera:worldCoords(love.mouse.getPosition())
selectedUnit:plotPath(x,y)
camera:cameraCoords(x, y)
Arguments:
  • x, y (numbers) – Point to transform.
Returns:

x,y – Transformed point.

Because a camera has a point it looks at, a rotation and a zoom factor, it defines a coordinate system. A point now has two sets of coordinates: One defines where the point is to be found in the game world, and the other describes the position on the computer screen. The first set of coordinates is called world coordinates, the second one camera coordinates. Sometimes it is needed to convert between the two coordinate systems, for example to get the position of a mouse click in the game world in a strategy game, or to see if an object is visible on the screen.

camera:worldCoords() and camera:cameraCoords() transform points between these two coordinate systems.

Example:

x,y = camera:cameraCoords(player.pos.x, player.pos.y)
love.graphics.line(x, y, love.mouse.getPosition())
camera:mousePosition()
Returns:Mouse position in world coordinates.

Shortcut to camera:worldCoords(love.mouse.getPosition()).

Example:

x,y = camera:mousePosition()
selectedUnit:plotPath(x,y)

Camera Movement Control

Camera movement is one of these things that go almost unnoticed when done well, but add a lot to the overall experience. The article Scroll Back: The Theory and Practice of Cameras in SideScrollers by Itay Keren gives a lot of insight into how to design good camera systems.

hump.camera offers functions that help to implement most of the techniques discussed in the article. The functions camera:lockX(), camera:lockY(), camera:lockPosition(), and camera:lockWindow() move the camera so that the interesting content stays in frame. Note that the functions must be called every frame:

function love.update()
   -- vertical locking
   camera:lockX(player.pos.x)
end

All movements are subject to smoothing (see Movement Smoothers). You can specify a default movement smoother by assigning the variable camera.smoother:

cam.smoother = Camera.smooth.linear(100)
camera:lockX(x, smoother, ...)
Arguments:
  • x (number) – X coordinate (in world coordinates) to lock to.
  • smoother (function) – Movement smoothing override. (optional)
  • ... (mixed) – Additional parameters to the smoothing function. (optional)

Horizontal camera locking: Keep the camera locked on the defined x-position (in world coordinates). The y-position is not affected.

You can define an off-center locking position by “aiming” the camera left or right of your actual target. For example, to center the player 20 pixels to the left of the screen, aim 20 pixels to it’s right (see examples).

Examples:

-- lock on player vertically
camera:lockX(player.x)
-- ... with linear smoothing at 25 px/s
camera:lockX(player.x, Camera.smooth.linear(25))
-- lock player 20px left of center
camera:lockX(player.x + 20)
camera:lockY(y, smoother, ...)
Arguments:
  • y (number) – Y coordinate (in world coordinates) to lock to.
  • smoother (function) – Movement smoothing override. (optional)
  • ... (mixed) – Additional parameters to the smoothing function. (optional)

Vertical camera locking: Keep the camera locked on the defined y-position (in world coordinates). The x-position is not affected.

You can define an off-center locking position by “aiming” the camera above or below your actual target. For example, to center the player 20 pixels below the screen center, aim 20 pixels above it (see examples).

Examples:

-- lock on player horizontally
camera:lockY(player.y)
-- ... with damped smoothing with a stiffness of 10
camera:lockY(player.y, Camera.smooth.damped(10))
-- lock player 20px below the screen center
camera:lockY(player.y - 20)
camera:lockPosition(x, y, smoother, ...)
Arguments:
  • x,y (numbers) – Position (in world coordinates) to lock to.
  • smoother (function) – Movement smoothing override. (optional)
  • ... (mixed) – Additional parameters to the smoothing function. (optional)

Horizontal and vertical camera locking: Keep the camera locked on the defined position (in world coordinates).

You can define an off-center locking position by “aiming” the camera to the opposite direction away from your real target. For example, to center the player 10 pixels to the left and 20 pixels above the screen center, aim 10 pixels to the right and 20 pixels below.

Examples:

-- lock on player
camera:lockPosition(player.x, player.y)
-- lock 50 pixels into player's aiming direction
camera:lockPosition(player.x - player.aiming.x * 50, player.y - player.aiming.y * 50)
camera:lockWindow(x, y, x_min, x_max, y_min, y_max, smoother, ...)
Arguments:
  • x,y (numbers) – Position (in world coordinates) to lock to.
  • x_min (numbers) – Upper left X coordinate of the camera window (in camera coordinates!).
  • x_max (numbers) – Lower right X coordinate of the camera window (in camera coordinates!).
  • y_min (numbers) – Upper left Y coordinate of the camera window (in camera coordinates!).
  • y_max (numbers) – Lower right Y coordinate of the camera window (in camera coordinates!).
  • smoother (function) – Movement smoothing override. (optional)
  • ... (mixed) – Additional parameters to the smoothing function. (optional)

The most powerful locking method: Lock camera to x,y, but only move the camera if the position would be out of the screen-rectangle defined by x_min, x_max, y_min, y_max.

Note

The locking window is defined in camera coordinates, whereas the position to lock to is defined in world coordinates!

All of the other locking methods can be implemented by window locking. For position locking, set x_min = x_max and y_min = y_max. Off-center locking can be done by defining the locking window accordingly.

Examples:

-- lock on player
camera:lock(player.x, player.y)
camera.smoother

The default smoothing operator. Must be a function with the following prototype:

function customSmoother(dx,dy, ...)
    do_stuff()
    return new_dx,new_dy
end

where dx,dy is the offset the camera would move before smoothing and new_dx, new_dy is the offset the camera should move after smoothing.

Movement Smoothers

It is not always desirable that the camera instantly locks on a target. Platform snapping, for example, would look terrible if the camera would instantly jump to the focussed platform. Smoothly moving the camera to the locked position can also give the illusion of a camera operator an add to the overall feel of your game.

hump.camera allows to smooth the movement by either passing movement smoother functions to the locking functions or by setting a default smoother (see camera.smoother).

Smoothing functions must have the following prototype:

function customSmoother(dx,dy, ...)
    do_stuff()
    return new_dx,new_dy
end

where dx,dy is the offset the camera would move before smoothing and new_dx, new_dy is the offset the camera should move after smoothing.

This is a simple “rubber-band” smoother:

function rubber_band(dx,dy)
    local dt = love.timer.getDelta()
    return dx*dt, dy*dt
end

hump.camera defines generators for the most common smoothers:

Camera.smooth.none()
Returns:Smoothing function.

Dummy smoother: does not smooth the motion.

Example:

cam.smoother = Camera.smooth.none()
Camera.smooth.linear(speed)
Arguments:
  • speed (number) – Smoothing speed.
Returns:

Smoothing function.

Smoothly moves the camera towards to snapping goal with constant speed.

Examples:

cam.smoother = Camera.smooth.linear(100)
-- warning: creates a function every frame!
camera:lockX(player.x, Camera.smooth.linear(25))
Camera.smooth.damped(stiffness)
Arguments:
  • stiffness (number) – Speed of the camera movement.
Returns:

Smoothing function.

Smoothly moves the camera towards the goal with a speed proportional to the distance to the target. Stiffness defines the speed of the motion: Higher values mean that the camera moves more quickly.

Examples:

cam.smoother = Camera.smooth.damped(10)
-- warning: creates a function every frame!
camera:lockPosition(player.x, player.y, Camera.smooth.damped(2))