Tilemaps: placeable asset painted with autotiles, layers, brush, eraser, rectangle, runtime and export (#128)
1 parent
f739aa5a7b
commit
318f00e8ae
6 files changed
+90
-12
No files matched your search
+3
-2
@@ -19,7 +19,7 @@
|
|||||||
the scrolling part — and it *measures* that chrome (a search box, a header, a row of buttons)
|
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
|
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
|
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.
|
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
|
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
|
`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)
|
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/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/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),
|
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)
|
RowDialog + FramesDialog (FormDialogs)
|
||||||
src/editor/autotile/ AutotileEditor (header + picker + kind), AutotileCanvas (every tile of the block and a sample map, animated),
|
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)
|
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/statemachine/ StateMachineEditor (header + picker, on the graph canvas)
|
||||||
src/editor/particles/ ParticlesEditor (graph canvas left of a preview panel, splitter between them),
|
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)
|
ParticlePreview (the panel: the asset's own particles, wearing its own rows)
|
||||||
|
|||||||
+4
-5
@@ -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.
|
**Remove** takes the selected one out, and dragging a row to another place reorders the steps.
|
||||||
Every one of those is an undo step.
|
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
|
An autotile is never placed by itself: it is laid down by a [tilemap](Tilemaps), which is the asset
|
||||||
a running game — all of that is [#128](https://git.nockal.com/meleki/game-maker/issues/128). Two
|
that carries a grid of cells and is placed in a scene. Two different autotiles do not join each
|
||||||
different autotiles do not join each other, and there is no way to say which one wins where they
|
other there — each one closes against everything else, as the RPG-Maker sheets are drawn.
|
||||||
meet; that question belongs to the tilemap as well.
|
|
||||||
+5
-5
@@ -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
|
# code is lost (tail's is reported), so a red run reads as green
|
||||||
npm run build # writes dist/ and regenerates index.html
|
npm run build # writes dist/ and regenerates index.html
|
||||||
./build.sh # the same under `set -o pipefail`, for callers that pipe the output
|
./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
|
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 start [GM_STEPS] # the same run detached, see below
|
||||||
./e2e.sh status # live progress, or the last run's result
|
./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,
|
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,
|
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,
|
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`,
|
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
|
`object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it
|
||||||
exercises.
|
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
|
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
|
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
|
(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
|
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 sixteen stand on screen at once — the step still
|
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
|
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()`:
|
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
|
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
|
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
|
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 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.
|
||||||
+1
@@ -19,6 +19,7 @@ Live: <https://nockal.com/game-maker/> · Widget playground: <https://nockal.com
|
|||||||
| [Sounds](Sounds) | Uploading a sound, what the asset says, `Play sound` and `Stop sound`, what a pause and a stop do to a voice |
|
| [Sounds](Sounds) | Uploading a sound, what the asset says, `Play sound` and `Stop sound`, what a pause and a stop do to a voice |
|
||||||
| [Animations](Animations) | Animation assets, the Animation tab, how they run |
|
| [Animations](Animations) | Animation assets, the Animation tab, how they run |
|
||||||
| [Autotiles](Autotiles) | The autotile asset, the two block kinds, the pictures as animation steps, the Autotile tab |
|
| [Autotiles](Autotiles) | The autotile asset, the two block kinds, the pictures as animation steps, the Autotile tab |
|
||||||
|
| [Tilemaps](Tilemaps) | The tilemap asset, painting in the scene view, layers, what it looks like in the game |
|
||||||
| [State machines](State-machines) | The state machine asset, its scope, named transitions, the start state, `State branch` and its `else`, `Set state`, `Set state by name`, `State transition`, the two state events and the live highlight |
|
| [State machines](State-machines) | The state machine asset, its scope, named transitions, the start state, `State branch` and its `else`, `Set state`, `Set state by name`, `State transition`, the two state events and the live highlight |
|
||||||
| [Shaders](Shaders) | The shader asset, the Shader tab, the node set, the Adjust colors recipe, wearing a shader |
|
| [Shaders](Shaders) | The shader asset, the Shader tab, the node set, the Adjust colors recipe, wearing a shader |
|
||||||
| [Particles](Particles) | The particles asset, a particle as a formula of its age, the source's rows, the Particles tab |
|
| [Particles](Particles) | The particles asset, a particle as a formula of its age, the source's rows, the Particles tab |
|
||||||
|
|||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
# Tilemaps
|
||||||
|
|
||||||
|
A **tilemap** is a grid painted with [autotiles](Autotiles), and it is what puts them into a scene:
|
||||||
|
an autotile is a tile, the tilemap is the ground it is laid on. It is an asset like any other, and
|
||||||
|
unlike an autotile it is **placed** — drag it from the asset panel into the scene, and it is drawn
|
||||||
|
in the editor, in the running game and in the preview alike.
|
||||||
|
|
||||||
|
## The asset
|
||||||
|
|
||||||
|
`+ → Tilemap` makes one. Its own rows in the inspector are:
|
||||||
|
|
||||||
|
| Row | What it means |
|
||||||
|
|---|---|
|
||||||
|
| Columns, Rows | How far the grid reaches. Cells outside are not drawn — and not thrown away either, so making the grid smaller and larger again brings the drawing back. |
|
||||||
|
| Tile size | The side of one cell in world units. An autotile's pictures are scaled onto it, so one map can mix autotiles drawn at different resolutions. |
|
||||||
|
|
||||||
|
Everything else is the usual: the switch, the position, scale, rotation and mirroring. The map is
|
||||||
|
centred on its position, as every other object is.
|
||||||
|
|
||||||
|
**Which tile a cell shows is never stored.** It follows from the neighbours and is worked out while
|
||||||
|
drawing, so painting one cell fixes the edges of the ones around it by itself. Only *which
|
||||||
|
autotile* lies in a cell is kept.
|
||||||
|
|
||||||
|
Two different autotiles do **not** join: each one closes against everything else, which is how the
|
||||||
|
RPG-Maker sheets are drawn. Outside the grid counts as empty as well, so a map ends with a proper
|
||||||
|
edge.
|
||||||
|
|
||||||
|
## Painting
|
||||||
|
|
||||||
|
Select a placed tilemap; the inspector then shows a **PAINTING** section under its rows.
|
||||||
|
|
||||||
|
- **Select** leaves the scene view as it is: the map is picked up and dragged like any other object.
|
||||||
|
This is what a tilemap starts in — a map one cannot move would be worse than a click.
|
||||||
|
- **Brush** paints the chosen autotile. A drag paints every cell it passes, not only the ones the
|
||||||
|
pointer was reported at.
|
||||||
|
- **Erase** clears cells the same way.
|
||||||
|
- **Rect** fills the box a drag spans; while dragging, that box is outlined.
|
||||||
|
|
||||||
|
While any of the three is chosen, the map shows its cell grid, so one does not paint blind.
|
||||||
|
Shift-drag still pans the view, whatever the tool.
|
||||||
|
|
||||||
|
**One stroke is one undo step** — a line of twenty cells comes back with one Ctrl+Z, and a stroke
|
||||||
|
painted over another autotile puts *that* one back, not merely emptiness.
|
||||||
|
|
||||||
|
Under **AUTOTILES** stands every autotile of the project; picking one reaches for the brush by
|
||||||
|
itself. Without one chosen, painting says so instead of quietly doing nothing.
|
||||||
|
|
||||||
|
## Layers
|
||||||
|
|
||||||
|
A tilemap holds one or more layers, listed under **LAYERS**, the topmost row being the one drawn
|
||||||
|
last (over the others). Per row there is:
|
||||||
|
|
||||||
|
- a **switch**: a layer switched off keeps its cells and is left out of the drawing, in the editor
|
||||||
|
and in the game alike;
|
||||||
|
- its **name**, which is also how it is chosen to paint on;
|
||||||
|
- **↑ / ↓** to move it in the drawing order and **✕** to remove it, cells and all.
|
||||||
|
|
||||||
|
**Add layer** appends one on top. A tilemap always keeps at least one layer — there has to be
|
||||||
|
something to paint on.
|
||||||
|
|
||||||
|
## In the running game
|
||||||
|
|
||||||
|
A tilemap is scene content, not editor decoration: it is drawn in the game exactly as in the
|
||||||
|
editor, and an animated autotile keeps running there at its own rate. Every map showing the same
|
||||||
|
autotile is on the same step, so animated water does not run out of phase between two of them.
|
||||||
|
|
||||||
|
Drawing is done with **one mesh per autotile and layer** rather than one sprite per cell, so a map
|
||||||
|
of a thousand tiles costs as many draw calls as it has autotiles on that layer.
|
||||||
|
|
||||||
|
## What it does not do yet
|
||||||
|
|
||||||
|
- **No collision.** A tilemap is a picture; figures walk through it. Whether it should contribute
|
||||||
|
colliders is a question of its own and is not decided.
|
||||||
|
- Nothing fills an area by flooding it, and there is no picking up a tile off the map.
|
||||||
|
- The cells are stored one entry per painted cell (`"x,y"` → autotile), uncompressed. Big maps
|
||||||
|
therefore make big project files; the format leaves room to pack them later.
|
||||||
+1
@@ -10,6 +10,7 @@
|
|||||||
- [Sounds](Sounds)
|
- [Sounds](Sounds)
|
||||||
- [Animations](Animations)
|
- [Animations](Animations)
|
||||||
- [Autotiles](Autotiles)
|
- [Autotiles](Autotiles)
|
||||||
|
- [Tilemaps](Tilemaps)
|
||||||
- [State machines](State-machines)
|
- [State machines](State-machines)
|
||||||
- [Shaders](Shaders)
|
- [Shaders](Shaders)
|
||||||
- [Particles](Particles)
|
- [Particles](Particles)
|
||||||
|
|||||||
Reference in new issue
Block a user