From 318f00e8aea589efcb672bea61b1f33c36a56a3c Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Fri, 18 Sep 2026 06:41:50 +0200 Subject: [PATCH] Tilemaps: placeable asset painted with autotiles, layers, brush, eraser, rectangle, runtime and export (#128) --- Architecture.md | 5 ++-- Autotiles.md | 9 +++--- Development.md | 10 +++---- Home.md | 1 + Tilemaps.md | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ _Sidebar.md | 1 + 6 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 Tilemaps.md diff --git a/Architecture.md b/Architecture.md index b3200c5..c518f84 100644 --- a/Architecture.md +++ b/Architecture.md @@ -19,7 +19,7 @@ the scrolling part — and it *measures* that chrome (a search box, a header, a row of buttons) rather than repeating it as a number, so no popup can forget one. A fixed cap would waste a tall screen or overflow a short one, and a constant gap that frames a popup on a laptop is a hairline - on a large screen. It is what holds the whole `+` menu of sixteen asset types on screen, and what + on a large screen. It is what holds the whole `+` menu of seventeen asset types on screen, and what lets the image, font and sound pickers show six rows of what the server has instead of two. A popup that does not fit below its anchor is still *moved* up rather than squeezed, and `clampPopupY()` enforces the tenth while moving it — otherwise the gap ends up entirely above it @@ -61,7 +61,7 @@ ``` web/index.html Vite source page (dev) index.html built page (generated, served) -src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames and the slice grid), autotile (the two block kinds, which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (where a state machine's current state lives: per instance or one for the run — variables are shared by living on a Variables asset instead) +src/core/model/ Project (object holders), Asset + AssetTypes, SceneObject, Properties (value types), hierarchy, walk (generic parent/child walks), instance (container content resolved from its template), usages (where an asset is used), container (the cycle guard), placement (what may be dropped where), texture (frames and the slice grid), autotile (the two block kinds, which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), animation (which frame at time t, a row's value there, what a row may aim at, the players on an object; KeyRef and its lookups) + animationPreview (the root at a frame, written into copies), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (where a state machine's current state lives: per instance or one for the run — variables are shared by living on a Variables asset instead) src/core/properties/ PropertySchema, kinds (one row per value kind), listItems (an item's kind, read off its value), resolve (the only override merge), deviation (what is gold), collider/camera/bounds geometry, aspect, enabled, editorRun, assetRef/textureRef (how a reference reads), declaredRows (the row a Property node declares in a shader or particle graph, and the params that node has in both) src/core/commands/ undoable commands, CommandStack, SetBagValueCommand base, RenameCommand base (undone by renaming back: variable, struct field, port), one import command shape for texture/font/sound, require.ts (validated lookups, and what an instance may not change), naming.ts src/core/graph/ graph model, NodeRegistry/NodeContext/values, interpreter, dropTarget, nested (behaviors as nodes), @@ -108,6 +108,7 @@ src/editor/animation/ AnimationEditor (header + picker, preview over timeline), RowDialog + FramesDialog (FormDialogs) src/editor/autotile/ AutotileEditor (header + picker + kind), AutotileCanvas (every tile of the block and a sample map, animated), PictureStrip (the pictures as animation steps; dropIndex is the reorder maths) +src/editor/inspector/ … tilemapSection (tools, autotiles and layers of a placed tilemap; painting itself is SceneViewport) src/editor/statemachine/ StateMachineEditor (header + picker, on the graph canvas) src/editor/particles/ ParticlesEditor (graph canvas left of a preview panel, splitter between them), ParticlePreview (the panel: the asset's own particles, wearing its own rows) diff --git a/Autotiles.md b/Autotiles.md index 8fbc346..ac4f758 100644 --- a/Autotiles.md +++ b/Autotiles.md @@ -69,9 +69,8 @@ Kind, rate and playback are ordinary properties, so the inspector shows them lik **Remove** takes the selected one out, and dragging a row to another place reorders the steps. Every one of those is an undo step. -## What it does not do yet +## Where they are placed -Nothing places an autotile: there is no tilemap, no painting in the Scene tab and nothing of it in -a running game — all of that is [#128](https://git.nockal.com/meleki/game-maker/issues/128). Two -different autotiles do not join each other, and there is no way to say which one wins where they -meet; that question belongs to the tilemap as well. +An autotile is never placed by itself: it is laid down by a [tilemap](Tilemaps), which is the asset +that carries a grid of cells and is placed in a scene. Two different autotiles do not join each +other there — each one closes against everything else, as the RPG-Maker sheets are drawn. diff --git a/Development.md b/Development.md index ee2f14a..27b5051 100644 --- a/Development.md +++ b/Development.md @@ -12,7 +12,7 @@ npm run check # typecheck + lint + format:check + tests, the gate CI runs # code is lost (tail's is reported), so a red run reads as green npm run build # writes dist/ and regenerates index.html ./build.sh # the same under `set -o pipefail`, for callers that pipe the output -node tools/seed-e2e.mjs && node tools/e2e.mjs # 989 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1006 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 @@ -64,7 +64,7 @@ The e2e run is a list of steps in `tools/e2e/` (hierarchy, inspector, play, asse graph, parenting, colliders, containers, text/enabled, menus, textures, frames, overflow, aspect, container tab, run-in-editor, pause, nested behaviors, key repeat, list tab, struct variable, variable nodes, property nodes, animations, state machines, console, shaders, transforms, fonts, -particles, shared scope, render on change, sounds, load scene, graph groups, nine-slice, plugins, autotiles) that build on each other's +particles, shared scope, render on change, sounds, load scene, graph groups, nine-slice, plugins, autotiles, tilemaps) that build on each other's scene state and share the CDP driver in `tools/lib/harness.mjs` — `press`, `undo`, `play`, `object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it exercises. @@ -104,8 +104,8 @@ whole suite down the day the Particles tab was added. The same holds for the **`+` menu**: it has one entry per asset type, and an entry below the mask edge is still found by `locate` while a click on it lands on nothing. The fourteenth type (Variables) pushed `Font` under that edge and took the font step down, because only the particles -step had carried a workaround of its own; sound is the fifteenth and the autotile the sixteenth. Since #43 a menu may grow to the -height of the canvas, so on the test window all sixteen stand on screen at once — the step still +step had carried a workaround of its own; sound is the fifteenth, the autotile the sixteenth and the tilemap the seventeenth. Since #43 a menu may grow to the +height of the canvas, so on the test window all seventeen stand on screen at once — the step still picks a type with `pickAssetType(t, 'Font')` from the same file, which keeps the scroll as insurance for a shorter window. The menu's own order is checked in the assets step through `__gm.menuRows()`: two alphabetical groups, placeable types then library ones, one separator between them. Order is @@ -232,4 +232,4 @@ widths to find click points. The **render device is not in this repo** — `/dev and a 1 GB `/dev/shm` come from `container.options` in the runner's own `config.yaml`, which applies to every job on that runner. Without them chromium falls back to llvmpipe, which gets WebGL 1.0 only. Measured in the job container: `ANGLE (Intel, Vulkan 1.3.230, HD Graphics 630)`, -WebGL 2.0, and the same 989 checks as here. +WebGL 2.0, and the same 1006 checks as here. diff --git a/Home.md b/Home.md index 5d5fe84..e31baf9 100644 --- a/Home.md +++ b/Home.md @@ -19,6 +19,7 @@ Live: · Widget playground: