From 93a5641da712077be43ee42e80892f6b28e7cc8f Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Sat, 19 Sep 2026 01:56:20 +0200 Subject: [PATCH] Collision outlines for autotiles: several outlines per frame in Textures, outlined autotiles in Tilemaps and Tiles, Architecture and Development updated, check total 1068 (#141) Co-Authored-By: Claude Opus 5 --- Architecture.md | 4 ++-- Development.md | 2 +- Textures.md | 28 +++++++++++++++++++--------- Tilemaps.md | 40 ++++++++++++++++++++++++++++++---------- Tiles.md | 5 +++-- 5 files changed, 55 insertions(+), 24 deletions(-) diff --git a/Architecture.md b/Architecture.md index 0d5ad76..cffab33 100644 --- a/Architecture.md +++ b/Architecture.md @@ -66,7 +66,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, the slice grid, a frame's collision outline), polygon (an outline checked and cut into convex pieces), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, the outlined cells as polygons, and where one sits in the world), 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, the slice grid, a frame's collision outlines), polygon (an outline checked, cut into convex pieces and clipped to a rectangle), tile (the three kinds and their block shapes, reading a tile's pictures, the animation step), autotile (which quarter tile a neighbourhood asks for, the 47/16 tiles a block produces, 1 for a plain tile), tilemap (the cells of a layer, which neighbours a cell is joined to, the cells a stroke or a rectangle covers), tilemapCollision (the solid cells of a map, drawn in or out by their tile's collision scale and merged into rectangles, the outlined cells as polygons – an autotile's outlines cut into its quarters and pieced together per cell like its picture –, and where one sits in the world), 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, convex (capsules and polygons: rounded convex pieces against each other), rigidbody (what the engine reads off a rigidbody, a collider's material and the scene), 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), @@ -96,7 +96,7 @@ src/viewport/ view (transforms), items (what to draw), symbols (how to so tests reach it freely src/editor/ App → Editor (layout + ticker), PlaySession (the run), ProjectSession (load/create), EditorRun (run in editor), state, actions, shortcuts, debugHook, autosave, - polygonEdit (corners of an outline: what is under the pointer, put / move / take away – scene view and Texture tab), + polygonEdit (corners of an outline: what is under the pointer, whether a point is inside, put / move / take away – scene view and Texture tab), fileImport (drop/paste) → assetImport (one upload pipeline, an ImportKind per sort) ← textureImport / fontImport / soundImport, FilePicker (the shared "+ → Texture / Font / Sound" dialog; ImagePicker/FontPicker/SoundPicker only describe their row), framing (fit/marquee/snap maths), FormDialog (the small modal forms: label column, Cancel + confirm, one open at a time), AssetTabEditor (the shared shell of the asset tabs), TabHeader (the bar across the top of a tab – asset tabs, Plugins tab, NodeBoard), diff --git a/Development.md b/Development.md index 4264e3d..a4bb724 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 # 1067 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1068 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/Textures.md b/Textures.md index 4345e2c..a0dde31 100644 --- a/Textures.md +++ b/Textures.md @@ -73,17 +73,27 @@ wearing *any* of its frames, not just the ones naming the texture directly. ## Collision outline -A frame can carry an **outline** a tile made of it blocks with (#141; see -[Tilemaps](Tilemaps#outlined-tiles)). Switch **Collision** on in the Texture tab's header: a press -then edits the selected frame's outline instead of the frames. Click inside the frame to add a -corner after the last one – that is how an outline is drawn from nothing –, drag a corner to move -it, press on an edge to put a corner there, double-click a corner to take it away. Corners land on -whole pixels inside the frame and are stored relative to its top-left corner, so they move with the -frame. Every step is one undo step. The outlines of all frames are always shown; one that crosses -itself is drawn red. Switch **Collision** off to move and resize frames again. +A frame can carry **outlines** a tile made of it blocks with (#141; see +[Tilemaps](Tilemaps#outlined-tiles)). Several, because an autotile's block holds several shapes – +its bordered tile and its notches. Switch **Collision** on in the Texture tab's header: a press +then edits the selected frame's outlines instead of the frames. + +- **Click** inside the frame to add a corner after the last one of the *active* outline – that is + how an outline is drawn from nothing. The active one is drawn heavier; on a frame just selected it + is the last outline. +- **Ctrl+click** (Cmd+click) starts a new outline there. +- **Drag** a corner to move it, **press on an edge** to put a corner there, **double-click** a + corner to take it away. Each of these makes that outline the active one, and so does a click + inside another finished outline. +- **Delete** removes the active outline whole – in collision mode never the frame. + +Corners land on whole pixels inside the frame and are stored relative to its top-left corner, so +they move with the frame. Every step is one undo step. The outlines of all frames are always shown; +one that crosses itself is drawn red and collides as none. When the project is loaded, corners that are no numbers fall away, and an outline with fewer than -three corners left is none. +three corners left is none. A frame saved with a single outline, before a frame could hold +several, loads as a frame with that one outline. ## Nine-slice — a frame that keeps its corners diff --git a/Tilemaps.md b/Tilemaps.md index 1c52d51..14d5b62 100644 --- a/Tilemaps.md +++ b/Tilemaps.md @@ -155,19 +155,39 @@ The outline in the scene view shows the drawn-in or grown shapes. ### Outlined tiles -A plain tile (kind *None*) whose first picture is a frame with a **collision outline** (drawn in -the Texture tab, see [Textures](Textures#collision-outline)) blocks with that outline instead of -its whole square (#141): a slope, a rock, a tree trunk. The outline is scaled from the frame onto -the cell, and the tile's collision scale draws it in or out about the cell's centre. Such cells are -not merged – each brings its own polygon – and the scene view outlines each one. +A tile whose first picture is a frame with **collision outlines** (drawn in the Texture tab, see +[Textures](Textures#collision-outline)) blocks with those outlines instead of its whole square +(#141): a slope, a rock, a tree trunk, the shore of a lake. Such cells are not merged with the +rectangles – they bring their own polygons – and the scene view outlines each one. + +**A plain tile** (kind *None*) lays all its outlines over the cell: scaled from the frame onto the +cell, and drawn in or out about the cell's centre by the tile's collision scale. + +**A floor or wall autotile** is drawn from four quarters of its block per cell, picked by the +neighbours – so its outlines are drawn over the *whole block* and cut along the same quarters, and +every cell takes the pieces of exactly the quarters its picture is made of. An outline that follows +the drawing continues across cells the way the drawing does: outline the bordered 2 × 2 tile of a +floor a little inside its shore, and every lake painted with it blocks a little inside its shore, +at straight edges and outer corners alike. + +- A tile of the block with **nothing drawn in it** blocks whole in all its quarters. Most often that + is a floor's notch tile (top right): left undrawn, the inner corners of a lake stay closed. Once + anything is drawn in a tile of the block, only what is drawn blocks there, and a quarter can be + left open on purpose. +- Drawing past a tile's edge is harmless: what reaches into another quarter is cut off there. +- A cell drawn only from quarters that block whole – the inside of a lake – is a plain square and + is merged with its neighbours like any other. +- The collision scale does not apply to an autotile with outlines: the outline is the shape. An + autotile without one blocks as its square, with its collision scale, as before. + +For both: -- Only plain tiles: a floor or wall autotile's cell is pieced together from quarters of a block, - where no single frame is the cell. Those keep blocking as their square. - Only the first picture of an animated tile counts: a blocking shape that twitched with the animation is rarely wanted. -- A plain solid tile without an outline on another layer of the same cell covers the whole cell, - so there the outline is left out. -- An outline that crosses itself blocks as the square it would have replaced. +- A solid tile blocking as a square on another layer of the same cell covers the whole cell, so + there the outlines are left out. +- An outline that crosses itself collides as none; a tile none of whose outlines is a polygon blocks + as its square. In the running game the placed map is a **body of its own** in the collision system: diff --git a/Tiles.md b/Tiles.md index c3948f4..81057e4 100644 --- a/Tiles.md +++ b/Tiles.md @@ -63,8 +63,9 @@ 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). -- A plain tile whose picture is a frame with a **collision outline** blocks with that outline - instead of its square (#141); see [Tilemaps](Tilemaps#outlined-tiles). +- 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 — undo, defaults and the gold mark for a non-default value come for free.