# Pixeldex · LÖVE Tile Collision Starter

Drop-in tile-grid collision for LÖVE 11.x. Bump.lua-style: axis-separated sweep tests, push-out resolution, supports passable tiles and one-way platforms. Pure Lua, no dependencies.

License: **CC0**.
Source: https://pixeldex.dev/prompts/love2d-bump-collision

---

## What's in this folder

| File | Purpose |
|---|---|
| `collision.lua` | The collision module. Drop into your project to use it. |
| `main.lua` | A working demo: pink box moves around a tile grid with WASD / arrow keys. Walls block it. |
| `conf.lua` | LÖVE config (window title, version). |

---

## Fastest path (~10 seconds)

If you have LÖVE 11.x installed:

```bash
love .
```

(from inside this folder). Or drag the entire folder onto the LÖVE application icon.

You'll get a 640×448 window with a tile grid and a pink box. Move with WASD / arrow keys. Walls block you. Try walking diagonally, you'll slide along walls instead of getting stuck on corners (that's the axis-separated resolution working).

---

## Using just the collision module

```lua
local Collision = require("collision")

-- Your tile grid: array of arrays of integer tile IDs.
-- 0 = empty/walkable, anything else = a tile in your tile set.
local grid = {
 {1,1,1,1,1},
 {1,0,0,0,1},
 {1,0,0,0,1},
 {1,1,1,1,1},
}

local world = Collision.new(grid, 32) -- 32 = tile size in pixels

-- Mark which tile IDs are walkable. 0 is passable by default.
world:set_passable(7) -- e.g. "decorative grass" tile

-- One-way platforms: blocks only when entity moves into the specified direction.
world:set_one_way(3, "up") -- e.g. cloud platform you can jump through from below

-- An entity is just a table with x, y, w, h.
local entity = { x = 100, y = 100, w = 16, h = 16 }

-- Move with collision resolution. Returns actual displacement + hit flags.
local function love.update(dt)
 local intent_dx = 80 * dt
 local intent_dy = 30 * dt
 local adx, ady, hit_x, hit_y = world:move(entity, intent_dx, intent_dy)
 -- entity.x and entity.y are already updated.
 -- hit_x / hit_y are true if you bonked a wall on that axis.
end
```

---

## How it works

Two key design choices that prevent the most common collision bugs:

1. **Axis-separated motion**. Resolve X first, then Y. Without this, entities snag on tile corners during diagonal movement.

2. **One-way tile direction check uses entity intent**. The tile's `direction` is the direction in which the tile blocks. So `set_one_way(3, "up")` means "this tile blocks entities moving UPWARD into it", which is the standard "jump-through platform from below."

Combined: the entity can pass through one-way platforms in any direction except the blocked one, and walks along walls smoothly without getting trapped on corners.

---

## Tunable

The module is small (~80 lines) and intentionally has no config. Customize by editing `collision.lua`:

- Add slope handling: in `is_solid`, check tile metadata for slope direction; compute a per-cell collision offset based on the entity's X-within-tile.
- Add tile-specific friction: track which cell the entity is standing on; the calling code adjusts horizontal velocity based on tile ID.
- Add trigger callbacks (non-blocking): add a `triggers` table, fire callbacks when entity overlaps a trigger tile.

---

## Pairing ideas

- Pair with the [LOVE2D particle system starter](https://pixeldex.dev/prompts/love2d-particle-system) for hit sparks when you bonk a wall.
- Pair with the [Pico-8 palette swap](https://pixeldex.dev/prompts/pico8-sprite-sheet-palette-swap), same axis-separation pattern works in Pico-8 with minor adjustments.

---

If you ship something with this, a link to https://pixeldex.dev helps. Not required.
