Introduction^top

Helper Utilities for a Multitude of Problems is a set of lightweight helpers for the awesome LÖVE Engine.

hump differs from other libraries in that every component is independent of the remaining ones. hump's footprint is very small and thus should fit nicely into your projects.

Modules^top

hump.gamestate
A gamestate system.
hump.timer
Delayed and time-limited function calls.
hump.vector
2D vector math.
hump.vector-light
Lightweight 2D vector math.
hump.class
Object oriented programming for Lua.
hump.signal
Simple Signal/Slot (aka. Observer) implementation.
hump.camera
A camera for LÖVE.

hump.gamestate^top

Gamestate = require "hump.gamestate"

A gamestate encapsulates independent data an behaviour in a single table.

A typical game could consist of a menu-state, a level-state and a game-over-state.

Example:

local menu = {} -- previously: Gamestate.new()
local game = {}

function menu:draw()
    love.graphics.print("Press Enter to continue", 10, 10)
end

function menu:keyreleased(key, code)
    if key == 'enter' then
        Gamestate.switch(game)
    end
end

function game:enter()
    Entities.clear()
    -- setup entities here
end

function game:update(dt)
    Entities.update(dt)
end

function game:draw()
    Entities.draw()
end

function love.load()
    Gamestate.registerEvents()
    Gamestate.switch(menu)
end

Module overview

Callbacks
Gamestate Callbacks.
new()
Create a new gamestate.
switch()
Switch to gamestate.
<callback>()
Call function on active gamestate.
registerEvents()
Automatically do all of the above when needed.

Callbacks ^top

A gamestate can define all callbacks that LÖVE defines. In addition, there are callbacks for initalizing, entering and leaving a state:

init()
Called once before entering the state. See switch().
enter(previous, ...)
Called when entering the state. See switch().
leave()
Called when leaving a state. See switch().
update()
Update the game state. Called every frame.
draw()
Draw on the screen. Called every frame.
focus()
Called if the window gets or looses focus.
keypressed()
Triggered when a key is pressed.
keyreleased()
Triggered when a key is released.
mousepressed()
Triggered when a mouse button is pressed.
mousereleased()
Triggered when a mouse button is released.
joystickpressed()
Triggered when a joystick button is pressed.
joystickreleased()
Triggered when a joystick button is released.
quit()
Called on quitting the game. Only called on the active gamestate.

When using registerEvents(), all these callbacks will be called by the corresponding LÖVE callbacks and receive receive the same arguments (e.g. state:update(dt) will be called by love.update(dt)).

Example:

menu = {} -- previously: Gamestate.new()
function menu:init() -- run only once
    self.background = love.graphics.newImage('bg.jpg')
    Buttons.initialize()
end

function menu:enter(previous) -- run every time the state is entered
    Buttons.setActive(Buttons.start)
end

function menu:update(dt)
    Buttons.update(dt)
end

function menu:draw()
    love.graphics.draw(self.background, 0, 0)
    Buttons.draw()
end

function menu:keyreleased(key)
    if key == 'up' then
        Buttons.selectPrevious()
    elseif key == 'down' then
        Buttons.selectNext()
    elseif
        Buttons.active:onClick()
    end
end

function menu:mousereleased(x,y, mouse_btn)
    local button = Buttons.hovered(x,y)
    if button then
        Button.select(button)
        if mouse_btn == 'l' then
            button:onClick()
        end
    end
end

function new()^top

Deprecated: Use the table constructor instead (see example)

Declare a new gamestate (just an empty table). A gamestate can define several callbacks.

Parameters:
None
Returns:
Gamestate
An empty table.
Example:
menu = {}
-- deprecated method:
menu = Gamestate.new()

function switch(to, ...)^top

Switch to a gamestate, with any additional arguments passed to the new state.

Switching a gamestate will call the leave() callback on the current gamestate, replace the current gamestate with to, call the init() function if the state was not yet inialized and finally call enter(old_state, ...) on the new gamestate.

Parameters:
Gamestate to
Target gamestate.
mixed ...
Additional arguments to pass to to:enter(current, ...).
Returns:
mixed
The results of to:enter(current, ...)
Example:
Gamestate.switch(game, level_two)

function <callback>(...)^top

Calls a function on the current gamestate. Can be any function, but is intended to be one of the callbacks. Mostly useful when not using registerEvents().

Parameters:
mixed ...
Arguments to pass to the corresponding function.
Returns:
mixed
The result of the callback function.
Example:
function love.draw()
    Gamestate.draw() -- <callback> is `draw'
end

function love.update(dt)
    Gamestate.update(dt) -- pass dt to currentState:update(dt)
end

function love.keypressed(key, code)
    Gamestate.keypressed(key, code) -- pass multiple arguments
end

function registerEvents(callbacks)^top

Overwrite love callbacks to call Gamestate.update(), Gamestate.draw(), etc. automatically. love callbacks (e.g. love.update()) are still invoked.

This is by done by overwriting the love callbacks, e.g.:

local old_update = love.update
function love.update(dt)
    old_update(dt)
    return Gamestate.current:update(dt)
end

Note: Only works when called in love.load() or any other function that is executed after the whole file is loaded.

Parameters:
table callbacks (optional)
Names of the callbacks to register. If omitted, register all love callbacks.
Returns:
Nothing
Examples:
function love.load()
    Gamestate.registerEvents()
    Gamestate.switch(menu)
end

-- love callback will still be invoked
function love.update(dt)
    Timer.update(dt)
    -- no need for Gamestate.update(dt)
end
function love.load()
    -- only register draw, update and quit
    Gamestate.registerEvents{'draw', 'update', 'quit'}
    Gamestate.switch(menu)
end

hump.timer^top

Timer = require "hump.timer"

hump.timer offers a simple interface to schedule the execution of functions. It is possible to run functions after and for some amount of time. For example, a timer could be set to move critters every 5 seconds or to make the player invincible for a short amount of time.

Example:

function love.keypressed(key)
    if key == ' ' then
        Timer.add(1, function() print("Hello, world!") end)
    end
end

function love.update(dt)
    Timer.update(dt)
end

Module overview

new()
Create new timer instance.
add()
Schedule a function.
addPeriodic()
Add a periodic function.
do_for()
Run a function for the next few seconds.
cancel()
Cancel a scheduled function.
clear()
Remove all timed and periodic functions.
update()
Update scheduled functions.

function new()^top

If you don't need multiple independent schedulers, you can use the global/default timer (see examples).

Creates a new timer instance that is independent of the global timer: It will manage it's own list of scheduled functions and does not in any way affect the the global timer. Likewise, the global timer does not affect timer instances.

Note: Timer instances use the colon-notation (e.g. instance:update(dt)), while the global timer uses the dot-notation (e.g. Timer.update(dt)).

Parameters:
None
Returns:
Timer
A timer instance.
Example:
menuTimer = Timer.new()

function add(delay, func)^top

Schedule a function. The function will be executed after delay seconds have elapsed, given that update(dt) is called every frame.

Note: There is no guarantee that the delay will not be exceeded, it is only guaranteed that the function will not be executed before the delay has passed.

func will receive itself as only parameter. This is useful to implement periodic behavior (see the example).

Parameters:
number delay
Number of seconds the function will be delayed.
function func
The function to be delayed.
Returns:
table
The timer handle.
Examples:
-- grant the player 5 seconds of immortality
player.isInvincible = true
Timer.add(5, function() player.isInvincible = false end)
-- print "foo" every second. See also addPeriodic()
Timer.add(1, function(func) print("foo") Timer.add(1, func) end)
--Using a timer instance:
menuTimer:add(1, finishAnimation)

function addPeriodic(delay, func)^top

Add a function that will be called count times every delay seconds.

If count is omitted, the function will be called until it returns false or cancel(handle) or clear() is called.

Parameters:
number delay
Number of seconds between two consecutive function calls.
function func
The function to be called periodically.
number count (optional)
Number of times the function is to be called.
Returns:
table
The timer handle. See also cancel().
Examples:
-- toggle light on and off every second
Timer.addPeriodic(1, function() lamp:toggleLight() end)
-- launch 5 fighters in quick succession (using a timer instance)
mothership_timer:addPeriodic(0.3, function() self:launchFighter() end, 5)
-- flicker player's image as long as he is invincible
Timer.addPeriodic(0.1, function()
    player:flipImage()
    return player.isInvincible
end)

function do_for(delay, func, after)^top

Run func(dt) for the next delta seconds. The function is called every time update(dt) is called. Optionally run after() once delta seconds have passed.

after() will receive itself as only parameter.

Note: You should not add new timers in func(dt), as this can lead to random crashes.

Parameters:
number delta
Number of seconds the func will be called.
function func
The function to be called on update(dt).
function after (optional)
A function to be called after delta seconds.
Returns:
table
The timer handle.
Examples:
-- play an animation for 5 seconds
Timer.do_for(5, function(dt) animation:update(dt) end)
-- shake the camera for one second
local orig_x, orig_y = camera:pos()
Timer.do_for(1, function()
    camera:lookAt(orig_x + math.random(-2,2), orig_y + math.random(-2,2))
end, function()
    -- reset camera position
    camera:lookAt(orig_x, orig_y)
end)
player.isInvincible = true
-- flash player for 3 seconds
local t = 0
player.timer:do_for(3, function(dt)
    t = t + dt
    player.visible = (t % .2) < .1
end, function()
    -- make sure the player is visible after three seconds
    player.visible = true
    player.isInvincible = false
end)

function cancel(handle)^top

Prevent a timer from being executed in the future.

Parameters:
table handle
The function to be canceled.
Returns:
Nothing
Example:
function tick()
    print('tick... tock...')
end
handle = Timer.addPeriodic(1, tick)
-- later
Timer.cancel(handle) -- NOT: Timer.cancel(tick)

function clear()^top

Remove all timed and periodic functions. Functions that have not yet been executed will discarded.

Parameters:
None
Returns:
Nothing
Examples:
Timer.clear()
menu_timer:clear()

function update(dt)^top

Update timers and execute functions if the deadline is reached. Use this in love.update(dt).

Parameters:
number dt
Time that has passed since the last update().
Returns:
Nothing
Examples:
function love.update(dt)
    do_stuff()
    Timer.update(dt)
end
-- using hump.gamestate and a timer instance
function menuState:update(dt)
    self.timer:update(dt)
end

hump.vector^top

vector = require "hump.vector"

A handy 2D vector class providing most of the things you do with vectors.

You can access the individual coordinates by using vec.x and vec.y.

Example:

function player:update(dt)
    local delta = vector(0,0)
    if love.keyboard.isDown('left') then
        delta.x = -1
    elseif love.keyboard.isDown('right') then
        delta.x =  1
    end
    if love.keyboard.isDown('up') then
        delta.y = -1
    elseif love.keyboard.isDown('down') then
        delta.y =  1
    end
    delta:normalize_inplace()

    player.velocity = player.velocity + delta * player.acceleration * dt

    if player.velocity:len() > player.max_velocity then
        player.velocity = player.velocity:normalized() * player.max_velocity
    end

    player.position = player.position + player.velocity * dt
end

Module overview

Operators
Arithmetics and relations.
new()
Create a new vector.
isvector()
Test if value is a vector.
vector:clone()
Copy a vector.
vector:unpack()
Extract coordinates.
vector:permul()
Per element multiplication.
vector:len()
Get length.
vector:len2()
Get squared length.
vector:dist()
Distance to other vector.
vector:normalized()
Get normalized vector.
vector:normalize_inplace()
Normalize vector in-place.
vector:rotated()
Get rotated vector.
vector:rotate_inplace()
Rotate vector in-place.
vector:perpendicular()
Get perpendicular vector.
vector:projectOn()
Get projection onto another vector.
vector:mirrorOn()
Mirrors vector on other vector
vector:cross()
Cross product of two vectors.

Operators ^top

Vector arithmetic is implemented by using __add, __mul and other metamethods:

vector + vector = vector
Component wise sum.
vector - vector = vector
Component wise difference.
vector * vector = number
Dot product.
number * vector = vector
Scalar multiplication (scaling).
vector * number = vector
Scalar multiplication.
vector / number = vector
Scalar multiplication.

Relational operators are defined, too:

a == b
Same as a.x == b.x and a.y == b.y.
a <= b
Same as a.x <= b.x and a.y <= b.y.
a < b
Lexical sort: a.x < b.x or (a.x == b.x and a.y < b.y).

Example:

-- acceleration, player.velocity and player.position are vectors
acceleration = vector(0,-9)
player.velocity = player.velocity + acceleration * dt
player.position = player.position + player.velocity * dt

function new(x,y)^top

Create a new vector.

Parameters:
numbers x,y
Coordinates.
Returns:
vector
The vector.
Examples:
a = vector.new(10,10)
-- as a shortcut, you can call the module like a function:
vector = require "hump.vector"
a = vector(10,10)

function isvector(v)^top

Test whether a variable is a vector.

Parameters:
mixed v
The variable to test.
Returns:
boolean
true if v is a vector, false otherwise
Example:
if not vector.isvector(v) then
    v = vector(v,0)
end

function vector:clone()^top

Copy a vector. Simply assigning a vector a vector to a variable will create a reference, so when modifying the vector referenced by the new variable would also change the old one:

a = vector(1,1) -- create vector
b = a           -- b references a
c = a:clone()   -- c is a copy of a
b.x = 0         -- changes a,b and c
print(a,b,c)    -- prints '(1,0), (1,0), (1,1)'
Parameters:
None
Returns:
vector
Copy of the vector
Example:
copy = original:clone()

function vector:unpack()^top

Extract coordinates.

Parameters:
None
Returns:
numbers
The coordinates
Examples:
x,y = pos:unpack()
love.graphics.draw(self.image, self.pos:unpack())

function vector:permul(other)^top

Multiplies vectors coordinate wise, i.e. result = vector(a.x * b.x, a.y * b.y).

This does not change either argument vectors, but creates a new one.

Parameters:
vector other
The other vector
Returns:
vector
The new vector as described above
Example:
scaled = original:permul(vector(1,1.5))

function vector:len()^top

Get length of a vector, i.e. math.sqrt(vec.x * vec.x + vec.y * vec.y).

Parameters:
None
Returns:
number
Length of the vector.
Example:
distance = (a - b):len()

function vector:len2()^top

Get squared length of a vector, i.e. vec.x * vec.x + vec.y * vec.y.

Parameters:
None
Returns:
number
Squared length of the vector.
Example:
-- get closest vertex to a given vector
closest, dsq = vertices[1], (pos - vertices[1]):len2()
for i = 2,#vertices do
    local temp = (pos - vertices[i]):len2()
    if temp < dsq then
        closest, dsq = vertices[i], temp
    end
end

function vector:dist(other)^top

Get distance of two vectors. The same as (a - b):len().

Parameters:
vector other
Other vector to measure the distance to.
Returns:
number
The distance of the vectors.
Example:
-- get closest vertex to a given vector
-- slightly slower than the example using len2()
closest, dist = vertices[1], pos:dist(vertices[1])
for i = 2,#vertices do
    local temp = pos:dist(vertices[i])
    if temp < dist then
        closest, dist = vertices[i], temp
    end
end

function vector:normalized()^top

Get normalized vector, i.e. a vector with the same direction as the input vector, but with length 1.

This does not change the input vector, but creates a new vector.

Parameters:
None
Returns:
vector
Vector with same direction as the input vector, but length 1.
Example:
direction = velocity:normalized()

function vector:normalize_inplace()^top

Normalize a vector, i.e. make the vector unit length. Great to use on intermediate results.

This modifies the vector. If in doubt, use vector:normalized().

Parameters:
None
Returns:
vector
Itself - the normalized vector
Example:
normal = (b - a):perpendicular():normalize_inplace()

function vector:rotated(angle)^top

Get a rotated vector.

This does not change the input vector, but creates a new vector.

Parameters:
number angle
Rotation angle in radians.
Returns:
vector
The rotated vector
Example:
-- approximate a circle
circle = {}
for i = 1,30 do
    local phi = 2 * math.pi * i / 30
    circle[#circle+1] = vector(0,1):rotated(phi)
end
Sketch:

Rotated vector sketch

function vector:rotate_inplace(angle)^top

Rotate a vector in-place. Great to use on intermediate results.

This modifies the vector. If in doubt, use vector:rotate().

Parameters:
number angle
Rotation angle in radians.
Returns:
vector
Itself - the rotated vector
Example:
-- ongoing rotation
spawner.direction:rotate_inplace(dt)

function vector:perpendicular()^top

Quick rotation by 90°. Creates a new vector. The same (but faster) as vec:rotate(math.pi/2).

Parameters:
None
Returns:
vector
A vector perpendicular to the input vector
Example:
normal = (b - a):perpendicular():normalize_inplace()
Sketch:

Perpendiculat vector sketch

function vector:projectOn(v)^top

Project vector onto another vector (see sketch).

Parameters:
vector v
The vector to project on.
Returns:
vector
The projected vector.
Example:
velocity_component = velocity:projectOn(axis)
Sketch:

Projected vector sketch

function vector:mirrorOn(v)^top

Mirrors vector on the axis defined by the other vector.

Parameters:
vector v
The vector to mirror on.
Returns:
vector
The mirrored vector.
Example:
deflected_velocity = ball.velocity:mirrorOn(surface_normal)
Sketch:

Mirrored vector sketch

function vector:cross(other)^top

Get cross product of both vectors. Equals the area of the parallelogram spanned by both vectors.

Parameters:
vector other
Vector to compute the cross product with.
Returns:
number
Cross product of both vectors.
Example:
parallelogram_area = a:cross(b)

hump.vector-light^top

vector = require "hump.vector-light"

An table-free version of hump.vector. Instead of a vector type, hump.vector-light provides functions that operate on numbers.

Note: Using this module instead of hump.vector might result in faster code, but does so at the expense of readability. Unless you are sure that it causes a significant performance penalty, I recommend using hump.vector.

Example:

function player:update(dt)
    local dx,dy = 0,0
    if love.keyboard.isDown('left') then
        dx = -1
    elseif love.keyboard.isDown('right') then
        dx =  1
    end
    if love.keyboard.isDown('up') then
        dy = -1
    elseif love.keyboard.isDown('down') then
        dy =  1
    end
    dx,dy = vector.normalize(dx, dy)

    player.velx, player.vely = vector.add(player.velx, player.vely,
                                    vector.mul(dy, dx, dy))

    if vector.len(player.velx, player.vely) > player.max_velocity then
        player.velx, player.vely = vector.mul(player.max_velocity,
                            vector.normalize(player.velx, player.vely)
    end

    player.x = player.x + dt * player.velx
    player.y = player.y + dt * player.vely
end

Module overview

str()
String representation.
mul()
Product of a vector and a scalar.
div()
Product of a vector and the inverse of a scalar.
add()
Sum of two vectors.
sub()
Difference of two vectors.
permul()
Per element multiplication.
dot()
Dot product.
cross()
Cross product.
eq()
Equality.
le()
Partial lexical order.
lt()
Strict lexical order.
len()
Get length.
len2()
Get squared length.
dist()
Distance of two points.
normalize()
Normalize vector.
rotate()
Rotate vector.
perpendicular()
Get perpendicular vector.
project()
Project vector onto another vector.
mirror()
Mirror vector on other vector.

function str(x,y)^top

Transforms a vector to a string of the form (x,y).

Parameters:
numbers x,y
The vector
Returns:
string
The string representation
Example:
print(vector.str(love.mouse.getPosition()))

function mul(s, x,y)^top

Computes x*s,y*s. The order of arguments is chosen so that it's possible to chain multiple operations (see example).

Parameters:
number s
The scalar.
numbers x,y
The vector.
Returns:
numbers
x*s, y*s
Example:
velx,vely = vec.mul(dt, vec.add(velx,vely, accx,accy))

function div(s, x,y)^top

Computes x/s,y/s. The order of arguments is chosen so that it's possible to chain multiple operations.

Parameters:
number s
The scalar.
numbers x,y
The vector.
Returns:
numbers
x/s, y/s
Example:
x,y = vec.div(self.zoom, x-w/2, y-h/2)

function add(x1,y1, x2,y2)^top

Computes the sum (x1+x2,y1+y2) of two vectors. Meant to be used in conjunction with other functions.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
numbers
x1+x2, x1+x2
Example:
player.x,player.y = vector.add(player.x,player.y, vector.mul(dt, dx,dy))

function sub(x1,y1, x2,y2)^top

Computes the difference (x1-x2,y1-y2) of two vectors. Meant to be used in conjunction with other functions.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
numbers
x1-x2, x1-x2
Example:
dx,dy = vector.sub(400,300, love.mouse.getPosition())

function permul(x1,y1, x2,y2)^top

Multiplies vectors coordinates, i.e.: x1*x2, y1*y2.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
numbers
x1*x2, y1*y2
Example:
x,y = vector.permul(x,y, 1,1.5)

function dot(x1,y1, x2,y2)^top

Computes the dot product of two vectors: x1*x2 + y1*y2.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
number
x1*x2 + y1*y2
Example:
cosphi = vector.dot(rx,ry, vx,vy)

function cross(x1,y1, x2,y2)^top

Computes the cross product of two vectors, x1*y2 - y1*x2.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
number
x1*y2 - y1*x2
number
x1*y2 - y1*x2
Examples:
parallelogram_area = vector.cross(ax,ay, bx,by)

Alias to [vector.cross(x1,y1, x2,y2)].

parallelogram_area = vector.det(ax,ay, bx,by)

function eq(x1,y1, x2,y2)^top

Test for equality.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
boolean
x1 == x2 and y1 == y2
Example:
if vector.eq(x1,y1, x2,y2) then be.happy() end

function le(x1,y1, x2,y2)^top

Test for partial lexical order, <=.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
boolean
x1 <= x2 and y1 <= y2
Example:
if vector.le(x1,y1, x2,y2) then be.happy() end

function lt(x1,y1, x2,y2)^top

Test for strict lexical order, <.

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
boolean
x1 < x2 or (x1 == x2) and y1 <= y2
Example:
if vector.lt(x1,y1, x2,y2) then be.happy() end

function len(x,y)^top

Get length of a vector, i.e. math.sqrt(x*x + y*y).

Parameters:
numbers x,y
The vector.
Returns:
number
Length of the vector.
Example:
distance = vector.len(love.mouse.getPosition())

function len2(x,y)^top

Get squared length of a vector, i.e. x*x + y*y.

Parameters:
numbers x,y
The vector.
Returns:
number
Squared length of the vector.
Example:
-- get closest vertex to a given vector
closest, dsq = vertices[1], vector.len2(px-vertices[1].x, py-vertices[1].y)
for i = 2,#vertices do
    local temp = vector.len2(px-vertices[i].x, py-vertices[i].y)
    if temp < dsq then
        closest, dsq = vertices[i], temp
    end
end

function dist(x1,y1, x2,y2)^top

Get distance of two points. The same as vector.len(x1-x2, y1-y2).

Parameters:
numbers x1,y1
First vector.
numbers x2,y2
Second vector.
Returns:
number
The distance of the points.
Example:
-- get closest vertex to a given vector
-- slightly slower than the example using len2()
closest, dist = vertices[1], vector.dist(px,py, vertices[1].x,vertices[1].y)
for i = 2,#vertices do
    local temp = vector.dist(px,py, vertices[i].x,vertices[i].y)
    if temp < dist then
        closest, dist = vertices[i], temp
    end
end

function normalize(x,y)^top

Get normalized vector, i.e. a vector with the same direction as the input
vector, but with length 1.
Parameters:
numbers x,y
The vector.
Returns:
numbers
Vector with same direction as the input vector, but length 1.
Example:
dx,dy = vector.normalize(vx,vy)

function rotate(phi, x,y)^top

Get a rotated vector.

Parameters:
number phi
Rotation angle in radians.
numbers x,y
The vector.
Returns:
numbers
The rotated vector
Example:
-- approximate a circle
circle = {}
for i = 1,30 do
    local phi = 2 * math.pi * i / 30
    circle[i*2-1], circle[i*2] = vector.rotate(phi, 0,1)
end

function perpendicular(x,y)^top

Quick rotation by 90°. The same (but faster) as vector.rotate(math.pi/2, x,y).

Parameters:
numbers x,y
The vector.
Returns:
numbers
A vector perpendicular to the input vector
Example:
nx,ny = vector.normalize(vector.perpendicular(bx-ax, by-ay))

function project(x,y, u,v)^top

Project vector onto another vector.

Parameters:
numbers x,y
The vector to project.
numbers u,v
The vector to project onto.
Returns:
numbers
The projected vector.
Example:
vx_p,vy_p = vector.project(vx,vy, ax,ay)

function mirror(x,y, u,v)^top

Mirrors vector on the axis defined by the other vector.

Parameters:
numbers x,y
The vector to mirror.
numbers u,v
The vector defining the axis.
Returns:
numbers
The mirrored vector.
Example:
vx,vy = vector.mirror(vx,vy, surface.x,surface.y)

hump.class^top

Class = require "hump.class"

A small, fast class/prototype implementation with multiple inheritance.

Implements class commons.

Example:

Critter = Class{
    init = function(self, pos, img)
        self.pos = pos
        self.img = img
    end,
    speed = 5
}

function Critter:update(dt, player)
    -- see hump.vector
    local dir = (player.pos - self.pos):normalize_inplace()
    self.pos = self.pos + dir * Critter.speed * dt
end

function Critter:draw()
    love.graphics.draw(self.img, self.pos.x, self.pos.y)
end

Module overview

new()
Declare a new class.
class.init()
Call class constructor.
class:include()
Explicit class inheritance/mixin support.
class:clone()
Clone class/prototype support.
Caveats
Common gotchas.

function new{init = constructor, __includes = parents, ...}^top

Declare a new class.

init() will receive the new object instance as first argument. Any other arguments will also be forwarded (see examples), i.e. init() has the following signature:

function init(self, ...)

If you do not specify a constructor, an empty constructor will be used instead.

The name of the variable that holds the module can be used as a shortcut to new() (see example).

Parameters:
function constructor (optional)
Class constructor. Can be accessed with theclass.init(object, ...)
class or table of classes parents (optional)
Classes to inherit from. Can either be a single class or a table of classes
mixed ... (optional)
Any other fields or methods common to all instances of this class.
Returns:
class
The class.
Examples:
Class = require 'hump.class' -- `Class' is now a shortcut to new()

-- define a class class
Feline = Class{
    init = function(self, size, weight)
        self.size = size
        self.weight = weight
    end;
    -- define a method
    stats = function(self)
        return string.format("size: %.02f, weight: %.02f", self.size, self.weight)
    end;
}

-- create two objects
garfield = Feline(.7, 45)
felix = Feline(.8, 12)

print("Garfield: " .. garfield:stats(), "Felix: " .. felix:stats())
Class = require 'hump.class'

-- same as above, but with 'external' function definitions
Feline = Class{}

function Feline:init(size, weight)
    self.size = size
    self.weight = weight
end

function Feline:stats()
    return string.format("size: %.02f, weight: %.02f", self.size, self.weight)
end

garfield = Feline(.7, 45)
print(Feline, garfield)
Class = require 'hump.class'
A = Class{
    foo = function() print('foo') end
}

B = Class{
    bar = function() print('bar') end
}

-- single inheritance
C = Class{__includes = A}
instance = C()
instance:foo() -- prints 'foo'
instance:bar() -- error: function not defined

-- multiple inheritance
D = Class{__includes = {A,B}}
instance = D()
instance:foo() -- prints 'foo'
instance:bar() -- prints 'bar'
-- class attributes are shared across instances
A = Class{ foo = 'foo' } -- foo is a class attribute/static member

one, two, three = A(), A(), A()
print(one.foo, two.foo, three.foo) --> prints 'foo    foo    foo'

one.foo = 'bar' -- overwrite/specify for instance `one' only
print(one.foo, two.foo, three.foo) --> prints 'bar    foo    foo'

A.foo = 'baz' -- overwrite for all instances without specification
print(one.foo, two.foo, three.foo) --> prints 'bar    baz    baz'

function class.init(object, ...)^top

Calls class constructor of a class on an object.

Derived classes should use this function their constructors to initialize the parent class(es) portions of the object.

Parameters:
Object object
The object. Usually self.
mixed ...
Arguments to pass to the constructor.
Returns:
mixed
Whatever the parent class constructor returns.
Example:
Class = require 'hump.class'

Shape = Class{
    init = function(self, area)
        self.area = area
    end;
    __tostring = function(self)
        return "area = " .. self.area
    end
}

Rectangle = Class{__includes = Shape,
    init = function(self, width, height)
        Shape.init(self, width * height)
        self.width  = width
        self.height = height
    end;
    __tostring = function(self)
        local strs = {
            "width = " .. self.width,
            "height = " .. self.height,
            Shape.__tostring(self)
        }
        return table.concat(strs, ", ")
    end
}

print( Rectangle(2,4) ) -- prints 'width = 2, height = 4, area = 8'

function class:include(other)^top

Inherit functions and variables of another class, but if they are not already defined. This is done by (deeply) copying the functions and variables over to the subclass.

Note: class:include() doesn't actually care if the arguments supplied are hump classes. Just any table will work.

Note 2: You can use Class.include(a, b) to copy any fields from table a to table b (see second example).

Parameters:
tables other
Parent classes/mixins.
Returns:
table
The class.
Examples:
Class = require 'hump.class'

Entity = Class{
    init = function(self)
        GameObjects.register(self)
    end
}

Collidable = {
    dispatch_collision = function(self, other, dx, dy)
        if self.collision_handler[other.type])
            return collision_handler[other.type](self, other, dx, dy)
        end
        return collision_handler["*"](self, other, dx, dy)
    end,

    collision_handler = {["*"] = function() end},
}

Spaceship = Class{
    init = function(self)
        self.type = "Spaceship"
        -- ...
    end
}

-- make Spaceship collidable
Spaceship:include(Collidable)

function Spaceship:collision_handler["Spaceship"](other, dx, dy)
    -- ...
end
-- using Class.include()
Class = require 'hump.class'
a = {
    foo = 'bar',
    bar = {one = 1, two = 2, three = 3},
    baz = function() print('baz') end,
}
b = {
    foo = 'nothing to see here...'
}

Class.include(b, a) -- copy values from a to b
                    -- note that neither a nor b are hump classes!

print(a.foo, b.foo) -- prints 'bar    nothing to see here...'

b.baz() -- prints 'baz'

b.bar.one = 10 -- changes only values in b
print(a.bar.one, b.bar.one) -- prints '1    10'

function class:clone()^top

Create a clone/deep copy of the class.

Note: You can use Class.clone(a) to create a deep copy of any table (see second example).

Parameters:
None
Returns:
table
A deep copy of the class/table.
Examples:
Class = require 'hump.class'

point = Class{ x = 0, y = 0 }

a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10    10'

b = point:clone()
print(b.x, b.y) --> prints '10    10'
-- using Class.clone() to copy tables
Class = require 'hump.class'
a = {
    foo = 'bar',
    bar = {one = 1, two = 2, three = 3},
    baz = function() print('baz') end,
}
b = Class.clone(a)

b.baz() -- prints 'baz'
b.bar.one = 10
print(a.bar.one, b.bar.one) -- prints '1    10'

Caveats ^top

Be careful when using metamethods like __add or __mul: If subclass inherits those methods from a superclass, but does not overwrite them, the result of the operation may be of the type superclass. Consider the following:

Class = require 'hump.class'

A = Class{init = function(self, x) self.x = x end}
function A:__add(other) return A(self.x + other.x) end
function A:show() print("A:", self.x) end

B = Class{init = function(self, x, y) A.init(self, x) self.y = y end}
function B:show() print("B:", self.x, self.y) end
function B:foo() print("foo") end
B:include(A)

one, two = B(1,2), B(3,4)
result = one + two -- result will be of type A, *not* B!
result:show()      -- prints "A:    4"
result:foo()       -- error: method does not exist

Note that while you can define the __index metamethod of the class, this is not a good idea: It will break the class mechanism. To add a custom __index metamethod without breaking the class system, you have to use rawget(). But beware that this won't affect subclasses:

Class = require 'hump.class'

A = Class{}
function A:foo() print('bar') end

function A:__index(key)
    print(key)
    return rawget(A, key)
end

instance = A()
instance:foo() -- prints foo  bar

B = Class{__includes = A}
instance = B()
instance:foo() -- prints only foo

hump.signal^top

Signals = require 'hump.signal'

A simple yet effective implementation of Signals and Slots, also known as Observer pattern: Functions can be dynamically bound to signals. When a signal is emitted, all registered functions will be invoked. Simple as that.

hump.signal makes things more interesing by allowing to emit all signals that match a Lua string pattern.

Example:

-- in AI.lua
signals.register('shoot', function(x,y, dx,dy)
    -- for every critter in the path of the bullet:
    -- try to avoid being hit
    for critter in pairs(critters) do
        if critter:intersectsRay(x,y, dx,dy) then
            critter:setMoveDirection(-dy, dx)
        end
    end
end)

-- in sounds.lua
signals.register('shoot', function()
    Sounds.fire_bullet:play()
end)

-- in main.lua
function love.keypressed(key)
    if key == ' ' then
        local x,y   = player.pos:unpack()
        local dx,dy = player.direction:unpack()
        signals.emit('shoot', x,y, dx,dy)
    end
end

Module overview

new()
Create a new signal registry
register()
Register function with signal.
emit()
Call all functions bound to a signal.
remove()
Remove functions from registry.
clear()
Clears a signal registry.
emit_pattern()
Emits signals matching a pattern.
remove_pattern()
Remove functions from signals matching a pattern.
clear_pattern()
Clears signal registry matching a pattern.

function new()^top

If you don't need multiple independent registries, you can use the global/default registry (see examples).

Creates a new signal registry that is independent of the default registry: It will manage it's own list of signals and does not in any way affect the the global registry. Likewise, the global registry does not affect the instance.

Note: Independent registries use the colon-notation (e.g. instance:emit("foo")), while the global registry uses the dot-notation (e.g. Signal.emit("foo")).

Parameters:
None
Returns:
Registry
A new signal registry.
Example:
player.signals = Signals.new()

function register(s, f)^top

Registers a function f to be called when signal s is emitted.

Parameters:
string s
The signal identifier.
function f
The function to register.
Returns:
function
A function handle to use in remove().
Examples:
Signal.register('level-complete', function() self.fanfare:play() end)
handle = Signal.register('level-load', function(level) level.show_help() end)
menu:register('key-left', select_previous_item)

function emit(s, ...)^top

Calls all functions bound to signal s with the supplied arguments.

Parameters:
string s
The signal identifier.
mixed ... (optional)
Arguments to pass to the bound functions.
Returns:
Nothing
Examples:
function love.keypressed(key)
    -- using a signal instance
    if key == 'left' then menu:emit('key-left') end
end
if level.is_finished() then
    -- adding arguments
    Signal.emit('level-load', level.next_level)
end

function remove(s, ...)^top

Unbinds (removes) functions from signal s.

Parameters:
string s
The signal identifier.
functions ...
Functions to unbind from the signal.
Returns:
Nothing
Example:
Signal.remove('level-load', handle)

function clear(s)^top

Removes all functions from signal s.

Parameters:
string s
The signal identifier.
Returns:
Nothing
Example:
Signal.clear('key-left')

function emit_pattern(p, ...)^top

Emits all signals matching a string pattern.

Parameters:
string p
The signal identifier pattern.
mixed ... (optional)
Arguments to pass to the bound functions.
Returns:
Nothing
Example:
Signal.emit_pattern('^update%-.*', dt)

function remove_pattern(p, ...)^top

Removes functions from all signals matching a string pattern.

Parameters:
string p
The signal identifier pattern.
functions ...
Functions to unbind from the signals.
Returns:
Nothing
Example:
Signal.remove_pattern('key%-.*', play_click_sound)

function clear_pattern(p)^top

Removes all functions from all signals matching a string pattern.

Parameters:
string p
The signal identifier pattern.
Returns:
Nothing
Examples:
Signal.clear_pattern('sound%-.*')
player.signals:clear_pattern('.*') -- clear all signals

hump.camera^top

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()
    cam = Camera(player.pos.x, player.pos.y)
end

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

Module overview

new()
Create a new camera.
camera:move()
Move camera.
camera:lookAt()
Move camera to position.
camera:pos()
Get camera position.
camera:rotate()
Rotate camera.
camera:rotateTo()
Set camera rotation.
camera:zoom()
Change zoom.
camera:zoomTo()
Set zoom.
camera:attach()
Attach camera.
camera:detach()
Detach camera.
camera:draw()
Attach, draw, then detach.
camera:worldCoords()
Convert point to world coordinates.
camera:cameraCoords()
Convert point to camera coordinates.
camera:mousepos()
Get mouse position in world coordinates.

function new(x,y, zoom, rot)^top

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().

Parameters:
numbers x,y (optional)
Point for the camera to look at.
number zoom (optional)
Camera zoom.
number rot (optional)
Camera rotation in radians.
Returns:
camera
A new camera.
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)

function camera:move(dx,dy)^top

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

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

Parameters:
numbers dx,dy
Direction to move the camera.
Returns:
camera
The camera.
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

function camera:lookAt(x,y)^top

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(x,y).

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

Parameters:
numbers x,y
Position to look at.
Returns:
camera
The camera.
Examples:
function love.update(dt)
    camera:lookAt(player.pos:unpack())
end
function love.update(dt)
    camera:lookAt(player.pos:unpack()):rotation(player.rot)
end

function camera:pos()^top

Returns camera.x, camera.y.

Parameters:
None
Returns:
numbers
Camera position.
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

function camera:rotate(angle)^top

Rotate the camera by some angle. To set the angle use camera.rot = new_angle.

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

Parameters:
number angle
Rotation angle in radians
Returns:
camera
The camera.
Examples:
function love.update(dt)
    camera:rotate(dt)
end
function love.update(dt)
    camera:rotate(dt):move(dt,dt)
end

function camera:rotateTo(angle)^top

Set rotation: camera.rot = angle.

Parameters:
number angle
Rotation angle in radians
Returns:
number
The camera.
Example:
camera:rotateTo(math.pi/2)

function camera:zoom(mul)^top

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

Parameters:
number mul
Zoom change. Should be > 0.
Returns:
number
The camera.
Examples:
camera:zoom(2)   -- make everything twice as big
camera:zoom(0.5) -- ... and back to normal
camera:zoom(-1)  -- flip everything

function camera:zoomTo(zoom)^top

Set zoom: camera.scale = zoom.

Parameters:
number zoom
New zoom.
Returns:
number
The camera.
Example:
camera:zoomTo(1)

function camera:attach()^top

Start looking through the camera.

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

Parameters:
None
Returns:
Nothing
Example:
function love.draw()
    camera:attach()
    draw_world()
    cam:detach()

    draw_hud()
end

function camera:detach()^top

Stop looking through the camera.

Parameters:
None
Returns:
Nothing
Example:
function love.draw()
    camera:attach()
    draw_world()
    cam:detach()

    draw_hud()
end

function camera:draw(func)^top

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

cam:attach()
func()
cam:detach()
Parameters:
function func
Drawing function to be wrapped.
Returns:
Nothing
Example:
function love.draw()
    camera:draw(draw_world)
    draw_hud()
end

function camera:worldCoords(x, y)^top

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(x,y) and camera:cameraCoords(x,y) transform a point between these two coordinate systems.

Parameters:
numbers x, y
Point to transform.
Returns:
numbers
Transformed point.
Example:
x,y = camera:worldCoords(love.mouse.getPosition())
selectedUnit:plotPath(x,y)

function camera:cameraCoords(x, y)^top

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(x,y) and camera:cameraCoords(x,y) transform a point between these two coordinate systems.

Parameters:
numbers x, y
Point to transform.
Returns:
numbers
Transformed point.
Example:
x,y = cam:cameraCoords(player.pos)
love.graphics.line(x, y, love.mouse.getPosition())

function camera:mousepos()^top

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

Parameters:
None
Returns:
numbers
Mouse position in world coordinates.
Example:
x,y = camera:mousepos()
selectedUnit:plotPath(x,y)

License^top

Yay, free software

Copyright (c) 2010-2012 Matthias Richter

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

Except as contained in this notice, the name(s) of the above copyright holders shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Download^top

You can view and download the individual modules on github: vrld/hump You may also download the whole packed sourcecode either in zip or tar formats.

Using Git, you can clone the project by running:

git clone git://github.com/vrld/hump

Once done, tou can check for updates by running

git pull