| Engine | LÖVE 11.x |
|---|---|
| Language | Lua 5.1 (LuaJIT) |
| Time to run | ~4 minutes |
| Level | Intermediate — comfortable with Lua tables and OOP-via-metatables |
| Output | One module (~120 lines), ready to require |
| Dependencies | None. Pure Lua. |
The prompt
Open claude.ai/new, paste, send.
$ Build me a tile-grid collision module for LÖVE 11.x in pure Lua. // shape - A module returned via "return Collision" exposing: - Collision.new(tile_grid, tile_size) → returns a world instance - world:move(entity, dx, dy) → returns actual_dx, actual_dy, hit_horizontal, hit_vertical - entity is a table with x, y, w, h fields. - tile_grid is a 2D table where grid[y][x] = tile_id. - A tile is solid if tile_id > 0 and not in the world.passable set. // behavior - Move axis-separated: resolve X first, then Y. This avoids corner snags. - For each axis, compute the candidate AABB at the new position, query overlapping grid cells, find the first solid one in the direction of travel, snap entity flush to its edge. - If no collision, accept the full motion. // extras - world:set_passable(tile_id) and world:set_one_way(tile_id, "up" | "down" | "left" | "right"). - One-way tiles: only block when entity is moving in the blocked direction AND was not already overlapping the tile. // constraints - No global state. Each world is independent. - No external libs. Bump.lua-style API but reimplemented. // return format - One file collision.lua in a code block. - Include a 10-line example showing how to use it from love.update. - No prose before or after.
What this gets you
The collision system that's "good enough for jam, good enough for shipped indie." Nothing fancy, no broadphase optimization, no rotated AABBs. But the axis-separated resolution avoids the most-common collision bug (catching on tile corners), and the one-way platform support is the thing that makes this go from "tutorial code" to "actually usable".
Quick checklist
- X is resolved before Y, in separate passes. If both axes resolve simultaneously, you'll snag on tile corners.
- One-way tiles check the entity's previous frame position. Otherwise you can't drop through a one-way platform by holding down.
- The world doesn't store entities. Each move() call is stateless. Cleaner architecture, easier to reason about.
Full breakdown coming soon
The deeper writeup with slopes, swept-circle alternative, and a worked platformer example is on the roadmap. Bookmark this page or check back on the blog.