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:
1 parent
4b0919a224
commit
3ab8d88ad6
13 files changed
+552
-3
No files matched your search
@@ -1,10 +1,13 @@
|
||||
import {
|
||||
asNumber,
|
||||
type EntityHandle,
|
||||
type GraphNode,
|
||||
GraphInterpreter,
|
||||
isHandleEnabled,
|
||||
type NodeContext,
|
||||
type TraceListener,
|
||||
} from '../../core/graph';
|
||||
import { stepKeyRepeat } from '../../core/graph/keyRepeat';
|
||||
import { behaviorLookup } from '../../core/model/behaviorPrograms';
|
||||
import type { PropertyBag } from '../../core/model/Properties';
|
||||
import { findAsset, type Project } from '../../core/model/Project';
|
||||
@@ -100,7 +103,7 @@ export class BehaviorSystem implements System {
|
||||
this.run(
|
||||
instance,
|
||||
(i, ctx) => {
|
||||
for (const key of this.input.justPressed) i.fire('OnKeyDown', ctx, {}, (n) => keyOf(n) === key);
|
||||
for (const node of i.eventNodes('OnKeyDown')) this.fireKeyDown(i, ctx, node, dt);
|
||||
for (const key of this.input.justReleased) i.fire('OnKeyUp', ctx, {}, (n) => keyOf(n) === key);
|
||||
for (const key of this.input.heldKeys) i.fire('OnKeyHeld', ctx, { dt }, (n) => keyOf(n) === key);
|
||||
for (const node of i.eventNodes('OnTimer')) {
|
||||
@@ -128,6 +131,29 @@ export class BehaviorSystem implements System {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The press plus whatever repeats holding the key has earned. Each node keeps its own hold clock
|
||||
* in `nodeState`, so two nodes on the same key repeat on their own schedules.
|
||||
*/
|
||||
private fireKeyDown(i: GraphInterpreter, ctx: NodeContext, node: GraphNode, dt: number): void {
|
||||
const key = String(node.params.key ?? '');
|
||||
const stored = i.nodeState.get(node.id);
|
||||
const { state, fires } = stepKeyRepeat(
|
||||
stored ? { held: stored.held ?? 0, repeats: stored.repeats ?? 0 } : null,
|
||||
{
|
||||
pressed: this.input.justPressed.has(key),
|
||||
down: this.input.heldKeys.has(key),
|
||||
dt,
|
||||
// The node asks for milliseconds so the fields stay whole numbers; the clock runs in seconds.
|
||||
after: asNumber(node.params.repeatAfter ?? 0) / 1000,
|
||||
interval: asNumber(node.params.repeatInterval ?? 0) / 1000,
|
||||
},
|
||||
);
|
||||
if (state) i.nodeState.set(node.id, { held: state.held, repeats: state.repeats });
|
||||
else i.nodeState.delete(node.id);
|
||||
for (let fired = 0; fired < fires; fired++) i.fire('OnKeyDown', ctx, {}, (n) => n.id === node.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collision events for this behavior: every pair in which one collider belongs to the
|
||||
* behavior's body (its owner, or itself when it has none) fires once, with `other` being
|
||||
|
||||
Reference in new issue
Block a user