Let a tilemap layer sort its rows by Y with the objects (#228)
A tilemap layer was one piece in the draw order, so a figure stood either in front of a whole layer of trees or behind all of it. For a top-down game the trees in front of the figure's feet have to cover it and the ones behind must not. - A new "Sort by Y" box under the layer list sets `sortByY` on the painted layer. Every change is its own undo step. - Such a layer comes as one draw slot per row. On its `z`, the rows and all objects on that `z` are sorted by where they stand: the bottom of an object's box, the bottom of a row. Ties keep the hierarchy order, and other `z` values are untouched. - A layer drawn as one piece and the map's own node stay behind the sorted ones on the same `z`, since neither has a single place to stand. Ground belongs on a lower `z` or stays unsorted. - A new tile property "Sort offset" (whole rows, -16 to 16) lets a crown sort with the trunk below it. - `TilemapField` draws a container per row for such a layer, and picking takes the row into account, so a click picks what is drawn in front. Scene view, game view and play share the same rule. Limits: on a rotated map "down" is world Y. A layer sorted by Y costs about one draw call per row and tile instead of one per tile. Tests: - ./check.sh: 160 files, 2350 tests, CHECK EXIT 0 (before 2340). - 10 new unit tests in tests/viewport/sort-by-y.test.ts. - e2e 39-43: ALL PASSED, 148 checks. New check 40p-iii ticks the box and finds the slot of row 0, undo brings back the whole layer. - Counter checks: without the Y sort 5 unit tests fail, with the offset ignored 5, with no row in picking 1. If TilemapField draws no rows, exactly 40p-iii fails in e2e. - The e2e sum in the README is 1378, counted, not yet seen in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ffb5bbe039
commit
50854389bb
16 files changed
+467
-51
No files matched your search
@@ -226,12 +226,14 @@ export interface TileAsset extends AssetBase {
|
||||
* from its neighbours while drawing, never stored – see `model/tilemap.ts`. `z` is added to the `z`
|
||||
* of the placed tilemap (#174), as a child's is to its parent's, so every placement of the map
|
||||
* keeps its layers apart by the same steps; layers of equal `z` keep their list order.
|
||||
* `sortByY` draws the layer row by row, sorted in by Y with every object on its `z` (#228).
|
||||
*/
|
||||
export interface TilemapLayer {
|
||||
id: string;
|
||||
name: string;
|
||||
hidden?: boolean;
|
||||
z?: number;
|
||||
sortByY?: boolean;
|
||||
cells: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,12 @@ export const TILE_FRICTION_KEY = 'friction';
|
||||
export const TILE_BOUNCINESS_KEY = 'bounciness';
|
||||
export const TILE_COLLISION_SCALE_MIN = 0.1;
|
||||
export const TILE_COLLISION_SCALE_MAX = 2;
|
||||
/**
|
||||
* How many rows further down a cell of this tile is sorted on a layer sorted by Y (#228): a
|
||||
* treetop at 1 sorts with the trunk below it.
|
||||
*/
|
||||
export const TILE_SORT_OFFSET_KEY = 'sortOffset';
|
||||
export const TILE_SORT_OFFSET_LIMIT = 16;
|
||||
|
||||
/** Only the two that make sense for a tile that is always on screen; there is no end to hold. */
|
||||
const TILE_PLAYBACKS = ['loop', 'pingpong'] as const;
|
||||
@@ -73,6 +79,13 @@ export function tileFps(asset: TileAsset): number {
|
||||
return Number.isFinite(fps) && fps > 0 ? fps : 0;
|
||||
}
|
||||
|
||||
/** The sort offset in whole rows, held to the range the field offers; anything unreadable is 0. */
|
||||
export function tileSortOffset(asset: TileAsset): number {
|
||||
const rows = Math.round(Number(asset.props[TILE_SORT_OFFSET_KEY] ?? 0));
|
||||
if (!Number.isFinite(rows)) return 0;
|
||||
return Math.min(TILE_SORT_OFFSET_LIMIT, Math.max(-TILE_SORT_OFFSET_LIMIT, rows));
|
||||
}
|
||||
|
||||
/** A quarter tile inside the block, in quadrant units from its top-left corner. */
|
||||
export interface QuadPos {
|
||||
x: number;
|
||||
|
||||
Reference in new issue
Block a user