Isometric Depth Sorting — The Problem Nobody Warns You About
It Looks Simple Until It Isn't
Isometric projection seems straightforward: tiles go on a diamond grid, objects draw back-to-front. Sort by mapX + mapY and you're done.
Except you're not. Because then you add:
- Multi-tile furniture (a 2×3 sofa)
- Stackable items (gold ingots piled on a tile)
- Players walking between furniture
- Background items that render below the floor grid
Each of these breaks the naive sort in a different way.
The Depth Sort Formula
For a pure tile-based game, the depth key is indeed mapX + mapY. Items at higher sums are "closer" to the camera in isometric space and should draw later (on top).
entries.sort((a, b) ->
Integer.compare(a.mapX + a.mapY, b.mapX + b.mapY));
This works perfectly for 1×1 items on different tiles. It falls apart for multi-tile furniture because the origin tile (top-left) doesn't represent the visual footprint.
Multi-Tile Furniture
A 2×3 desk at position (2, 4) occupies tiles (2,4), (2,5), (2,6), (3,4), (3,5), (3,6). Its depth should be based on its bottom-right corner — the closest point to the camera — not its origin.
The fix: use mapX + mapY + tileWidth + tileHeight - 2 as the depth key for furniture.
Stacking: The Visual Offset Problem
When you place 5 gold ingots on the same tile, they all have the same depth key and the same screen position. Without intervention, they render exactly on top of each other — one visible ingot instead of five.
The solution is a stackIndex per item: the Nth stackable item on a tile draws with a vertical offset of stackIndex * itemHeight * overlapFactor.
int stackShift = 0;
if (furniture.stackable && furniture.stackIndex > 0) {
stackShift = (int)(furniture.stackIndex * drawH * STACK_OVERLAP);
}
g2d.drawImage(image, posX, posY - stackShift, drawW, drawH, null);
The overlapFactor (0.15–0.65) controls how much each item peeks above the one below. Lower values = tighter stacking.
Background Items: The Two-Pass Solution
Floor rugs and carpets should render below everything, including the tile highlight overlay. But they're still "furniture" in the data model.
The solution is a two-pass render:
- Pass 1: Draw tiles, then background furniture (sorted)
- Pass 2: Draw standing furniture interleaved with players (sorted)
A background boolean flag on each furniture item determines which pass handles it.
The Interaction With Pathfinding
Depth sorting is purely visual, but it creates an expectation: if item A renders behind item B, clicking on the overlap region should select B (the frontmost).
This means hit-testing must mirror the render order — iterate in reverse depth order and return the first non-transparent pixel match. If you sort differently for rendering vs. hit-testing, users will click on visually hidden items.
Lessons
Isometric rendering is a masterclass in how simple rules create complex interactions. Every feature you add — stacking, multi-tile, background layers, players — creates a new edge case in the sort order.
The key insight: render order is not a single sort. It's a multi-pass pipeline where each pass has its own rules. Trying to solve everything with one comparator leads to bugs that only appear with specific furniture arrangements.