From 70697cce9ae8ecaf4cd1bfbfd864f99fcd087ccc Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Sun, 4 Oct 2026 18:44:21 +0200 Subject: [PATCH] Describe sprite preview, machine ports and shared local machines The wiki catches up with the last four commits in the code repo. - Concepts: new paragraph Sprite preview. Architecture lists SpritePreview.ts and previewFit.ts. - State-machines: a Start input or a Variables node can name the machine it carries. A called behavior now shares the caller's local machines instead of starting empty. - Behaviors: the warning at Run behavior for a different machine, the shared local machines and Get world position in the data nodes. - Scene-editing: a half sentence on Get world position. - Development: e2e sum 1744. Co-Authored-By: Claude Opus 5.5 --- Architecture.md | 4 +++- Behaviors.md | 12 ++++++++---- Concepts.md | 7 +++++++ Development.md | 2 +- Scene-editing.md | 3 ++- State-machines.md | 28 ++++++++++++++++++++-------- 6 files changed, 41 insertions(+), 15 deletions(-) diff --git a/Architecture.md b/Architecture.md index 3e0ed5b..c94d2bb 100644 --- a/Architecture.md +++ b/Architecture.md @@ -281,7 +281,9 @@ src/editor/inspector/ Inspector panel, host (what a section builds into, and `e every variable), shapeSection (the shapes of a vector drawing: the list, the four order buttons and the style of the picked one, #129; Fill type and Line type with the stop bar, the picked stop's rows or the texture, #186), portSection, aspectAction, - objectQueries/vectorRows/parentRow, cutSection (the Nine-slice and Sorting rows, shared by the frame and the drawing inspectors, #308) + objectQueries/vectorRows/parentRow, cutSection (the Nine-slice and Sorting rows, shared by the frame and the drawing inspectors, #308), + SpritePreview (a sprite as `syncSpriteMesh` draws it in the scene) + previewFit (whole device + pixels per unit, the corner on one) src/editor/panels/ AssetPanel, assetGroups (groups by type and their order, #176), assetIcons (one SVG per asset type in icons/, shared by the panel, the + menu, the tabs and Scene objects, plus the List, Game, Plugins and User management tabs' own four, which stand for no asset), diff --git a/Behaviors.md b/Behaviors.md index 613b319..8f072da 100644 --- a/Behaviors.md +++ b/Behaviors.md @@ -220,7 +220,7 @@ the inspector). the given object, nearer levels first, the order `Child by name` searches in), `Children` (the children of an object; with **recursive** every object below it, level by level) and `Objects of asset` (every instance of the chosen asset, spawned ones included), - `Variables`, `Properties` (every property row of the target's type, the transform rows `scale`, `rotation`, `mirrorX`, `mirrorY` included — how a graph turns a sprite), `Get position` (local and world), `Get Z` (the object's own `z` and `world z`, summed up with its parents'), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`/`State machine`/`Sound`/`Scene`/`Animation`/`Asset`, `Texture size` (a texture's, frame's or pixel art's own size in pixels, 0 for none — wired into a sprite's `width`/`height`, a picture shows pixel for pixel), `Add/Subtract/Multiply/Divide`, + `Variables`, `Properties` (every property row of the target's type, the transform rows `scale`, `rotation`, `mirrorX`, `mirrorY` included — how a graph turns a sprite), `Get position` (local and world), `Get world position` (only the world `x`/`y`), `Get Z` (the object's own `z` and `world z`, summed up with its parents'), `Is enabled`, literals `Number`/`Text`/`Boolean`/`Texture`/`State machine`/`Sound`/`Scene`/`Animation`/`Asset`, `Texture size` (a texture's, frame's or pixel art's own size in pixels, 0 for none — wired into a sprite's `width`/`height`, a picture shows pixel for pixel), `Add/Subtract/Multiply/Divide`, `Min/Max/Abs/Sqrt/Clamp`, `Lerp` (from a to b as t runs from 0 to 1, t held in 0…1; the setting *Ease* bends the way: *Linear*, *In* starts slow, *Out* ends slow, *In-out* both, all quadratic, or *Curve*, which follows the [curve asset](Curves) picked in the *Curve* row and may overshoot b; @@ -372,7 +372,8 @@ the inspector). removing takes the edges hanging on it in *every* graph that calls the behavior. A call is a **call, not an inlining**: the nested graph runs in its own pulse cache with a fresh copy of its variable defaults, so values travel in and out through the ports and nothing else. That makes a - call repeatable and keeps a nested behavior free of hidden state. A behavior that would run + call repeatable and keeps a nested behavior free of hidden state. The one exception are local + state machines, which belong to the instance running the call (see *Keep* below). A behavior that would run itself raises an error instead of looping; there is one start and one end node per behavior. A behavior **without a start node** is called from its **On start** nodes instead, all of them in graph order, as when it starts on its own. It takes no inputs then, but its end node still hands @@ -427,7 +428,8 @@ the inspector). a call inside a call has one per outer node. It follows today's declarations: a variable added since starts at its default, a removed one is dropped, and one whose kind changed starts over. The kept value lives as long as the object does in the run and is never saved. Local state - machines of the called behavior are not kept, they start in no state every call. For state + machines need no Keep: a call reads and moves the states of the instance running it, so they + carry over from call to call anyway (see *State machines*). For state shared between objects, use a Variables asset or a behavior object in the scene (see *Cross-object state* below). Three ways in and out of a call: **drag** a behavior out of the asset panel onto the graph and it @@ -437,7 +439,9 @@ the inspector). stays in the same tab, and the trail belongs to that tab: a behavior opened any other way comes up in a tab of its own, without a trail, and switching back brings the trail back with it. And a call that cannot do anything **says so on the node** — no behavior picked, the behavior deleted, - or neither a start node nor an On start in it. The last one is the one worth naming: without + or neither a start node nor an On start in it. It also warns when a State machine input is set + to another machine than the one its Start input carries (see *State machines*). The missing + start is the one worth naming: without either there is no way in, so the call is skipped and the flow simply carries on at its `exec` output. A missing *end* node is not a defect — a call is always synchronous, "done" is the nested flow running out, and the end node only decides what comes back. diff --git a/Concepts.md b/Concepts.md index 5302da9..96754e8 100644 --- a/Concepts.md +++ b/Concepts.md @@ -288,6 +288,13 @@ without rows is left out. with a header of its own (*Layers*, *Frames*, *Shapes*) folds with a click on that header, clear of its plus, and takes the notes and buttons under it along. +**Sprite preview.** A sprite, placed or as an asset, ends its inspector with a **Preview** group: +the sprite as the scene draws it, with its picture or frame, nine-slice, tint and shader, on the +scene's background. A placed sprite shows its own overrides. An edit shows at once, also while a +value is dragged, and a shader with a Time node keeps moving while the group is open. The picture +is fitted into the group and magnified by whole device pixels at most 8 times, so pixel art stays +sharp; a sprite larger than the group is shrunk to fit. + The asset panel and **Scene objects** share the left column; the divider between them drags the height from one to the other, and dragged all the way either keeps its header. The split is a share of the column, so a taller or shorter window keeps it. diff --git a/Development.md b/Development.md index c79f004..44290db 100644 --- a/Development.md +++ b/Development.md @@ -18,7 +18,7 @@ php tests/php/accounts.test.php # the API's accoun # has and the web server's container has not (SKIP there) php tests/php/snapshots.test.php # the snapshot store (#231), the same way php tests/php/releases.test.php # the release store (#266), the same way -node tools/seed-e2e.mjs && node tools/e2e.mjs # 1742 editor checks, screenshots in /tmp/gm-e2e-*.png +node tools/seed-e2e.mjs && node tools/e2e.mjs # 1744 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 eff2e12..c609bf5 100644 --- a/Scene-editing.md +++ b/Scene-editing.md @@ -179,7 +179,8 @@ scene opens fully collapsed — which rows are open is view state of the panel, the project — and selecting an object anywhere (viewport, `Add collider…`, `Create container`) unfolds the way down to it and scrolls it into view, so a selection is never hidden. In play mode entities carry the same `parent` link: moving a parent entity moves its subtree, -`Destroy` removes it. `Get position` exposes both the local and the world coordinates. +`Destroy` removes it. `Get position` exposes both the local and the world coordinates, +`Get world position` only the world ones. **Groups in the hierarchy (#241).** A group is a folder for top-level objects — 100 crates under one foldable row, say. It is editor looks only: parents, positions and behaviors stay exactly as diff --git a/State-machines.md b/State-machines.md index 2ba2695..a64c433 100644 --- a/State-machines.md +++ b/State-machines.md @@ -95,15 +95,25 @@ state lives in the *machine* — so nothing cleans up there, and the edge stays lost, though: the behavior's node shows the key as a **ghost port** with the edge dashed in the warning colour, and *Disconnect* on its right-click menu removes it. Put the state back and the edge works again. Which machine the -ports belong to is read off the graph, not off a running value — the inline pick, or a -`State machine` literal wired in; a variable or an `Item at` feeding the input yields no state -ports, exactly as `Run behavior` knows its behavior only as a setting. That machine is still not +ports belong to is read off the graph, not off a running value — the inline pick, a +`State machine` literal wired in, or an input of the behavior's **Start** node of the kind +State machine with a machine picked next to its name in the inspector. A variable of that kind +read by a `Variables` node works the same way: the machine is picked next to the variable's row. +The pick only names the states, the value the variable holds at run time is not checked against +it. An `Item at` or anything else feeding the input yields no state ports, exactly as +`Run behavior` knows its behavior only as a setting. That machine is still not stuck: `Set state by name` names the state as text, `State branch` leaves along `else`, and `Current state` answers as always. The price is spelled out, not hidden — a name is typed, so a typo is possible where a port made it impossible, which is what `not found` is there for. Picking another machine orphans the edges of the old states, the way any setting that decides a node's ports does. +A Start input with a machine picked has that machine **picked in advance** on every node running +the behavior. Another pick there is allowed, but the node warns (`"machine" expects "Doors"`): +the called graph asks for the states of its own machine, so with another one every state would +read as never entered. A **local** machine handed in is the caller's: the called graph reads and +moves the very state the calling instance holds (see *The current state while playing* below). + Two more read it as **data** rather than as a flow, both pure readers (no exec ports, answered whenever they are asked): @@ -148,9 +158,9 @@ The rules, all of them worth knowing before wiring a graph on them: whose events keep moving the machine stops the run with an endless-loop error, like the step limit. - Who hears it follows the **scope**: a local machine is heard by the instance that moved it alone, a global one by **every** instance with a matching event node — which is the point of sharing a - state. A move a nested `Run behavior` makes to a *local* machine dies with the call and is not - reported, even where the called behavior keeps variables (see *Behaviors*); a move it makes to a - global one is a move like any other. + state. A move made inside a `Run behavior`, however deep, counts as a move of the instance that + runs the call: its own `On state entered` and `On state exited` hear it. The event nodes of the + called graph itself stay silent, like every event there. ## The current state while playing @@ -164,8 +174,10 @@ gone the moment the run stops. Play repaints the tab the way it repaints the Beh **A local machine's current state is the behavior instance's own**, kept beside its variables (`NodeContext.states`, machine id → state id): two crates running the same behavior are in -states of their own, and a behavior run through `Run behavior` starts in no state, -and again with every call. A **global** one is kept for the run instead (`SharedState.states`, built +states of their own. A behavior run through `Run behavior` has no states of its own: it reads and +moves those of the instance that runs the call, however deep, so a state one call sets is still set +in the next one, and the call begins in the start state like the instance does. Two characters +running the same behavior therefore stay apart, each with its calls. A **global** one is kept for the run instead (`SharedState.states`, built once per run in `core/graph/sharedState.ts`) and every instance reads and moves that one — which bag a node touches is decided by the machine's scope, not by the node (`stateBag`). Neither survives a stop: both begin the next Play at the start state again, or in no state where there is