Main Module

HC = require 'HC'

The purpose of the main modules is to connect shapes with the spatial hash – a data structure to quickly look up neighboring shapes – and to provide utilities to tell which shapes intersect (collide) with each other.

Most of the time, HC will be run as a singleton; you can, however, also create several instances, that each hold their own little worlds.

Initialization

HC.new([cell_size = 100])
Arguments:
  • cell_size (number) – Resolution of the internal search structure (optional).
Returns:

Collider instance.

Creates a new collider instance that holds a separate spatial hash. Collider instances carry the same methods as the main module. The only difference is that function calls must use the colon-syntax (see below).

Useful if you want to maintain several collision layers or several separate game worlds.

The cell_size somewhat governs the performance of HC.neighbors() and HC.collisions(). How this parameter affects the performance depends on how many shapes there are, how big these shapes are and (somewhat) how the shapes are distributed. A rule of thumb would be to set cell_size to be about four times the size of the average object. Or just leave it as is and worry about it only if you run into performance problems that can be traced back to the spatial hash.

Example:

collider = HC.new(150) -- create separate world

-- method calls with colon syntax
ball = collider:circle(100,100,20)
rect = collider:rectangle(110,90,20,100)

for shape, delta in pairs(collider:collisions(ball)) do
    shape:move(delta.x, delta.y)
end
HC.resetHash([cell_size = 100])
Arguments:
  • cell_size (number) – Resolution of the internal search structure (optional).

Reset the internal search structure, the spatial hash. This clears all shapes that were registered beforehand, meaning that HC will not be able to find any collisions with those shapes anymore.

Example:

function new_stage()
    actors = {} -- clear the stage on our side
    HC.resetHash() -- as well as on HC's side
end

Shapes

See also the HC.shapes sub-module.

HC.rectangle(x, y, w, h)
Arguments:
  • x,y (numbers) – Upper left corner of the rectangle.
  • w,h (numbers) – Width and height of the rectangle.
Returns:

The rectangle Shape() added to the scene.

Add a rectangle shape to the scene.

Note

Shape() transformations, e.g. Shape.moveTo() and Shape.rotate() will be with respect to the center, not the upper left corner of the rectangle!

Example:

rect = HC.rectangle(100, 120, 200, 40)
rect:rotate(23)
HC.polygon(x1, y1, ..., xn, yn)
Arguments:
  • x1,y1,...,xn,yn (numbers) – The corners of the polygon. At least three corners that do not lie on a straight line are required.
Returns:

The polygon Shape() added to the scene.

Add a polygon to the scene. Any non-self-intersection polygon will work. The polygon will be closed; the first and the last point do not need to be the same.

Note

If three consecutive points lie on a line, the middle point will be discarded. This means you cannot construct polygon shapes that are lines.

Note

Shape() transformations, e.g. Shape.moveTo() and Shape.rotate() will be with respect to the center of the polygon.

Example:

shape = HC.polygon(10,10, 40,50, 70,10, 40,30)
shape:move(42, 5)
HC.circle(cx, cy, radius)
Arguments:
  • cx,cy (numbers) – Center of the circle.
  • radius (number) – Radius of the circle.
Returns:

The circle Shape() added to the scene.

Add a circle shape to the scene.

Example:

circle = HC.circle(400, 300, 100)
HC.point(x, y)
Arguments:
  • x, y (numbers) – Position of the point.
Returns:

The point Shape() added to the scene.

Add a point shape to the scene.

Point shapes are most useful for bullets and such, because detecting collisions between a point and any other shape is a little faster than detecting collision between two non-point shapes. In case of a collision, the separating vector will not be valid.

Example:

bullets[#bullets+1] = HC.point(player.pos.x, player.pos.y)
HC.register(shape)
Arguments:

Add a shape to the bookkeeping system. HC.neighbors() and Hc.collisions() works only with registered shapes. You don’t need to (and should not) register any shapes created with the above functions.

Overwrites Shape.move(), Shape.rotate(), and Shape.scale() with versions that update the HC.spatialhash.

This function is mostly only useful if you provide a custom shape. See Custom Shapes.

HC.remove(shape)
Arguments:
  • shape (Shape) – The Shape() to remove from the spatial hash.

Remove a shape to the bookkeeping system.

Warning

This will also invalidate the functions Shape.move(), Shape.rotate(), and Shape.scale(). Make sure you delete the shape from your own actor list(s).

Example:

for i = #bullets,1,-1 do
    if bullets[i]:collidesWith(player)
        player:takeDamage()

        HC.remove(bullets[i]) -- remove bullet from HC
        table.remove(bullets, i) -- remove bullet from own actor list
    end
end

Collision Detection

HC.collisions(shape)
Arguments:
  • shape (Shape) – Query shape.
Returns:

Table of colliding shapes and separating vectors.

Get shapes that are colliding with shape and the vector to separate the shapes. The separating vector points in the direction that shape has to move to clear the collission. The length of the vector is the minimal amount that either shape has to move to clear the collission.

The table is a set, meaning that the shapes are stored in keys of the table. The values are the separating vector. You can iterate over the shapes using pairs (see example).

Example:

local collisions = HC.collisions(shape)
for other, separating_vector in pairs(collisions) do
    shape:move( separating_vector.x/2,  separating_vector.y/2)
    other:move(-separating_vector.x/2, -separating_vector.y/2)
end
HC.neighbors(shape)
Arguments:
  • shape (Shape) – Query shape.
Returns:

Table of neighboring shapes, where the keys of the table are the shapes.

Get other shapes in that are close to shape. The table is a set, meaning that the shapes are stored in keys of the table. You can iterate over the shapes using pairs (see example).

Note

The result depends on the size and position of shape as well as the grid size of the spatial hash: HC.neighbors() returns the shapes that are in the same cell(s) as shape.

Example:

local candidates = HC.neighbors(shape)
for other in pairs(candidates) do
    local collides, dx, dy = shape:collidesWith(other)
    if collides then
        other:move(dx, dy)
    end
end
HC.shapesAt(x, y)
Arguments:
  • x,y (numbers) – Point to query.
Returns:

Table of shapes at the point, where the keys of the table are the shapes.

Get shapes that contain the point (x,y). The table is a set, meaning that the shapes are stored in keys of the table. You can iterate over the shapes using pairs (see example).

Example:

local shapes = HC.shapesAt(love.mouse.getPosition)
for s in pairs(shapes) do
    game.selectUnit(s)
end
HC.hash()
Returns:SpatialHash().

Get a reference to the SpatialHash() instance.