From daeeaf9fe85e5369b9b762fc488f4fbe42a01606 Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Thu, 24 Sep 2026 19:34:44 +0200 Subject: [PATCH] Describe sorting by Y, the tile sort offset and the frame sort line (#228) --- Architecture.md | 2 +- Development.md | 2 +- Scene-editing.md | 2 ++ Textures.md | 17 +++++++++++++++++ Tilemaps.md | 30 ++++++++++++++++++++++++++++++ Tiles.md | 5 ++++- 6 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Architecture.md b/Architecture.md index e250f98..769a6e6 100644 --- a/Architecture.md +++ b/Architecture.md @@ -131,7 +131,7 @@ src/viewport/ view (transforms), items (what to draw), symbols (how to CPU and laid side by side onto one canvas, so a frame is cut out like a texture frame), SoundLibrary, ShaderLibrary, ParticleLibrary, particles (one ParticleContainer per source), tilemap (one container per layer), - drawOrder (objects and tilemap layers by z, #174), activeCamera — no editor concepts, + drawOrder (objects and tilemap layers by z, #174, rows of a layer sorted by Y, #228), activeCamera — no editor concepts, so tests reach it freely src/editor/ App → Editor (layout + ticker), PlaySession (the run), ProjectSession (load/create/delete plus the top bar's one menu, #183), diff --git a/Development.md b/Development.md index 13ca402..83faf71 100644 --- a/Development.md +++ b/Development.md @@ -14,7 +14,7 @@ npm run build # writes dist/ and regenerates index.html ./build.sh # the same under `set -o pipefail`, for callers that pipe the output php tools/make-user.php [admin|user] # an account; every tool signs in (see Accounts) php tests/php/accounts.test.php # the API's account rules, no server needed (CI's gate job) -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1377 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1380 editor checks, screenshots in /tmp/gm-e2e-*.png GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 — a subset while working on one step ./e2e.sh start [GM_STEPS] # the same run detached, see below ./e2e.sh status # live progress, or the last run's result diff --git a/Scene-editing.md b/Scene-editing.md index a3f4bda..c2e8cc2 100644 --- a/Scene-editing.md +++ b/Scene-editing.md @@ -56,6 +56,8 @@ icon. Beside the icons the sprite underneath is picked as before. `z` is drawn on top; objects with equal `z` keep their hierarchy (tree) order. It is edited in the inspector next to X/Y, undoable like any move, and applies in edit and play mode alike. The layers of a tilemap are sorted in one by one, each at the map's `z` plus its own (see [Tilemaps](Tilemaps), #174). +On the `z` of a layer sorted by Y, Y decides instead: whatever stands higher up is drawn first +(see [Tilemaps](Tilemaps#sorting-by-y), #228). **Transform.** Sprites, texts, colliders and containers carry four more rows next to their position: **Scale** (one factor, default 1), **Rotation** (degrees, positive turns clockwise on diff --git a/Textures.md b/Textures.md index 44abf78..becc0f7 100644 --- a/Textures.md +++ b/Textures.md @@ -163,6 +163,23 @@ drawn twice as big beside a pattern at its old size reads as two different pictu sprite narrower than its borders applies to the scaled size: two 32-unit borders on a 20-unit box become two of 10. +## Sort line — where a figure stands + +On a tilemap layer sorted by Y (see [Tilemaps](Tilemaps#sorting-by-y)) a sprite is sorted by the +bottom edge of its box. A figure with a shadow or a few empty pixels below its feet would then stand +too low. A frame's **Sort line** moves that point up: whole image pixels from the frame's bottom +edge, 0 by default, at most the frame's height. A sprite wearing the frame stands that share of its +box higher up, so a line 6 px up a 24 px frame lifts a 48-unit sprite by 12 units. + +- It lives on **the frame**, not on the texture, because one texture can hold several sheets with + figures of different sizes. The frames of one walk cycle want the same value, or the figure + changes its place in the order while it walks. +- In the **Texture tab** the selected frame shows it as a dashed yellow line with a knob left of the + frame. At 0 the line lies on the bottom edge together with the bottom border and a resize handle, + so grab it by the knob; a line inside the frame can also be dragged directly. The inspector has + the same number under *Sorting*. One drag or one typed value is one undo step. +- 0 carries no key in the saved document. It only matters on a Z with a layer sorted by Y. + **Fitting the sprite to the picture.** The *Width* and *Height* rows of a sprite carry a ↔ / ↕ button offering two things: **Fit to 64 x 48** sets that one side from the other and the picture's proportions — the frame's rectangle when it wears a frame, the whole image otherwise — and **Set diff --git a/Tilemaps.md b/Tilemaps.md index 0fc532e..44df093 100644 --- a/Tilemaps.md +++ b/Tilemaps.md @@ -128,6 +128,8 @@ The layer's settings (#138): Z keep their list order; against other objects of equal Z the hierarchy decides, as always. One undo step per change; the running game draws the same order, and a map whose Z a run changes takes its layers with it. +- **Sort by Y** (#228), the box under the list, sorts the chosen layer row by row with every object + on its Z, see [Sorting by Y](#sorting-by-y). Off by default, one undo step per change. - **Show** decides how the scene view draws the *other* layers of the selected map: **All layers** as they are, **Others faded** at 30 %, or **Active layer only**. It is a way of looking while you paint, not a property of the map: it touches neither the project nor the game, a map that is not @@ -137,6 +139,34 @@ The **+** in the header of the list appends a layer on top. The **✕** at the r removes that layer, cells and all. A tilemap always keeps at least one layer — there has to be something to paint on, so the last one has no ✕. +### Sorting by Y + +For top-down maps a layer can be **sorted by Y** (#228): trees, fences and houses a figure walks +both behind and in front of. With the box ticked the layer is no longer one piece in the drawing +order but **one slot per row**, and everything on that layer's Z is sorted by where it stands: + +- a **row** of the layer by the bottom edge of its cells, +- an **object** by the bottom edge of its box, which for a figure is its feet. A sprite whose frame + has a **Sort line** (see [Textures](Textures)) stands on that line + instead, for a figure drawn with a shadow or empty pixels below its feet. + +Whatever stands higher up on screen is drawn first. A figure below a trunk's row is drawn in front +of the tree, a figure above it behind. Objects on that Z sort among each other the same way. Ties +keep the usual order: the hierarchy, and between layers their list order. + +- Only the Z of a sorted layer is affected. Objects and layers on every other Z are drawn as + before, so switching the box on changes nothing elsewhere in the scene. +- On that Z a layer that is **not** sorted, and the map itself, stay behind everything sorted: + neither has one place to stand. Keep the ground on a Z of its own below, or on the same Z unsorted. +- A tree whose **crown** is a row above its trunk would sort by its own row, so a figure right under + the crown would stand in front of it. Give the crown tile a **Sort offset** of 1 (see + [Tiles](Tiles)): its cells then sort with the row below, the trunk's. A crown two rows tall gets + 2 on its upper tile. +- Each row is drawn with one mesh per tile, so a sorted layer costs about one draw call per row and + tile instead of one per tile. +- The scene view, the game view and the running game sort the same way, and a click picks what is + drawn in front. + ## In the running game A tilemap is scene content, not editor decoration: it is drawn in the game exactly as in the diff --git a/Tiles.md b/Tiles.md index b3df85c..dcc426e 100644 --- a/Tiles.md +++ b/Tiles.md @@ -99,11 +99,14 @@ asks for; a picture that does not fit is kept and named, never silently dropped. - **Collision scale** — 1 by default. How big a solid cell blocks against its square, drawn in or out only on the sides without a blocking neighbour (#140); see [Tilemaps](Tilemaps#collision-scale). +- **Sort offset** — 0 by default, a whole number of rows from -16 to 16. On a layer sorted by Y a + cell of this tile is sorted as if it stood that many rows further down (#228): a treetop at 1 + sorts with the trunk below it. See [Tilemaps](Tilemaps#sorting-by-y). - A tile whose picture is a frame with **collision outlines** blocks with them instead of its square (#141) – an autotile with its outlines cut into the same quarters as its picture; see [Tilemaps](Tilemaps#outlined-tiles). -Kind, rate, playback, the solid switch and the collision scale are ordinary properties, so the inspector shows them like any other asset's +Kind, rate, playback, the solid switch, the collision scale and the sort offset are ordinary properties, so the inspector shows them like any other asset's — undo, defaults and the gold mark for a non-default value come for free. ## The tab