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 <noreply@anthropic.com>
adminandClaude Opus 5 committed 2026-09-19 01:56:20 +02:00
1 parent 1ee4e0e41c
commit 93a5641da7
5 files changed
+55 -24

No files matched your search

+2 -2
@@ -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),
+1 -1
@@ -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
+19 -9
@@ -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
+30 -10
@@ -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:
+3 -2
@@ -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.