hump/README.md

2748 lines
58 KiB
Markdown
Raw Normal View History

2012-10-19 13:37:41 +00:00
# HUMP
## Introduction
2012-11-01 17:41:30 +00:00
Helper Utilities for a Multitude of Problems is a set of lightweight helpers
for the awesome LÖVE Engine.
2012-10-19 13:37:41 +00:00
2012-11-01 17:41:30 +00:00
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.
2012-10-19 13:37:41 +00:00
## Module hump.gamestate [A gamestate system.]
Gamestate = require "hump.gamestate"
2013-02-20 15:53:17 +00:00
A gamestate encapsulates independent data an behaviour in a single table.
2012-10-19 13:37:41 +00:00
A typical game could consist of a menu-state, a level-state and a game-over-state.
2012-11-01 17:41:30 +00:00
#### Example:
2013-02-20 15:53:17 +00:00
local menu = {} -- previously: Gamestate.new()
local game = {}
2012-11-01 17:41:30 +00:00
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
2012-10-19 13:37:41 +00:00
### Callbacks [Gamestate Callbacks.]
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()`](#hump.gamestateswitch).
=`enter(previous, ...)`=
Called when entering the state. See [`switch()`](#hump.gamestateswitch).
=`leave()`=
Called when leaving a state. See [`switch()`](#hump.gamestateswitch).
=`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()`](#hump.gamestateregisterEvents), 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:
2013-02-20 15:53:17 +00:00
menu = {} -- previously: Gamestate.new()
2012-10-19 13:37:41 +00:00
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() [Create a new gamestate.]
2013-02-20 15:53:17 +00:00
**Deprecated: Use the table constructor instead (see example)**
Declare a new gamestate (just an empty table). A gamestate can define several
callbacks.
2012-10-19 13:37:41 +00:00
#### Returns:
=Gamestate=
2013-02-20 15:53:17 +00:00
An empty table.
2012-10-19 13:37:41 +00:00
#### Example:
2013-02-20 15:53:17 +00:00
menu = {}
-- deprecated method:
2012-10-19 13:37:41 +00:00
menu = Gamestate.new()
### function switch(to, ...) [Switch to gamestate.]
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)
2013-08-21 14:29:52 +00:00
### function current() [Get current gamestate.]
Returns the currently activated gamestate.
#### Returns:
=table=
The active gamestate.
#### Example:
function love.keypressed(key)
if Gamestate.current() ~= menu and key == 'p' then
Gamestate.push(pause)
end
end
### function push(to, ...) [Push state on top of the stack.]
Pushes the `to` on top of the state stack, i.e. makes it the active state.
Semantics are the same as `switch(to, ...)`, except that `leave()` is *not*
called on the previously active state.
Useful for pause screens, menus, etc.
#### Parameters:
=Gamestate to=
Target gamestate.
=mixed ...=
Additional arguments to pass to `to:enter(current, ...)`.
#### Returns:
=mixed=
The results of `to:enter(current, ...)`
#### Example:
-- pause gamestate
Pause = Gamestate.new()
function Pause:enter(from)
self.from = from -- record previous state
end
function Pause:draw()
local W, H = love.graphics.getWidth(), love.graphics.getHeight()
-- draw previous screen
self.from:draw()
-- overlay with pause message
love.graphics.setColor(0,0,0, 100)
love.graphics.rectangle('fill', 0,0, W,H)
love.graphics.setColor(255,255,255)
love.graphics.printf('PAUSE', 0, H/2, W, 'center')
end
-- [...]
function love.keypressed(key)
if Gamestate.current() ~= menu and key == 'p' then
Gamestate.push(pause)
end
end
### function pop() [Pops state from the stack.]
Calls `leave()` on the current state and then removes it from the stack, making
the state below the current state. Does *not* call `enter()` on the activated
state.
#### Returns:
=mixed=
The results of `state:leave()`
#### Example:
-- extending the example of Gamestate.push() above
function Pause:keypressed(key)
if key == 'p' then
Gamestate.pop() -- return to previous state
end
end
2012-10-19 13:37:41 +00:00
### function <callback>(...) [Call function on active gamestate.]
Calls a function on the current gamestate. Can be any function, but is intended to
be one of the [callbacks](#hump.gamestateCallbacks). Mostly useful when not using
[`registerEvents()`](#hump.gamestateregisterEvents).
#### 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) [Automatically do all of the above when needed.]
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.
#### Example:
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
#### Example:
function love.load()
-- only register draw, update and quit
Gamestate.registerEvents{'draw', 'update', 'quit'}
Gamestate.switch(menu)
end
## Module hump.timer [Delayed and time-limited function calls.]
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.
2013-08-21 15:23:04 +00:00
In addition to that, `hump.timer` offers various
[tweening](http://en.wikipedia.org/wiki/Inbetweening) functions that make it
easier to produce [juicy games](http://www.youtube.com/watch?v=Fy0aCDmgnxg).
2012-11-01 17:41:30 +00:00
#### 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
2012-10-19 13:37:41 +00:00
### function new() [Create new timer instance.]
**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)`).
#### Returns:
=Timer=
A timer instance.
#### Example:
menuTimer = Timer.new()
### function add(delay, func) [Schedule a function.]
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.
#### Example:
-- grant the player 5 seconds of immortality
player.isInvincible = true
Timer.add(5, function() player.isInvincible = false end)
#### Example:
-- print "foo" every second. See also addPeriodic()
Timer.add(1, function(func) print("foo") Timer.add(1, func) end)
#### Example:
--Using a timer instance:
menuTimer:add(1, finishAnimation)
### function addPeriodic(delay, func) [Add a periodic function.]
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)`](#hump.timercancel) or [`clear()`](#hump.timerclear) 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()`](#hump.timercancel).
#### Example:
-- toggle light on and off every second
Timer.addPeriodic(1, function() lamp:toggleLight() end)
#### Example:
-- launch 5 fighters in quick succession (using a timer instance)
mothership_timer:addPeriodic(0.3, function() self:launchFighter() end, 5)
#### Example:
-- 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) [Run a function for the next few seconds.]
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.
#### Example:
-- play an animation for 5 seconds
Timer.do_for(5, function(dt) animation:update(dt) end)
#### Example:
-- 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)
#### Example:
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) [Cancel a scheduled function.]
Prevent a timer from being executed in the future.
#### Parameters:
=table handle=
The function to be canceled.
#### Example:
function tick()
print('tick... tock...')
end
handle = Timer.addPeriodic(1, tick)
-- later
Timer.cancel(handle) -- NOT: Timer.cancel(tick)
2013-08-21 15:23:04 +00:00
#### Example:
-- using a timer instance
function tick()
print('tick... tock...')
end
handle = menuTimer:addPeriodic(1, tick)
-- later
menuTimer:cancel(handle)
2012-10-19 13:37:41 +00:00
### function clear() [Remove all timed and periodic functions.]
Remove all timed and periodic functions. Functions that have not yet been
executed will discarded.
#### Example:
Timer.clear()
#### Example:
menu_timer:clear()
### function update(dt) [Update scheduled functions.]
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()`.
#### Example:
function love.update(dt)
do_stuff()
Timer.update(dt)
end
#### Example:
-- using hump.gamestate and a timer instance
function menuState:update(dt)
self.timer:update(dt)
end
2013-08-21 15:23:04 +00:00
### function tween(duration, subject, target, method, after, ...) [Add a tween.]
2013-08-22 15:16:49 +00:00
[Tweening](http://en.wikipedia.org/wiki/Inbetweening) (short for in-betweening)
is the process that happens between two defined states. For example, a tween
can be used to gradually fade out a graphic or move a text message to the
center of the screen. For more information why tweening should be important to
you, check out this great talk on [juicy
games](http://www.youtube.com/watch?v=Fy0aCDmgnxg).
`hump.timer` offers two interfaces for tweening: the low-level
[`timer.to_for()`](#hump.timerdofor) and the higher level interface
[`timer.tween()`](#hump.timertween).
To see which tweening methods hump offers, [see
below](#hump.timerTweening%20methods).
2013-08-21 15:23:04 +00:00
#### Parameters:
=number duration=
Duration of the tween.
=table subject=
Object to be tweened.
=table target=
Target values.
=string method (optional)=
2013-08-22 15:16:49 +00:00
Tweening method, defaults to 'linear' ([see here](#hump.timerTweening%20methods)).
2013-08-21 15:23:04 +00:00
=function after (optiona)=
Function to execute after the tween has finished.
=mixed ...=
Additional arguments to the *tweening* function.
#### Returns:
=table=
A timer handle.
#### Example:
function love.load()
color = {0, 0, 0}
Timer.tween(10, color, {255, 255, 255}, 'in-out-quad')
end
function love.update(dt)
Timer.update(dt)
end
function love.draw()
love.graphics.setBackgroundColor(color)
end
#### Example:
function love.load()
2013-08-22 15:16:49 +00:00
circle = {rad = 10, pos = {x = 400, y = 300}}
-- multiple tweens can work on the same subject
-- and nested values can be tweened, too
Timer.tween(5, circle, {rad = 50}, 'in-out-quad')
Timer.tween(2, circle, {pos = {y = 550}}, 'out-bounce')
end
function love.update(dt)
Timer.update(dt)
end
function love.draw()
love.graphics.circle('fill', circle.pos.x, circle.pos.y, circle.rad)
end
#### Example:
function love.load()
-- repeated tweening
2013-08-21 15:23:04 +00:00
circle = {rad = 10, x = 100, y = 100}
local grow, shrink, move_down, move_up
grow = function()
Timer.tween(1, circle, {rad = 50}, 'in-out-quad', shrink)
end
shrink = function()
Timer.tween(2, circle, {rad = 10}, 'in-out-quad', grow)
end
move_down = function()
Timer.tween(3, circle, {x = 700, y = 500}, 'bounce', move_up)
end
move_up = function()
Timer.tween(5, circle, {x = 200, y = 200}, 'out-elastic', move_down)
end
grow()
move_down()
end
function love.update(dt)
Timer.update(dt)
end
function love.draw()
love.graphics.circle('fill', circle.x, circle.y, circle.rad)
end
2013-08-22 15:16:49 +00:00
### Tweening methods [Predefined tweening methods.]
At the core of tweening lie interpolation methods. These methods define how the
output should look depending on how much time has passed. For example, consider
the following tween:
-- now: player.x = 0, player.y = 0
Timer.tween(2, player, {x = 2})
Timer.tween(4, player, {y = 8})
At the beginning of the tweens (no time passed), the interpolation method would
place the player at `x = 0, y = 0`. After one second, the player should be at
`x = 1, y = 2`, and after two seconds the output is `x = 2, y = 4`.
The actual duration of and time since starting the tween is not important, only
the fraction of the two. Similarly, the starting value and output are not
important to the interpolation method, since it can be calculated from the
start and end point. Thus an interpolation method can be fully characterized by
a function that takes a number between 0 and 1 and returns a number that
defines the output (usually also between 0 and 1). The interpolation function
must hold that the output is 0 for input 0 and 1 for input 1.
`hump` predefines several commonly used interpolation methods, which are
generalized versions of [Robert Penner's easing
functions](http://www.robertpenner.com/easing/). Those are:
`'linear'`, `'quad'`, `'cubic'`, `'quart'`, `'quint'`, `'sine'`,
`'expo'`, `'circ'`, `'back'`, `'bounce'`, and `'elastic'`.
Here are their respective graphs:
![Graph of interpolation functions](interpolators.png)
All methods can be 'inverted' by prefixing the keyword `out-`, for example
`'out-quad'`, `'out-sine'`, `'out-bounce'`, etc:
![Graph of inverted interpolation functions](inv-interpolators.png)
The `in-out-` and `out-in-` prefix chain the interpolators:
![Graph of chained interpolation functions](in-out-interpolators.png)
Of course these graphs cannot show how the different methods compare, so here's
an example script that highlights the different tweens:
Timer = require 'hump.timer'
function love.load()
methods = {'linear', 'quad', 'cubic', 'quart', 'quint', 'sine', 'expo', 'circ', 'back', 'bounce', 'elastic'}
method = methods[#methods]
next_method = {} -- next_method.linear = 'quad', etc
for i = 1,#methods do
next_method[methods[i]] = methods[(i%#methods)+1]
end
target = {x = 700, brightness = 200, radius = 50}
local function reset()
circle = {
{x = 100, y = 100, brightness = 100, radius = 20},
{x = 100, y = 233, brightness = 100, radius = 20},
{x = 100, y = 367, brightness = 100, radius = 20},
{x = 100, y = 500, brightness = 100, radius = 20},
}
method = next_method[method]
Timer.tween(3, circle[1], target, 'in-'..method)
Timer.tween(3, circle[2], target, 'out-'..method)
Timer.tween(3, circle[3], target, 'in-out-'..method)
Timer.tween(3, circle[4], target, 'out-in-'..method,
function() Timer.add(.5, reset) end)
end
reset()
end
function love.update(dt)
Timer.update(dt)
end
function love.draw()
love.graphics.setColor(255,255,255)
love.graphics.print('in-' .. method, 50, 100-50)
love.graphics.print('out-' .. method, 50, 233-50)
love.graphics.print('in-out-' .. method, 50, 367-50)
love.graphics.print('out-in-' .. method, 50, 500-50)
for i = 1,#circle do
love.graphics.setColor(255,255,255, circle[i].brightness)
love.graphics.circle('fill', circle[i].x, circle[i].y,
circle[i].radius)
end
end
### Custom interpolators [Advanced: Adding custom tweening methods.]
**This is a stub**
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
You can add custom interpolation methods by adding them to the `tween` table:
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
Timer.tween.sqrt = function(t) return math.sqrt(t) end
-- or just Timer.tween.sqrt = math.sqrt
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
Access the your method like you would the predefined ones. You can even use the
modyfing prefixes:
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
Timer.tween(5, 'in-out-sqrt', circle, {radius = 50})
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
You can also invert and chain functions:
2013-08-21 15:23:04 +00:00
2013-08-22 15:16:49 +00:00
outsqrt = Timer.tween.out(math.sqrt)
inoutsqrt = Timer.tween.chain(math.sqrt, outsqrt)
2013-08-21 15:23:04 +00:00
2012-10-19 13:37:41 +00:00
## Module hump.vector [2D vector math.]
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.
2012-11-01 17:41:30 +00:00
#### 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
2012-10-19 13:37:41 +00:00
### Operators [Arithmetics and relations.]
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) [Create a new vector.]
Create a new vector.
#### Parameters:
=numbers x,y=
Coordinates.
#### Returns:
=vector=
The vector.
#### Example:
a = vector.new(10,10)
2012-10-19 13:37:41 +00:00
#### Example:
-- as a shortcut, you can call the module like a function:
vector = require "hump.vector"
a = vector(10,10)
2012-10-19 13:37:41 +00:00
### function isvector(v) [Test if value is a vector.]
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() [Copy a vector.]
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)'
#### Returns:
=vector=
Copy of the vector
#### Example:
copy = original:clone()
### function vector:unpack() [Extract coordinates.]
Extract coordinates.
#### Returns:
=numbers=
The coordinates
#### Example:
x,y = pos:unpack()
#### Example:
love.graphics.draw(self.image, self.pos:unpack())
### function vector:permul(other) [Per element multiplication.]
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() [Get length.]
Get length of a vector, i.e. `math.sqrt(vec.x * vec.x + vec.y * vec.y)`.
#### Returns:
=number=
Length of the vector.
#### Example:
distance = (a - b):len()
### function vector:len2() [Get squared length.]
Get squared length of a vector, i.e. `vec.x * vec.x + vec.y * vec.y`.
#### 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) [Distance to other vector.]
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:dist2(other) [Squared distance to other vector.]
Get squared distance of two vectors. The same as `(a - b):len2()`.
#### Parameters:
=vector other=
Other vector to measure the distance to.
#### Returns:
=number=
The squared distance of the vectors.
#### Example:
-- get closest vertex to a given vector
-- slightly faster than the example using len2()
closest, dsq = vertices[1], pos:dist2(vertices[1])
for i = 2,#vertices do
local temp = pos:dist2(vertices[i])
if temp < dsq then
closest, dsq = vertices[i], temp
end
end
2012-10-19 13:37:41 +00:00
### function vector:normalized() [Get normalized vector.]
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.
#### Returns:
=vector=
Vector with same direction as the input vector, but length 1.
#### Example:
direction = velocity:normalized()
### function vector:normalize_inplace() [Normalize vector in-place.]
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()`](#hump.vectornormalized).**
#### Returns:
=vector=
Itself - the normalized vector
#### Example:
normal = (b - a):perpendicular():normalize_inplace()
### function vector:rotated(angle) [Get rotated vector.]
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](vector-rotated.png)
### function vector:rotate_inplace(angle) [Rotate vector in-place.]
Rotate a vector in-place. Great to use on intermediate results.
**This modifies the vector. If in doubt, use
[`vector:rotate()`](#hump.vectorrotate).**
#### Parameters:
=number angle=
Rotation angle in radians.
#### Returns:
=vector=
Itself - the rotated vector
#### Example:
-- ongoing rotation
spawner.direction:rotate_inplace(dt)
### function vector:perpendicular() [Get perpendicular vector.]
Quick rotation by 90°. Creates a new vector. The same (but faster) as
`vec:rotate(math.pi/2)`.
#### Returns:
=vector=
A vector perpendicular to the input vector
#### Example:
normal = (b - a):perpendicular():normalize_inplace()
#### Sketch:
![Perpendiculat vector sketch](vector-perpendicular.png)
### function vector:projectOn(v) [Get projection onto another vector.]
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](vector-projectOn.png)
### function vector:mirrorOn(v) [Mirrors vector on other vector]
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](vector-mirrorOn.png)
### function vector:cross(other) [Cross product of two vectors.]
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)
2013-07-30 10:28:39 +00:00
### function vector:angleTo(other) [Measure angle between two vectors.]
Measures the angle between two vectors. If `other` is omitted it defaults
to the vector `(0,0)`, i.e. the function returns the angle to the coordinate
system.
#### Parameters:
=vector other (optional)=
Vector to measure the angle to.
#### Returns:
=number=
Angle in radians.
#### Example:
lean = self.upvector:angleTo(vector(0,1))
if lean > .1 then self:fallOver() end
2012-10-19 13:37:41 +00:00
## Module hump.vector-light [Lightweight 2D vector math.]
vector = require "hump.vector-light"
An table-free version of [`hump.vector`](#hump.vector). Instead of a vector
type, `hump.vector-light` provides functions that operate on numbers.
**Note:** Using this module instead of [`hump.vector`](#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`](#hump.vector).
2012-11-01 17:41:30 +00:00
#### 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
2012-10-19 13:37:41 +00:00
### function str(x,y) [String representation.]
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) [Product of a vector and a scalar.]
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))
2012-10-20 11:12:55 +00:00
### function div(s, x,y) [Product of a vector and the inverse of a scalar.]
2012-10-19 13:37:41 +00:00
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) [Sum of two vectors.]
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) [Difference of two vectors.]
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) [Per element multiplication.]
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) [Dot product.]
Computes the [dot product](http://en.wikipedia.org/wiki/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) [Cross product.]
Computes the [cross product](http://en.wikipedia.org/wiki/Cross_product) of two
vectors, `x1*y2 - y1*x2`.
#### Parameters:
=numbers x1,y1=
First vector.
=numbers x2,y2=
Second vector.
#### Returns:
=number=
`x1*y2 - y1*x2`
#### Example:
parallelogram_area = vector.cross(ax,ay, bx,by)
Alias to [`vector.cross(x1,y1, x2,y2)`].
#### Parameters:
=numbers x1,y1=
First vector.
=numbers x2,y2=
Second vector.
#### Returns:
=number=
`x1*y2 - y1*x2`
#### Example:
parallelogram_area = vector.det(ax,ay, bx,by)
### function eq(x1,y1, x2,y2) [Equality.]
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) [Partial lexical order.]
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) [Strict lexical order.]
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) [Get length.]
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) [Get squared length.]
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) [Distance of two points.]
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 dist2(x1,y1, x2,y2) [Squared distance of two points.]
Get squared distance of two points. The same as `vector.len2(x1-x2, y1-y2)`.
#### Parameters:
=numbers x1,y1=
First vector.
=numbers x2,y2=
Second vector.
#### Returns:
=number=
2013-07-30 10:28:39 +00:00
The squared distance of two points.
#### Example:
-- get closest vertex to a given vector
closest, dsq = vertices[1], vector.dist2(px,py, vertices[1].x,vertices[1].y)
for i = 2,#vertices do
local temp = vector.dist2(px,py, vertices[i].x,vertices[i].y)
if temp < dsq then
closest, dsq = vertices[i], temp
end
end
2012-10-19 13:37:41 +00:00
### function normalize(x,y) [Normalize vector.]
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) [Rotate vector.]
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) [Get perpendicular vector.]
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) [Project vector onto another vector.]
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) [Mirror vector on other vector.]
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)
2013-07-30 10:28:39 +00:00
### function angleTo(ox,y, u,v) [Measure angle between two vectors.]
Measures the angle between two vectors. `u` and `v` default to `0` if omitted,
i.e. the function returns the angle to the coordinate system.
#### Parameters:
=numbers x,y=
Vector to measure the angle.
=numbers u,v (optional)=
Reference vector.
#### Returns:
=number=
Angle in radians.
#### Example:
lean = vector.angleTo(self.upx, self.upy, 0,1)
if lean > .1 then self:fallOver() end
2013-02-20 15:53:17 +00:00
## Module hump.class [Object oriented programming for Lua.]
2012-10-19 13:37:41 +00:00
Class = require "hump.class"
2013-02-20 15:53:17 +00:00
A small, fast class/prototype implementation with multiple inheritance.
2012-10-19 13:37:41 +00:00
Implements [class commons](https://github.com/bartbes/Class-Commons).
2012-11-01 17:41:30 +00:00
#### Example:
2013-02-25 17:45:33 +00:00
Critter = Class{
init = function(self, pos, img)
self.pos = pos
self.img = img
end,
speed = 5
}
2012-11-01 17:41:30 +00:00
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
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
### function new{init = constructor, __includes = parents, ...} [Declare a new class.]
2012-10-19 13:37:41 +00:00
Declare a new class.
2013-02-20 15:53:17 +00:00
`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, ...)
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
If you do not specify a constructor, an empty constructor will be used instead.
2012-10-19 13:37:41 +00:00
The name of the variable that holds the module can be used as a shortcut to
`new()` (see example).
#### Parameters:
=function constructor (optional)=
2013-02-20 15:53:17 +00:00
Class constructor. Can be accessed with theclass.init(object, ...)
=class or table of classes parents (optional)=
2012-10-19 13:37:41 +00:00
Classes to inherit from. Can either be a single class or a table of classes
2013-02-20 15:53:17 +00:00
=mixed ... (optional)=
Any other fields or methods common to all instances of this class.
2012-10-19 13:37:41 +00:00
#### Returns:
=class=
2013-02-20 15:53:17 +00:00
The class.
2012-10-19 13:37:41 +00:00
#### Example:
Class = require 'hump.class' -- `Class' is now a shortcut to new()
2013-02-20 15:53:17 +00:00
-- 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;
}
2012-10-19 13:37:41 +00:00
-- create two objects
garfield = Feline(.7, 45)
felix = Feline(.8, 12)
print("Garfield: " .. garfield:stats(), "Felix: " .. felix:stats())
#### Example:
Class = require 'hump.class'
2013-02-20 15:53:17 +00:00
-- same as above, but with 'external' function definitions
Feline = Class{}
function Feline:init(size, weight)
2012-10-19 13:37:41 +00:00
self.size = size
self.weight = weight
2013-02-20 15:53:17 +00:00
end
function Feline:stats()
return string.format("size: %.02f, weight: %.02f", self.size, self.weight)
end
2012-10-19 13:37:41 +00:00
garfield = Feline(.7, 45)
2013-02-20 15:53:17 +00:00
print(Feline, garfield)
2012-10-19 13:37:41 +00:00
#### Example:
Class = require 'hump.class'
2013-02-20 15:53:17 +00:00
A = Class{
foo = function() print('foo') end
}
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
B = Class{
bar = function() print('bar') end
}
2012-10-19 13:37:41 +00:00
-- single inheritance
2013-02-20 15:53:17 +00:00
C = Class{__includes = A}
2012-10-19 13:37:41 +00:00
instance = C()
instance:foo() -- prints 'foo'
2013-02-20 15:53:17 +00:00
instance:bar() -- error: function not defined
2012-10-19 13:37:41 +00:00
-- multiple inheritance
2013-02-20 15:53:17 +00:00
D = Class{__includes = {A,B}}
2012-10-19 13:37:41 +00:00
instance = D()
instance:foo() -- prints 'foo'
instance:bar() -- prints 'bar'
2013-02-20 15:53:17 +00:00
#### Example:
-- 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'
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
### function class.init(object, ...) [Call class constructor.]
2012-10-19 13:37:41 +00:00
Calls class constructor of a class on an object.
2013-02-20 15:53:17 +00:00
Derived classes should use this function their constructors to initialize the
parent class(es) portions of the object.
2012-10-19 13:37:41 +00:00
#### 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'
2013-02-20 15:53:17 +00:00
Shape = Class{
init = function(self, area)
self.area = area
end;
__tostring = function(self)
return "area = " .. self.area
end
}
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
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
}
2012-10-19 13:37:41 +00:00
print( Rectangle(2,4) ) -- prints 'width = 2, height = 4, area = 8'
2013-02-20 15:53:17 +00:00
### function class:include(other) [Explicit class inheritance/mixin support.]
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
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.
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
**Note:** `class:include()` doesn't actually care if the arguments supplied are
hump classes. Just any table will work.
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
**Note 2:** You can use `Class.include(a, b)` to copy any fields from table `a`
to table `b` (see second example).
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
#### Parameters:
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
=tables other=
Parent classes/mixins.
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
#### Returns:
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
=table=
The class.
2012-10-19 13:37:41 +00:00
#### Example:
Class = require 'hump.class'
2013-02-20 15:53:17 +00:00
Entity = Class{
init = function(self)
GameObjects.register(self)
end
}
2012-10-19 13:37:41 +00:00
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},
}
2013-02-20 15:53:17 +00:00
Spaceship = Class{
init = function(self)
self.type = "Spaceship"
-- ...
end
}
2012-10-19 13:37:41 +00:00
-- make Spaceship collidable
2013-02-20 15:53:17 +00:00
Spaceship:include(Collidable)
2012-10-19 13:37:41 +00:00
Spaceship.collision_handler["Spaceship"] = function(self, other, dx, dy)
2012-10-19 13:37:41 +00:00
-- ...
end
2013-02-20 15:53:17 +00:00
#### Example:
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
-- 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!
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
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() [Clone class/prototype support.]
Create a clone/deep copy of the class.
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
**Note:** You can use `Class.clone(a)` to create a deep copy of any table
(see second example).
2012-10-19 13:37:41 +00:00
#### Returns:
2013-02-20 15:53:17 +00:00
=table=
A deep copy of the class/table.
2012-10-19 13:37:41 +00:00
#### Example:
Class = require 'hump.class'
2013-02-20 15:53:17 +00:00
point = Class{ x = 0, y = 0 }
a = point:clone()
a.x, a.y = 10, 10
print(a.x, a.y) --> prints '10 10'
2012-10-19 13:37:41 +00:00
2013-02-20 15:53:17 +00:00
b = point:clone()
print(b.x, b.y) --> prints '10 10'
#### Example:
-- 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'
2012-10-19 13:37:41 +00:00
### Caveats [Common gotchas.]
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'
2013-02-20 15:53:17 +00:00
A = Class{init = function(self, x) self.x = x end}
2012-10-19 13:37:41 +00:00
function A:__add(other) return A(self.x + other.x) end
function A:show() print("A:", self.x) end
2013-02-20 15:53:17 +00:00
B = Class{init = function(self, x, y) A.init(self, x) self.y = y end}
2012-10-19 13:37:41 +00:00
function B:show() print("B:", self.x, self.y) end
function B:foo() print("foo") end
2013-02-20 15:53:17 +00:00
B:include(A)
2012-10-19 13:37:41 +00:00
one, two = B(1,2), B(3,4)
2013-02-20 15:53:17 +00:00
result = one + two -- result will be of type A, *not* B!
result:show() -- prints "A: 4"
result:foo() -- error: method does not exist
2012-10-19 13:37:41 +00:00
Note that while you can define the `__index` metamethod of the class, this is
2013-02-20 15:53:17 +00:00
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:
2012-10-19 13:37:41 +00:00
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
2013-02-20 15:53:17 +00:00
B = Class{__includes = A}
2012-10-19 13:37:41 +00:00
instance = B()
instance:foo() -- prints only foo
## Module hump.signal [Simple Signal/Slot (aka. Observer) implementation.]
2012-11-01 17:41:30 +00:00
Signals = require 'hump.signal'
2012-10-19 13:37:41 +00:00
A simple yet effective implementation of [Signals and
Slots](http://en.wikipedia.org/wiki/Signals_and_slots), also known as [Observer
pattern](http://en.wikipedia.org/wiki/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](http://www.lua.org/manual/5.1/manual.html#5.4.1).
2012-11-01 17:41:30 +00:00
#### 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
2012-10-19 13:37:41 +00:00
### function new() [Create a new signal registry]
**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")`).
#### Returns:
=Registry=
A new signal registry.
#### Example:
player.signals = Signals.new()
### function register(s, f) [Register function with signal.]
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()`](#hump.signalremove).
#### Example:
Signal.register('level-complete', function() self.fanfare:play() end)
#### Example:
handle = Signal.register('level-load', function(level) level.show_help() end)
#### Example:
menu:register('key-left', select_previous_item)
### function emit(s, ...) [Call all functions bound to a signal.]
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.
#### Example:
function love.keypressed(key)
-- using a signal instance
if key == 'left' then menu:emit('key-left') end
end
#### Example
if level.is_finished() then
-- adding arguments
Signal.emit('level-load', level.next_level)
end
### function remove(s, ...) [Remove functions from registry. ]
Unbinds (removes) functions from signal `s`.
#### Parameters:
=string s=
The signal identifier.
=functions ...=
Functions to unbind from the signal.
#### Example:
Signal.remove('level-load', handle)
### function clear(s) [Clears a signal registry.]
Removes all functions from signal `s`.
#### Parameters:
=string s=
The signal identifier.
#### Example:
Signal.clear('key-left')
### function emit_pattern(p, ...) [Emits signals matching a pattern.]
Emits all signals matching a [string pattern](http://www.lua.org/manual/5.1/manual.html#5.4.1).
#### Parameters:
=string p=
The signal identifier pattern.
=mixed ... (optional)=
Arguments to pass to the bound functions.
#### Example:
Signal.emit_pattern('^update%-.*', dt)
### function remove_pattern(p, ...) [Remove functions from signals matching a pattern.]
Removes functions from all signals matching a [string pattern](http://www.lua.org/manual/5.1/manual.html#5.4.1).
#### Parameters:
=string p=
The signal identifier pattern.
=functions ...=
Functions to unbind from the signals.
#### Example:
Signal.remove_pattern('key%-.*', play_click_sound)
### function clear_pattern(p) [Clears signal registry matching a pattern.]
Removes *all* functions from all signals matching a [string pattern](http://www.lua.org/manual/5.1/manual.html#5.4.1).
#### Parameters:
=string p=
The signal identifier pattern.
#### Example:
Signal.clear_pattern('sound%-.*')
#### Example:
player.signals:clear_pattern('.*') -- clear all signals
## Module hump.camera [A camera for LÖVE.]
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.
2012-11-01 17:41:30 +00:00
#### 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
2012-10-19 13:37:41 +00:00
### function new(x,y, zoom, rot) [Create a new camera.]
Creates a new camera. You can access the camera position using `camera.x,
2012-11-01 17:57:35 +00:00
camera.y`, the zoom using `camera.scale` and the rotation using `camera.rot`.
2012-10-19 13:37:41 +00:00
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) [Move camera.]
Move the camera *by* some vector. To set the position, use
[`camera:lookAt(x,y)`](#hump.cameralookAt).
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.
#### Example:
function love.update(dt)
camera:move(dt * 5, dt * 6)
end
#### Example:
function love.update(dt)
camera:move(dt * 5, dt * 6):rotate(dt)
end
### function camera:lookAt(x,y) [Move camera to position.]
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)`](#hump.cameramove).
This function is shortcut to `camera.x,camera.y = x, y`.
#### Parameters:
=numbers x,y=
Position to look at.
#### Returns:
=camera=
The camera.
#### Example:
function love.update(dt)
camera:lookAt(player.pos:unpack())
end
#### Example:
function love.update(dt)
camera:lookAt(player.pos:unpack()):rotation(player.rot)
end
### function camera:pos() [Get camera position.]
Returns `camera.x, camera.y`.
#### 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
2012-11-01 17:57:35 +00:00
### function camera:rotate(angle) [Rotate camera.]
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.
#### Example:
function love.update(dt)
camera:rotate(dt)
end
#### Example:
function love.update(dt)
camera:rotate(dt):move(dt,dt)
end
### function camera:rotateTo(angle) [Set camera rotation.]
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) [Change zoom.]
*Multiply* zoom: `camera.scale = camera.scale * mul`.
#### Parameters:
=number mul=
Zoom change. Should be > 0.
#### Returns:
=number=
The camera.
#### Example:
camera:zoom(2) -- make everything twice as big
#### Example:
camera:zoom(0.5) -- ... and back to normal
#### Example:
camera:zoom(-1) -- flip everything
### function camera:zoomTo(zoom) [Set zoom.]
Set zoom: `camera.scale = zoom`.
#### Parameters:
=number zoom=
New zoom.
#### Returns:
=number=
The camera.
#### Example:
camera:zoomTo(1)
2012-10-19 13:37:41 +00:00
### function camera:attach() [Attach camera.]
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()
cam:detach()
draw_hud()
end
### function camera:detach() [Detach camera.]
Stop looking through the camera.
#### Example:
function love.draw()
camera:attach()
draw_world()
cam:detach()
draw_hud()
end
### function camera:draw(func) [Attach, draw, then detach.]
Wrap a function between a `camera:attach()/camera:detach()` pair:
cam:attach()
func()
cam:detach()
#### Parameters:
=function func=
Drawing function to be wrapped.
#### Example:
function love.draw()
camera:draw(draw_world)
draw_hud()
end
### function camera:worldCoords(x, y) [Convert point to world coordinates.]
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) [Convert point to camera coordinates.]
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() [Get mouse position in world coordinates.]
Shortcut to `camera:worldCoords(love.mouse.getPosition())`.
#### Returns:
=numbers=
Mouse position in world coordinates.
#### Example:
x,y = camera:mousepos()
selectedUnit:plotPath(x,y)
## License
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
You can view and download the individual modules on github: [vrld/hump](http://github.com/vrld/hump)
2012-10-19 13:37:41 +00:00
You may also download the whole packed sourcecode either in
[zip](http://github.com/vrld/hump/zipball/master) or
[tar](http://github.com/vrld/hump/tarball/master) formats.
Using [Git](http://git-scm.com), 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