Describe On fixed update, sound preloading and timed FX changes
- Behaviors: On fixed update in the node list and the event order, and a section Fixed update. - Physics: the order of On fixed update against the engine steps. - Sounds: sounds are loaded ahead, and Set sound FX parameters has a time that ramps on the audio clock, with the sweep as an example. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent
f4de42b406
commit
e02851255c
3 files changed
+36
-12
No files matched your search
+13
-3
@@ -212,7 +212,7 @@ the inspector).
|
|||||||
- **Keys ignore case**: a letter is the same key with or without Shift or Caps Lock, so a key node
|
- **Keys ignore case**: a letter is the same key with or without Shift or Caps Lock, so a key node
|
||||||
set to `a` also hears `A`, and a key picked with Shift held is stored lower-case. A key is let go
|
set to `a` also hears `A`, and a key picked with Shift held is stored lower-case. A key is let go
|
||||||
under the name it went down as, even when Shift changes in between (`1` down, `!` up).
|
under the name it went down as, even when Shift changes in between (`1` down, `!` up).
|
||||||
- **Node set** (`src/core/graph/nodes/`, one file per node): events `On start`, `On enable`, `On update`, `On disable`, `On destroy` (see *Lifecycle*), `On key down/up/held`,
|
- **Node set** (`src/core/graph/nodes/`, one file per node): events `On start`, `On enable`, `On update`, `On fixed update` (see *Fixed update*), `On disable`, `On destroy` (see *Lifecycle*), `On key down/up/held`,
|
||||||
`On timer` (fires on the first frame past each due time; `late` says by how much, so `time - late` is exactly a multiple of the interval), `On collision enter/stay/exit` (stay every frame, or with an interval; while playing enter and stay tell normal, impulse and point of the contact; on a trigger tile of a tilemap all three tell `tile` and `cell x/y`, once per area or with the input `per cell` once per cell, see [Tilemaps](Tilemaps#trigger-tiles)), `On click` (the mouse pressed on the owner — anywhere when it has none), `On mouse wheel` (the wheel turned over the owner, or anywhere without one; `delta` in notches, positive downwards, a trackpad gives fractions), `On animation finished`, `On state entered`, `On state exited` (one output per state, see *State machines*), `On behavior` (another behavior heard as an event, see *A behavior as an event* below), `Start` · flow `Branch`, `Sequence`, `Repeat`, `For each`, `Find` … `Match`, `Run behavior`, `End`, and the four that move or follow a state machine, `State branch`, `Set state`, `Set state by name`, `State transition` (see *State machines*) · data `Self`, `Owner`, `Parent`, `Object by name`, `Child by name` (the nearest object of that name below the given one: its children, then their children, and so on; within a level the first in hierarchy order, nothing when none is called so), `Name of`,
|
`On timer` (fires on the first frame past each due time; `late` says by how much, so `time - late` is exactly a multiple of the interval), `On collision enter/stay/exit` (stay every frame, or with an interval; while playing enter and stay tell normal, impulse and point of the contact; on a trigger tile of a tilemap all three tell `tile` and `cell x/y`, once per area or with the input `per cell` once per cell, see [Tilemaps](Tilemaps#trigger-tiles)), `On click` (the mouse pressed on the owner — anywhere when it has none), `On mouse wheel` (the wheel turned over the owner, or anywhere without one; `delta` in notches, positive downwards, a trackpad gives fractions), `On animation finished`, `On state entered`, `On state exited` (one output per state, see *State machines*), `On behavior` (another behavior heard as an event, see *A behavior as an event* below), `Start` · flow `Branch`, `Sequence`, `Repeat`, `For each`, `Find` … `Match`, `Run behavior`, `End`, and the four that move or follow a state machine, `State branch`, `Set state`, `Set state by name`, `State transition` (see *State machines*) · data `Self`, `Owner`, `Parent`, `Object by name`, `Child by name` (the nearest object of that name below the given one: its children, then their children, and so on; within a level the first in hierarchy order, nothing when none is called so), `Name of`,
|
||||||
`Is asset` (is this object an instance of asset X — how you tell what you collided with),
|
`Is asset` (is this object an instance of asset X — how you tell what you collided with),
|
||||||
and the plural lookups, each handing on a `list` of objects taken at that moment (destroyed
|
and the plural lookups, each handing on a `list` of objects taken at that moment (destroyed
|
||||||
@@ -643,8 +643,8 @@ the inspector).
|
|||||||
Off and on again within one frame is no edge: neither event fires.
|
Off and on again within one frame is no edge: neither event fires.
|
||||||
- **Event order**: a frame goes event kind by event kind, each across every behavior: `On disable`,
|
- **Event order**: a frame goes event kind by event kind, each across every behavior: `On disable`,
|
||||||
`On enable`, `On key down`, `On key up`, `On key held`, `On timer`, `On collision enter`,
|
`On enable`, `On key down`, `On key up`, `On key held`, `On timer`, `On collision enter`,
|
||||||
`On collision stay`, `On collision exit`, `On animation finished`, `On click`, `On mouse wheel`
|
`On collision stay`, `On collision exit`, `On animation finished`, `On click`, `On mouse wheel`,
|
||||||
and last `On update`. So a key event of any behavior runs before the first `On update`. The state
|
`On fixed update` (as many steps as are due) and last `On update`. So a key event of any behavior runs before the first `On update`. The state
|
||||||
events a kind caused follow right after it. New instances get all their `On start` and then all
|
events a kind caused follow right after it. New instances get all their `On start` and then all
|
||||||
their `On enable`, a finished world all `On disable` and then all `On destroy`. Within one kind
|
their `On enable`, a finished world all `On disable` and then all `On destroy`. Within one kind
|
||||||
every event node has the setting *Order*, a whole number, 0 by default: a lower order runs first,
|
every event node has the setting *Order*, a whole number, 0 by default: a lower order runs first,
|
||||||
@@ -652,6 +652,16 @@ the inspector).
|
|||||||
hierarchy order, top to bottom as the hierarchy shows it right now (a spawned object stands where
|
hierarchy order, top to bottom as the hierarchy shows it right now (a spawned object stands where
|
||||||
it was put), and within one graph in the order the nodes were made. The node shows `Order: 5`
|
it was put), and within one graph in the order the nodes were made. The node shows `Order: 5`
|
||||||
only when the order is not 0.
|
only when the order is not 0.
|
||||||
|
- **Fixed update**: `On fixed update` runs 60 times per second of game time, each time with `dt`
|
||||||
|
exactly 1/60, however long the frames are. A frame at 120 Hz has a step only every other time,
|
||||||
|
a frame at 30 Hz has two, one after the other, before `On update`. A node inside a step that
|
||||||
|
reads `dt` (`Time`, `Move`, `Apply force`) gets 1/60 too. So something that counts up or down
|
||||||
|
in fixed amounts per step (a pitch sweep, a countdown) comes out the same on every run and every
|
||||||
|
machine, where adding up a frame's `dt` wobbles with the frame rate. It counts its steps by the
|
||||||
|
same rule as the physics, so it runs exactly as often as the physics steps. The time scale slows
|
||||||
|
it down like everything else, and while the game is paused it stands still, also for a behavior
|
||||||
|
that runs while paused. After a hitch it catches up 8 steps at most and drops the rest, as the
|
||||||
|
physics does. Code: `fixedSteps` in `src/core/time.ts`.
|
||||||
- **Runtime**: `BehaviorSystem` creates one `GraphInterpreter` per behavior entity; graphs only
|
- **Runtime**: `BehaviorSystem` creates one `GraphInterpreter` per behavior entity; graphs only
|
||||||
touch world entities. A runaway pulse throws (see *Loops* for the two limits) and stops play
|
touch world entities. A runaway pulse throws (see *Loops* for the two limits) and stops play
|
||||||
with a notice.
|
with a notice.
|
||||||
+4
@@ -71,6 +71,10 @@ fast for a while after. What the game view shows of a dynamic body lies between
|
|||||||
so it moves evenly on any display — which also means it is drawn up to one step (1/60 s) behind the
|
so it moves evenly on any display — which also means it is drawn up to one step (1/60 s) behind the
|
||||||
engine.
|
engine.
|
||||||
|
|
||||||
|
`On fixed update` (see [Behaviors](Behaviors)) runs exactly as often as the engine steps. It does
|
||||||
|
not run in between the steps, though: the engine takes all of a frame's steps first, the behaviors
|
||||||
|
come after. A force set in `On fixed update` therefore acts from the next frame's steps on.
|
||||||
|
|
||||||
## Collision events
|
## Collision events
|
||||||
|
|
||||||
`On collision enter/stay/exit` and `Is touching` come from the engine's contact pairs.
|
`On collision enter/stay/exit` and `Is touching` come from the engine's contact pairs.
|
||||||
|
|||||||
+19
-9
@@ -38,7 +38,7 @@ so what the inspector plays is what the graph will play.
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| **Play sound** | Starts the sound named by its `sound` input. `volume` **multiplies** the asset's own volume (1 leaves it alone), `loop` is a choice of three (*From the asset*, *On*, *Off*), `detached` says the voice belongs to nobody, `fx` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound); 0 where nothing was played. |
|
| **Play sound** | Starts the sound named by its `sound` input. `volume` **multiplies** the asset's own volume (1 leaves it alone), `loop` is a choice of three (*From the asset*, *On*, *Off*), `detached` says the voice belongs to nobody, `fx` names a [sound FX](#sound-fx) to play it through, and `fx params` takes values for that FX's [parameters](#parameters). Its output `voice` is the number of this one play, for changing it [while it plays](#changing-a-playing-sound); 0 where nothing was played. |
|
||||||
| **Stop sound** | Silences every voice of the sound it names, however many plays started it — a loop included. Naming no sound silences everything the run is playing. |
|
| **Stop sound** | Silences every voice of the sound it names, however many plays started it — a loop included. Naming no sound silences everything the run is playing. |
|
||||||
| **Set sound FX parameters** | Hands new values to the [parameters](#changing-a-playing-sound) of one voice that is already playing, by the `voice` number its `Play sound` gave out. |
|
| **Set sound FX parameters** | Hands new values to the [parameters](#changing-a-playing-sound) of one voice that is already playing, by the `voice` number its `Play sound` gave out, at once or over `time` seconds. |
|
||||||
| **Sound** | A constant sound, for either of the two to work on through an edge. |
|
| **Sound** | A constant sound, for either of the two to work on through an edge. |
|
||||||
|
|
||||||
The division is deliberate: the **asset says what a sound is**, the node only colours it. A
|
The division is deliberate: the **asset says what a sound is**, the node only colours it. A
|
||||||
@@ -61,10 +61,13 @@ overlapping — two coins picked up half a second apart sound like two coins. Th
|
|||||||
sounds as well: playing a loop twice gives two loops, and it is `Stop sound` that ends all of
|
sounds as well: playing a loop twice gives two loops, and it is `Stop sound` that ends all of
|
||||||
them at once.
|
them at once.
|
||||||
|
|
||||||
**It arrives late.** The bytes are fetched and decoded on first use, so the very first play of a
|
**It is loaded ahead.** Every sound of the project is fetched and decoded as soon as the project is
|
||||||
sound starts a moment after the node ran. `Play sound` never waits on the network, and a
|
open, the way images and fonts are, so the first play starts in the frame its node ran. Decoding
|
||||||
`Stop sound` that happens while the bytes are still in flight wins: the sound does not start
|
ahead opens no audio device: that only happens on the first play. A sound that is still loading
|
||||||
afterwards on its own.
|
when it is played — a run started the moment the project opened — starts a moment late instead.
|
||||||
|
`Play sound` never waits on the network, and a `Stop sound` that happens while the bytes are still
|
||||||
|
in flight wins: the sound does not start afterwards on its own. Values a graph sends to such a
|
||||||
|
voice in the meantime are what it starts with.
|
||||||
|
|
||||||
**A sound belongs to the object making it.** A voice is owned by the behavior that started it, and
|
**A sound belongs to the object making it.** A voice is owned by the behavior that started it, and
|
||||||
ends when that behavior is destroyed — a sound is something a thing in the world is *doing*, not a
|
ends when that behavior is destroyed — a sound is something a thing in the world is *doing*, not a
|
||||||
@@ -168,15 +171,22 @@ speed, or a loop that gets quieter.
|
|||||||
- The number is there at once, before the sound has loaded. A number is never given out twice in
|
- The number is there at once, before the sound has loaded. A number is never given out twice in
|
||||||
a run, so a kept number of a voice that has ended reaches nothing, never a newer sound.
|
a run, so a kept number of a voice that has ended reaches nothing, never a newer sound.
|
||||||
- A parameter not in the bundle keeps the value it has now, not its default.
|
- A parameter not in the bundle keeps the value it has now, not its default.
|
||||||
- Every value **glides** there in about 0.1 s (`setTargetAtTime`, 30 ms time constant), so a jump
|
- With `time` at 0, every value **glides** there in about 0.1 s (`setTargetAtTime`, 30 ms time
|
||||||
does not click. A value that did not change is not sent again, so running it every frame costs
|
constant), so a jump does not click. A value that did not change is not sent again, so running it
|
||||||
next to nothing.
|
every frame costs next to nothing.
|
||||||
|
- **`time`** is how many seconds the values take to get there, evenly: a pitch in semitones, a
|
||||||
|
Gain in dB, a band and a Mix in what they hold. It runs on the clock of the sound itself, not on
|
||||||
|
the game's, so a sweep sounds the same every time, also the first. For a sweep, set it once
|
||||||
|
right after `Play sound`: `On enable` → `Play sound` → `Set sound FX parameters` (`voice` from
|
||||||
|
`Play sound`, `pitch` -64, `time` 1.28) falls by 50 semitones a second. Counting a value down
|
||||||
|
in `On update` or `On fixed update` instead runs on game time, and the sound drifts away from it.
|
||||||
|
- A new change starts from where the voice is right now; whatever was still under way is dropped.
|
||||||
- **A pitch changes by speed here.** A pitched copy cannot be worked out again in the middle of a
|
- **A pitch changes by speed here.** A pitched copy cannot be worked out again in the middle of a
|
||||||
sound, so the voice plays faster or slower to reach the new pitch: it gets that much shorter or
|
sound, so the voice plays faster or slower to reach the new pitch: it gets that much shorter or
|
||||||
longer. On a loop that is not heard; the next play gets its own length again.
|
longer. On a loop that is not heard; the next play gets its own length again.
|
||||||
- A sound that has ended and the `voice` 0 of a play that played nothing are not reached. A
|
- A sound that has ended and the `voice` 0 of a play that played nothing are not reached. A
|
||||||
detached voice is reached like any other. A voice still waiting for its bytes starts with the
|
detached voice is reached like any other. A voice still waiting for its bytes starts with the
|
||||||
new values.
|
new values, and a change with a `time` starts with the voice, so the whole of it is heard.
|
||||||
|
|
||||||
**In the game** the `fx` input of `Play sound` names the asset. An FX that does not compile, or
|
**In the game** the `fx` input of `Play sound` names the asset. An FX that does not compile, or
|
||||||
one that was deleted, plays the sound **dry** rather than not at all: a broken effect should not
|
one that was deleted, plays the sound **dry** rather than not at all: a broken effect should not
|
||||||
|
|||||||
Reference in new issue
Block a user