The main module.
HardonCollider will automatically handle - but not resolve - collisions. It uses search data structure - a spatial hash - to quickly find colliding shapes.
A spatial hash is simply a grid that is laid over the whole scene in which a shape can occupy
several cells. To find shapes that may be colliding, you simply need to look which shapes occupy
the same cell. You can specify the cell size in the init
function.
To get a less boring explanation on how to use this, see the tutorial (once it's there).
Initializes the library. Call this in love.load(). All the parameters can be omitted.
cell_size
(100)callback_start
(empty function)callback_persist
(empty function)callback_stop
(empty function)function love.load()
hardoncollider.init(150)
end
Sets the different callbacks. The second calling style let's you specify the callbacks by name, see the example.
If nil
is passed as any callback, the callback will not be changed.
Each callback has the following prototype:
function callback(dt, shape_one, shape_two, mtv_x, mtv_y)
The two shape
parameters are the colliding shapes. The last two parameters, mtv_x
and mtv_y
define the minimum translation vector, i.e. the direction and magnitude shape_one has to be moved so that
the collision will be resolved.
start
persist
stop
function start(dt, shape_one, shape_two, mtv_x, mtv_y)
print('started colliding:', shape_one, shape_two)
print('mtv:', mtv_x, mtv_y)
end
function persist_one(dt, shape_one, shape_two, mtv_x, mtv_y)
print('still colliding:', shape_one, shape_two)
-- move both shape_one and shape_two to resolve the collision
shape_one:move(mtv_x/2, mtv_y/2)
shape_two:move(-mtv_x/2, -mtv_y/2)
end
function persist_two(dt, shape_one, shape_two, mtv_x, mtv_y)
print('still colliding:', shape_one, shape_two)
-- move only shape_one to resolve the collision
shape_one:move(mtv_x, mtv_y)
end
function stop(dt, shape_one, shape_two) -- ignore the translation vector
print('collision resolved')
end
function love.load()
hardoncollider.init(100)
-- set initial callbacks
hardoncollider.setCallbacks(start, persist_one, stop)
-- change persist callback
hardoncollider.setCallbacks{persist = persist_two}
end
Checks for collisions and call callbacks. Use this in love.update(dt)
.
A maximum time delta can be specified. dt
will be chopped up in slices
not bigger than this maximum and the scene is updated for each time slice.
Note that the delta time has no effect on the collision detection itself, but will be passed to the callback functions.
dt
max_delta
function love.update(dt)
hardoncollider.update(dt, .02)
end
Add a polygon to the collision detection system. Any non-intersection polygon will work, even convex polygons.
Note that if three consecutive points lie on a line, the middle point will be discarded. This means you cannot construct polygon shapes out of lines.
x1,y1, ..., xn,yn
shape = hardoncollider.addPolygon(10,10, 40,50, 70,10, 40,30)
Add a rectangle shape to the collision detection system.
x, y
w, h
rect = hardoncollider.addRectangle(100,120, 200,40)
Add a circle shape to the collision detection system.
cx, cy
radius
circle = hardoncollider.addCircle(400,300, 100)
Remove a shape from the collision detection system. Note that if you remove a shape in
the start
or persist
callback, other shapes might still have
collided with it, so the shape will be argument to the other calls of start
or persist
. In any case, the stop
callback will be called
in the next call to update
for each shape which the removed shape collided with.
shape
hardoncollider.remove(circle)
Add shapes to a group. Shapes in the same group will not collide with each other.
group
shape, ...
hardoncollider.addToGroup("platforms", platform1, platform2, platform3)
Remove shapes from a group.
group
shape, ...
hardoncollider.removeFromGroup("platforms", not_a_platform)
Makes a shape permeable. Ghost shapes will not collide with any other shape.
shape, ...
if cheat.set_ghost then
hardoncollider.setGhost(player)
end
Shape classes with collision detection methods.
This defines methods to move, rotate and draw shapes created with hardoncollider.add*
.
If you don't want to use the full blown module, you can still use these classes to test for colliding shapes.
They might also be useful for doing GUI stuff, e.g. when testing if the mouse hovers a button.
Some functions (getAxes
, projectOn
) are left
undocumented, as they have little value outside the scope of collision detection.
Construct a shape using a non-intersecting ploygon.
You can either specify the coordinates as with hardoncollider.addPolygon()
or use an instance of the Polygon class.
x1,y1, ..., xn,yn
polygon
shape = PolygonShape(100,100, 200,200, 300,100)
Construct a circular shape.
cx, cy
radius
shape = CircleShape(400,300, 100)
Move the shape.
x, y
circle:move(10,15) -- move the circle 10 units right and 15 units down
Rotate the shape. A rotation center can be specified. If no center is given, the shape's center is used.
angle
cx, cy
rectangle:rotate(math.pi/4)
Get the center of the shape.
If the shape is a CircleShape, this is the circle center, else it's the polygon's centroid.
x, y
print("Circle at:", circle:center())
Draw the shape either filled or as outline.
mode
circle:draw('fill')
Test if two shapes collide.
other
collide
true
if the two shapes collide, false
otherwise.mtv
nil
if the two shapes don't collide.if circle:collidesWith(rectangle) then
print("collision detected!")
end
Definition of a Polygon class and implementation of some handy algorithms.
On it's own, this class does not offer any collision detection. If you want that, use a PolygonShape instead.
Construct a polygon.
At least three points that are not collinear (being on a straight line) are needed to construct the polygon. If there are collinear points, these points will be removed so that the overall shape of the polygon is not changed.
x1,y1, ..., xn,yn
poly = Polygon(10,10, 40,50, 70,10, 40,30)
Get the polygon's vertices. Useful for drawing with love.graphics.polygon()
.
x1,y1, ..., xn,yn
love.graphics.draw('line', poly:unpack())
Get a copy of the polygon.
Since Lua uses references when simply assigning an existing polygon to a variable, unexpected things can happen when operating on the variable. Consider this code:
p1 = Polygon(10,10, 40,50, 70,10, 40,30)
p2 = p1
p3 = p1:clone()
p2:rotate(math.pi) -- p1 will be rotated, too!
p3:rotate(-math.pi) -- only p3 will be rotated
polygon
copy = poly:clone()
copy:move(10,20)
Get axis aligned bounding box.
x1, y1
x2, y2
x1,y1,x2,y2 = poly:getBBox()
-- draw bounding box
love.graphics.rectangle('line', x1,y2, x2-x1, y2-y1)
Test if a polygon is convex, i.e. a line line between any two points inside the polygon will lie in the interior of the polygon.
convex
true
if the polygon is convex, false
otherwise.-- split into convex sub polygons
if not poly:isConvex() then
list = poly:splitConvex()
else
list = {poly:clone()}
end
Move a polygon in a direction. You can either use coordinates (x, y
) or a hump vector.
x, y
direction
poly:move(10,-5) -- move 10 units right and 5 units up
Rotate the polygon. You can define a rotation center. If it is omitted, the polygon will be rotated around it's centroid.
For defining a rotation center, you can either use coordinate form (cx, cy
) or a hump vector.
angle
cx, cy
center
p1:rotate(math.pi/2) -- rotate p1 by 90° around it's center
p2:rotate(math.pi/4, 100,100) -- rotate p2 by 45° around the point 100,100
Split the polygon into triangles.
triangles
triangles = poly:triangulate()
for i,triangle in ipairs(triangles) do
triangles.move(math.random(5,10), math.random(5,10))
end
Split the polygon into convex sub polygons.
convex_polygons
convex = concave_polygon:splitConvex()
function love.draw()
for i,poly in ipairs(convex) do
love.graphics.polygon('fill', poly:unpack())
end
end
Create a merged polygon of two polygons if, and only if the two polygons share one edge. If the polygons share more than one edge, the result may be erroneous.
This function does not change either polygon, but rather create a new one.
other
merged
nil
if the two polygons don't share an edge.merged = p1:mergedWith(p2)
To be documented later.