Math
Lumen used to add square roots, rounding, angles, and interpolation to Ghost's math module, because a game needs them on nearly every frame and Ghost's module did not have them. Ghost covers all of it natively now, so Lumen adds nothing here — the full module is documented under Math and Arrays.
Nothing you were calling has gone away. This page is what a game reaches for most.
Angles
Lumen's rotations are all in radians.
angle = math.atan2(target.y - player.y, target.x - player.x)
bullet.vx = math.cos(angle) * speed
bullet.vy = math.sin(angle) * speed
math.atan2(y, x) is correct in all four quadrants, which math.atan() cannot be. math.angle(x1, y1, x2, y2) is the same thing given two points instead of a difference, and math.degrees() converts for the places a game thinks in degrees — source.setPosition(), for one.
Following and bounds
function update(dt) {
camera.x = math.lerp(camera.x, player.x - window.width / 2, 8 * dt)
camera.x = math.clamp(camera.x, 0, map.pixelWidth - window.width)
}
math.lerp(from, to, amount) is how a camera follows smoothly rather than snapping. math.clamp(value, low, high) keeps it inside the map, and a health bar between empty and full. math.smoothstep(low, high, value) is lerp with easing at both ends.
Tiles and distance
tileX = math.floor(player.x / tileSize)
if (math.distance(player.x, player.y, enemy.x, enemy.y) < 64) {
enemy.chase(player)
}
math.floor(), math.ceil(), math.round(), and math.truncate() all answer with whole numbers, so they can index a tile map directly. math.round(n, places) takes an optional number of decimal places.
Randomness
| Method | Returns |
|---|---|
math.random() | A decimal between 0 and 1. |
math.random(n) | A whole number from 1 to n. |
math.random(low, high) | A whole number from low to high. |
math.randomSeed(n) | Seeds the generator. |
math.noise(x, [y]) | Smooth noise for the given coordinate, between 0 and 1. |
Lumen seeds the generator with 1 before your game runs. A procedural level therefore looks the same every time it is opened, which is what makes it something to iterate on rather than something that shifts underfoot. Call math.randomSeed() yourself for a different world each run:
function load() {
math.randomSeed(timer.getTime()) // a new world every launch
}
math and random modules share one generator, so Lumen's seed governs random.random() too, and either module's seed call reseeds both. Lumen's math used to keep a generator of its own; it no longer does.noise() is what you want when variation should be smooth rather than jittery — terrain height, cloud cover, a torch flicker. Neighbouring inputs give neighbouring outputs, which is exactly what random() does not do, and the same input always gives the same result.
height = math.noise(x * 0.05) * 40
Everything else
Ghost's math module also carries statistics, whole-number mathematics, and linear algebra — vectors, matrices, dot and cross products, and solving systems. Vector maths in particular is worth knowing about for movement and collision:
direction = math.normalize([target.x - player.x, target.y - player.y])
player.x = player.x + direction[0] * speed * dt
player.y = player.y + direction[1] * speed * dt