A shared value is an asset you point at, not a word on a declaration (#91)

Behaviors: the Variables node's source setting, and Shared state rewritten --
a Variables asset instead of the coin counter's detour through Object by name,
including what the run does not write back and what the inspector shows live.

Concepts: the new asset type in the glossary and in the list of the unplaceable;
a machine's Scope replaces the flat claim that every instance keeps its own.

Architecture: sharedState and Scope in the file map.

State machines: the Scope section (#59).
root committed 2026-09-14 18:06:19 +02:00
1 parent 7b5974b37c
commit 6ed46a9920
4 files changed
+30 -8

No files matched your search

+2 -1
@@ -45,10 +45,11 @@
```
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), animation (which frame at time t), graphLookup (what a graph sees of the project: behaviors to run, state machines to read)
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), animation (which frame at time t), graphLookup (what a graph sees of the project: behaviors to run, state machines to read), Scope (local or shared for the whole run)
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)
src/core/commands/ undoable commands, CommandStack, SetBagValueCommand base, 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),
sharedState (what a run shares: a bag per Variables asset, the state of a global machine),
stateMachine (the State node, a machine's states, the ports they become), variablePorts, propertyPorts,
lookup (what a node may look up: behaviors, state machines), keyRepeat; nodes/{events,flow,data,action,variable}/<Name>.ts
src/core/particles/ nodes (the catalogue), compile (a graph → one function per output),
+16 -4
@@ -16,6 +16,10 @@ shown on the node and editable in the inspector.
first, so one node swaps two variables. `count = count + 1` is one `Variables` node feeding its
own output through an `Add` back into its input. The ports are picked from the behavior's
variables rather than named, they take their type from the declaration, and renaming a variable carries them (and their edges) along.
The node's **Variables** setting, above the list, decides *whose* declarations those are: empty
means the behavior's own, a **Variables asset** means one value per key for the whole run (see
below). Switching it changes nothing else — a port whose name the new source does not declare is
marked, not dropped.
`Properties` is the same node for the
object it is handed (fixed input `object`). Its **Type** setting, above the list, decides what
`+ Property` offers: *Sprite*, *Text*, *Collider*, *Camera* and *Container* offer their fields,
@@ -179,10 +183,18 @@ shown on the node and editable in the inspector.
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.
- **Cross-object state**: a behavior's variables *are* the properties of its object, so another
graph reads and writes them with `Object by name` → `Properties` of type *Behavior*. That is
how the example game keeps one coin counter for the whole run: picking a coin up is one node,
`coins` fed through `Add` back into itself.
- **Shared state**: a **Variables asset** is a list of declarations and nothing else — no graph, no
position, never placed. A `Variables` node naming it reads and writes one value per key for the
whole run, however many graphs point at it and however many objects wear them. The values start
at the declared ones every time Play does and are never written back, so playing in the editor
cannot change the project. While a run is on, the asset's inspector shows the values as they
stand and an edit there reaches the run at once — the declaration underneath keeps the value the
next run starts from. Two Variables assets may declare the same name without meeting: the
asset is the key, not the name. A coin counter is one such asset with `coins` in it; picking a
coin up is one node, `coins` fed through `Add` back into itself.
- **Cross-object state** the other way: a behavior's own variables *are* the properties of its
object, so another graph reads and writes them with `Object by name` → `Properties` of type
*Behavior*. That reaches one named object, where a Variables asset belongs to no object at all.
- **Mouse**: the Game tab feeds the pointer to the world in world units. `On click` fires for
the behavior whose owner was hit — colliders decide when there are any, otherwise the sprites
and texts of the owner and everything below it, so a button made of a box plus a label works
+4 -3
@@ -1,9 +1,9 @@
| Term | Meaning |
|---|---|
| **Asset** | Named thing with a type (`scene`, `camera`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`, `animation`, `statemachine`, `shader`, `font`, `particles`) and typed properties. Assets have **no position**. |
| **Texture** | An uploaded image. Scenes, textures, animations, state machines, shaders and fonts are the six types that cannot be placed: a texture has no position, it is *worn* by a sprite through the ordinary property `texture`. See [Textures](Textures). |
| **Asset** | Named thing with a type (`scene`, `camera`, `sprite`, `text`, `behavior`, `collider`, `container`, `texture`, `animation`, `statemachine`, `shader`, `font`, `particles`, `variables`) and typed properties. Assets have **no position**. |
| **Texture** | An uploaded image. Scenes, textures, animations, state machines, shaders, fonts and Variables assets are the seven types that cannot be placed: a texture has no position, it is *worn* by a sprite through the ordinary property `texture`. See [Textures](Textures). |
| **Font** | An uploaded typeface, worn by a text through the ordinary property `font`; no font means the editor's own. Uploaded and picked exactly like a texture, and stored the same way. See [Fonts](Fonts). |
| **State machine** | A named set of **states**, edited as nodes in its own tab. A behavior puts it into a state with `Set state`, moves it along a named transition with `State transition` and branches on it with `State branch`; each behavior instance keeps its own current state. See [State machines](State-machines). |
| **State machine** | A named set of **states**, edited as nodes in its own tab. A behavior puts it into a state with `Set state`, moves it along a named transition with `State transition` and branches on it with `State branch`. Its **Scope** says where the current state lives: **Local** gives every behavior instance one of its own, **Global** keeps a single one for the whole run. See [State machines](State-machines). |
| **Shader** | A node graph that becomes the GLSL a sprite is drawn with, edited in its own tab. A sprite wears it through `shader`; the numbers the graph declares are rows below that dropdown. See [Shaders](Shaders). |
| **Particles** | A node graph that says what *one* particle is as a function of its age and of a random number drawn once for it — no forces, no simulation. Unlike the other graph assets it **is** placeable: the object made from it is a source, whose rows say how many go out and for how long they live. Because a particle is a formula, the same second always draws the same picture and the tab's slider can jump to any of them exactly. See [Particles](Particles). |
| **Text** | An asset that draws a line of text (`text`, `font`, `fontSize`, `color`, `align`) — titles, HUD readouts, button labels. Its content is an ordinary property, so a `Properties` node of type Text with `text` switched to Set changes it while playing. |
@@ -12,6 +12,7 @@
| **Container** | A reusable group ("prefab"): the asset holds a **template** of objects (asset link, local position, overrides, parent links) centred on its origin. Placing a container stores the container object **and nothing else** — its content is resolved from the template on every read, so a template edit reaches every instance at once. **Create container** turns the current selection into such an asset: the selected objects move into the template and the scene keeps one placed container at their centre (world positions unchanged). The template is edited in the **Container** tab (see [Containers](Containers)) or from an instance through **Apply to container**. |
| **Selection** | One or more scene objects. Click selects, `Ctrl`/`Shift`-click adds/removes, dragging on empty scene space draws a rubber band (objects whose position lies inside are selected). Dragging a selected object moves the whole selection; `Delete` removes it; the inspector offers **Create container** and **Remove** for several objects. |
| **Behavior** | Logic as an asset: a **node graph** plus the **variables** it declares. Placed like anything else (a diamond symbol in the scene); its parent is the **owner** the graph acts on. |
| **Variables asset** | Declarations and nothing else — no graph, no position, never placed. A `Variables` node naming it reads and writes **one value per key for the whole run**, however many graphs point at it. The values start at the declared ones on every Play and are never written back, so playing cannot change the project; while a run is on the asset's inspector shows them live and an edit there reaches the run at once. Two such assets may declare the same name without meeting. See [Behaviors](Behaviors). |
| **Run in editor** | A switch only behaviors have (default off). With it on the behavior keeps running while the project is **not** playing — and unlike Play it then edits the *saved* model: what it moves or writes is a real change, autosaved and not on the undo stack. See *Running without Play*. |
| **Collider** | A shape (box or circle) for collision detection, placed as a **child** of the object it belongs to (its *body*). Drawn as a green outline in edit mode only. |
| **Override** | A per-object value that replaces the asset's value for one property. Only changed keys are stored; the inspector paints their label gold and offers a reset (↺) and an **apply** (↑) that writes the value into the asset instead. Both appear only where the value really differs from its base — an override carrying the asset's own value is none. |
+8
@@ -7,6 +7,14 @@ Double-clicking the asset (or creating it) opens the tab, like every asset with
only selects. Right-click on its canvas offers a `State` and
nothing else, named by its number (`State 3` for the third); no behavior palette offers one.
## Scope
A machine's **Scope** is a dropdown in its own inspector, since the machine is the asset and its
current state hangs off its id. **Local** (the default) gives every behavior instance a current
state of its own — two doors open independently. **Global** keeps one current state for the whole
run, so every behavior using the machine sees the same one, and a second behavior can branch on a
state it never set itself. A global machine starts each run in no state at all, like a local one.
## Transitions
A **transition** is an arrow from one state to another. Right-click a state, choose