Let On key down repeat while the key stays held

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>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-09 22:44:00 +02:00
1 parent 4b0919a224
commit 3ab8d88ad6
13 files changed
+552 -3

No files matched your search

+82
View File
@@ -0,0 +1,82 @@
das on key down event node bräuchte ein "repeat"
sprich wie wenn man auf der tastatur eine taste länger drückt:
- man drückt die taste, das event wir ganz normal gefeuert
- man hält länger gedrückt, nach einem bestimmten delay, feuert das event nochmal
- hält man weiterhin gedrückt, feuert das event öfter hintereinandert
das ist ein beispiel szenario für tetris links/rechts shape bewegung
ich denk wir brauchen beim "on key down" event zwei properties:
- repeat after (seconds)
- repeat interval (seconds, nachdem das erstmal repeat after aufgerufen wurde)
ist repeat after auf 0 - kein effekt (alles wie bisher)
ist repeat interval auf 0 - jedes weitere frame aufrufen
---
## Umgesetzt
Genau die beschriebene Form: zwei Zahlen-Properties am `On key down`-Node.
| | |
|---|---|
| **Repeat after (ms)** | Haltedauer bis zur ersten Wiederholung. **0 = kein Effekt**, alles wie bisher |
| **Repeat interval (ms)** | Abstand der weiteren Wiederholungen. **0 = jedes Frame** |
Beide Felder sind **Millisekunden** — damit bleiben die ueblichen Werte ganze Zahlen (300, 50)
statt 0,3 und 0,05. Die Uhr dahinter rechnet weiter in Sekunden; umgerechnet wird beim Auslesen
der Node-Properties.
Der erste Druck feuert immer sofort und unveraendert — die Wiederholung haengt nur hinten dran.
### Warum das nicht der Auto-Repeat des Browsers ist
Der Browser feuert bei gehaltener Taste selbst wiederholt `keydown`. Diese Ereignisse werden
seit jeher verworfen (`Input.press`: `if (!this.down.has(key)) ... // ignore auto-repeat`), und
das bleibt so: Verzoegerung und Rate sind eine **Betriebssystem-Einstellung des Spielers**,
kein Spielparameter. Die Wiederholung wird stattdessen aus der gemessenen Haltedauer gebaut —
damit ist sie auf jeder Maschine gleich und pro Node einstellbar.
Die Regel selbst steht als reine Funktion in `src/core/graph/keyRepeat.ts`, nicht als Bedingung
im Runtime-Code — deshalb ist sie ohne Browser pruefbar. Die Haltedauer pro Node liegt in
`nodeState`, derselben Ablage, aus der `On timer` seine Uhr bezieht.
### Eine Entscheidung, die ich getroffen habe
Ein sehr kleines, positives Intervall wird auf **10 ms** angehoben — dieselbe Untergrenze, die
`On timer` schon hat. Sonst wuerde ein vertipptes `0,0001` bei einem langen Frame tausende
Aufrufe in einem Bild ausloesen. **0 bleibt davon unberuehrt** und behaelt seine eigene
Bedeutung ("jedes Frame").
### Der Node bleibt schmal
Zwei Zeilen `Repeat after (ms): 0` an *jedem* Tastatur-Node waeren reiner Laerm gewesen — ich habe
mir das Bild angesehen, es war deutlich sichtbar. Beide Zeilen bleiben vom Node-Koerper weg,
solange die Verzoegerung 0 ist; der Inspector zeigt sie immer. Neu dafuer ist `NodeDef.hiddenParams`.
Sobald eine Verzoegerung gesetzt ist, erscheinen **beide** — auch ein Intervall von 0, denn das
ist dort eine Einstellung und keine Abwesenheit.
### Der Beweis
Zwei `On key down` auf **derselben** Taste, einer schlicht, einer wiederholend — ein einziges
Halten misst beide, damit "es hat wiederholt" nicht mit "die Taste kam oefter an" verwechselt
werden kann.
Die folgende Ausgabe stammt aus dem Lauf **vor** der Umstellung auf Millisekunden (daher `0.300` /
`0.050` / `0.75 s`); das Modul tippt jetzt `300`, `50` und `750`. Der Nachlauf steht aus.
```
PASS a hold shorter than the delay fires once, repeat or not
– plain 0 → 1, repeating 0 → 1 over 120 ms with a 0.3 s delay
PASS holding past the delay keeps the repeating node firing, while the plain one counts one press
– plain +1, repeating +11 over 800 ms (press + ~10 repeats expected)
PASS a node with no repeat set carries no repeat rows, the repeating one carries both
– plain ["Key: Right arrow"], repeating [… "Repeat after (s): 0.300", "Repeat interval (s): 0.050"]
PASS typing a new delay lands on the node
PASS the delay typed into the inspector is the one the run uses
– repeating 0 → 1 over 500 ms with the delay raised to 0.75 s
```
Der letzte ist der wichtigste: derselbe Graph, nur die getippte Zahl geaendert — und der Node
schweigt. Damit haengt die Laufzeit nachweislich am Inspector-Wert und nicht an etwas anderem.