Which of two events ran first depended on hidden lists: the order of
the behavior instances and the order in which nodes were created. A
graph had no way to say that one On update must run before another,
and an On update of one behavior could run before a key event of
another.
- A frame now runs event kind by event kind over all behaviors: On
disable and On enable, key down, key up, key held, timer, collision
enter, stay and exit, animation finished, click, mouse wheel and at
last On update.
- All 17 event nodes have a field Order, a whole number, default 0,
negative allowed. Within one kind a lower Order runs earlier, no
matter which behavior holds the node.
- On a tie the current hierarchy decides from top to bottom, then the
order in which the nodes were created. The old rule that a behavior
enabled later goes to the end is gone.
- The node shows Order: 5 only when the value is not 0, so existing
graphs look the same.
- The rule lives as pure functions in core/graph/eventOrder.ts. The
interpreter can now fire a single event node.
Changes in existing projects:
- An object destroyed in a frame gets no further event in that frame.
On disable and On destroy still come at the end of the frame.
- New instances get all their On start first, then all On enable.
Teardown runs all On disable first, then all On destroy.
- State events follow right after the event kind that caused them.
- Four unit tests had fixed the old order and are adjusted:
two in behavior-events, one in physics-system, one in key-repeat.
Tests:
- ./check.sh: 191 files, 2784 tests, CHECK EXIT 0.
- GM_STEPS=1-6: ALL PASSED, 280 checks.
- Full e2e run (local): ALL PASSED, 1551 checks, measured.
- tests/core/event-order.test.ts: 11 tests for the rule, the field on
all 17 nodes, order across behaviors, key before update, two nodes
in one graph, On start, hierarchy after late enable and reorder at
runtime, state events, save and load.
- e2e 6z-viii, 2 checks with real mouse and keyboard: Order -1 set in
the inspector shows on the node, and a run logs B, A although B is
lower in the hierarchy.
- Counter-checks: 5 in unit tests and 1 in the browser, all fail as
expected. The originals were restored and checked with cmp.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two On key down fields read in milliseconds now, and say so in their
labels. A number field shows three decimals unless a property asks for
fewer, so both params set decimals: 0 – the field reads 300, not 300.000,
and rounds to whole milliseconds while typing or scrubbing. The clock
behind it still counts seconds; the conversion sits at the one place that
reads the properties.
A held key fired once and then nothing, so sliding a Tetris piece sideways or
walking a menu meant building a timer next to every key node. Two settings on
the node do it now: Repeat after and Repeat interval, both in milliseconds so
the usual values stay whole numbers (300, 50) instead of 0.3 and 0.05. A delay
of 0 keeps the node exactly as it was, an interval of 0 means every frame.
The browser's own auto-repeat is deliberately not used - Input.press has always
dropped it - because its delay and rate are an operating-system setting of the
player's, not a game's. The repeat is built from the measured hold time instead,
per node, so two nodes on one key keep their own clocks. The rule itself is a
pure function in keyRepeat.ts and is checked without a world.
Both rows stay off the node body while the delay is 0: two lines of "0" on every
key node in every graph would be noise, and the screenshot said so. NodeDef gains
hiddenParams for that; the inspector still shows them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>