Describe Animation playing and Animation length (#233)

- Animations: what the two nodes answer in each playback, what None
  asks for, and where the rule lives.
- Behaviors: both nodes in the node list.
- Development: 1615 editor checks.
admin committed 2026-10-01 10:57:44 +02:00
1 parent 9d9e73e243
commit c02aa7e531
3 files changed
+28 -2

No files matched your search

+26
@@ -150,6 +150,32 @@ values stay those of its first start, so a relative row does not add up. Played
every frame, an animation never gets past its first frame — use it on a single event such as a key every frame, an animation never gets past its first frame — use it on a single event such as a key
press. press.
**Asking whether it still runs.** `Animation playing` is a data node with the inputs *object* and
*animation* and three answers, read at the moment they are asked for — right after a `Play animation`
in the same pulse it already says yes:
- **playing** is on while the animation runs. A paused one does not run, and neither does one that
`Stop animation` ended or is fading out. A *Once (reset)* pass blending back to where it started still
runs: it still shows. In the frame `On animation finished` fires for a *Once* animation it is off.
- **progress** is where the playhead stands on the timeline, 0 to 1, in every playback. On *Loop* it
starts over with each pass; on *Ping-pong* it rises to 1 at the last key and falls again on the way
back.
- **time left (s)** is the time until the playhead reaches the end of its pass: the end of the
animation for the two *Once* modes, with *blend out (s)* added for *Once (reset)*; the end of the
round on *Loop*; the next turn on *Ping-pong*. A paused animation keeps its place and its time left.
Leave *Animation* at None to ask for whatever runs on the object: the animation started last among
those running, or the one started last at all when none runs. With no object, or an animation that is
not on it, the answers are off, 0 and 0. So "no new attack while the swing runs" is one `Branch` on
`playing` instead of a timer that has to repeat the animation's length.
`Animation length` gives the seconds one pass takes, from the timeline and the frame rate alone — for
a cooldown that is meant to be longer than the animation (`length + 0.1`).
Under the hood: the rule is `playerStatus` and `leadingPlayer` in `src/core/model/animation.ts`; the
nodes reach the animation through `GraphLookup.animation`, which hands a run only the frame count,
the frame rate and the playback.
**It runs in play only.** The Scene and Container tabs show the objects as stored; the edit mode **It runs in play only.** The Scene and Container tabs show the objects as stored; the edit mode
has no clock. The fastest way in is right-clicking a texture → **Create animation from texture**, has no clock. The fastest way in is right-clicking a texture → **Create animation from texture**,
which makes a sprite of the texture and an animation over it with a `texture` row keyed frame by which makes a sprite of the texture and an animation over it with a `texture` row keyed frame by
+1 -1
@@ -228,7 +228,7 @@ playing use the same two words.
normalized direction, so diagonals are not faster), `Compare` (two numbers count as equal up to normalized direction, so diagonals are not faster), `Compare` (two numbers count as equal up to
float noise, a relative 1e-9, so `time - late == 6` holds without a `Round`; `Equals` does the float noise, a relative 1e-9, so `time - late == 6` holds without a `Round`; `Equals` does the
same for two numbers, and for a number against a typed text that is a number, like `130`), `And/Or/Not`, `Key is down`, same for two numbers, and for a number against a typed text that is a number, like `130`), `And/Or/Not`, `Key is down`,
`Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Get velocity`, `Current state` (name and id), `Is in state` (one boolean per state) · `Mouse position`, `Mouse button`, `Is hovered`, `Is touching`, `Animation playing` (whether an animation runs on an object, its progress and time left) and `Animation length` (see [Animations](Animations)), `Get velocity`, `Current state` (name and id), `Is in state` (one boolean per state) ·
actions `Move` (dx, dy, scale, dt — connect an event's dt for per-second movement; in the object's own axes, which a turned parent turns too; with *Collide with solids* it slides along what it hits, less friction, and outputs `blocked x/y`, `hit` and the contact `normal x/y`, see [Colliders](Colliders)), `Set position`, `Spawn` actions `Move` (dx, dy, scale, dt — connect an event's dt for per-second movement; in the object's own axes, which a turned parent turns too; with *Collide with solids* it slides along what it hits, less friction, and outputs `blocked x/y`, `hit` and the contact `normal x/y`, see [Colliders](Colliders)), `Set position`, `Spawn`
(asset input + x/y/z, optional parent → outputs the new object; the asset is picked on the node or arrives over an edge, from an `Asset` node, a variable or a list item. A pick saved while it was still a setting reads as that input's value, so older projects spawn as before; the same holds for the asset of `Is asset`), `Destroy` (removes the object with everything below it; with **keep children** on, only the object goes: its children move up to its parent (top level when it had none) and keep their place, rotation and size in the world, and the output `children` is the list of them, nearest level only. Its behaviors and its rigidbody go with it either way. In the editor run, children that are content of a placed container move up for the run only: their place belongs to the template, so the scene does not learn it), `Emit` (starts a particle source over, see *Particles*), (asset input + x/y/z, optional parent → outputs the new object; the asset is picked on the node or arrives over an edge, from an `Asset` node, a variable or a list item. A pick saved while it was still a setting reads as that input's value, so older projects spawn as before; the same holds for the asset of `Is asset`), `Destroy` (removes the object with everything below it; with **keep children** on, only the object goes: its children move up to its parent (top level when it had none) and keep their place, rotation and size in the world, and the output `children` is the list of them, nearest level only. Its behaviors and its rigidbody go with it either way. In the editor run, children that are content of a placed container move up for the run only: their place belongs to the template, so the scene does not learn it), `Emit` (starts a particle source over, see *Particles*),
`Play sound` / `Stop sound` (see [Sounds](Sounds): the asset says how loud and whether it loops, the node's `Play sound` / `Stop sound` (see [Sounds](Sounds): the asset says how loud and whether it loops, the node's
+1 -1
@@ -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) # 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/snapshots.test.php # the snapshot store (#231), the same way
php tests/php/releases.test.php # the release store (#266), 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 # 1613 editor checks, screenshots in /tmp/gm-e2e-*.png node tools/seed-e2e.mjs && node tools/e2e.mjs # 1615 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