yuuna-engine 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/engine/types.d.ts +1 -0
- package/lib/index.cjs +24 -0
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +24 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/engine/types.d.ts
CHANGED
|
@@ -248,6 +248,7 @@ export type RunEngineProps<State, Custom = never> = {
|
|
|
248
248
|
zoom: number;
|
|
249
249
|
};
|
|
250
250
|
timeScale?: (state: State) => number;
|
|
251
|
+
maxFps?: (state: State) => number;
|
|
251
252
|
};
|
|
252
253
|
export type RunEngineFunction = <State, Custom = never>(props: RunEngineProps<State, Custom>) => Promise<{
|
|
253
254
|
sendEvent: (event: Custom) => void;
|
package/lib/index.cjs
CHANGED
|
@@ -680,6 +680,9 @@ const runEngine = async (props) => {
|
|
|
680
680
|
};
|
|
681
681
|
const nextStateFns = Array.isArray(props.nextState) ? props.nextState : [props.nextState];
|
|
682
682
|
let lastFrame = Date.now();
|
|
683
|
+
// When the next tick is due under RunEngineProps.maxFps — only read
|
|
684
|
+
// or advanced while a cap is set (see the top of the loop below).
|
|
685
|
+
let nextTickAt = 0;
|
|
683
686
|
let hoveredId = null;
|
|
684
687
|
// How much this tick's simulated time is scaled by (see
|
|
685
688
|
// RunEngineProps.timeScale) — set once per tick, right before that
|
|
@@ -1002,6 +1005,27 @@ const runEngine = async (props) => {
|
|
|
1002
1005
|
const intervalId = setInterval(() => {
|
|
1003
1006
|
const now = Date.now();
|
|
1004
1007
|
const rawDelta = now - lastFrame;
|
|
1008
|
+
// Skipped rather than delayed (see RunEngineProps.maxFps) — returning
|
|
1009
|
+
// before anything else runs leaves `events`, keyboard/mouse state and
|
|
1010
|
+
// lastFrame all untouched, so whatever happened in the meantime is
|
|
1011
|
+
// still delivered by the next tick that does run, and its TIME delta
|
|
1012
|
+
// covers this skipped one too.
|
|
1013
|
+
const maxFps = props.maxFps?.(state);
|
|
1014
|
+
if (maxFps !== undefined && maxFps > 0) {
|
|
1015
|
+
if (now < nextTickAt) {
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
// Scheduled from the previous target rather than from `now`: the
|
|
1019
|
+
// interval only fires every ~4ms, so "at least 1000/maxFps since
|
|
1020
|
+
// the last tick" would always round late (a 60 cap landing on
|
|
1021
|
+
// 20ms ticks, i.e. 50 FPS), while this lets the early and late
|
|
1022
|
+
// ticks average out to the cap itself. Falling more than a whole
|
|
1023
|
+
// interval behind (a hitch, a backgrounded tab, the cap having
|
|
1024
|
+
// just been turned on) restarts the schedule from now instead of
|
|
1025
|
+
// bursting through every missed tick to catch up.
|
|
1026
|
+
const interval = 1000 / maxFps;
|
|
1027
|
+
nextTickAt = now - nextTickAt > interval ? now + interval : nextTickAt + interval;
|
|
1028
|
+
}
|
|
1005
1029
|
// Read from state as this tick starts (i.e. still last tick's own
|
|
1006
1030
|
// result) — resolveAnimatedSprite (below, during this same tick's
|
|
1007
1031
|
// render pass) reads this same currentTimeScale rather than calling
|
package/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/lib/index.js
CHANGED
|
@@ -678,6 +678,9 @@ const runEngine = async (props) => {
|
|
|
678
678
|
};
|
|
679
679
|
const nextStateFns = Array.isArray(props.nextState) ? props.nextState : [props.nextState];
|
|
680
680
|
let lastFrame = Date.now();
|
|
681
|
+
// When the next tick is due under RunEngineProps.maxFps — only read
|
|
682
|
+
// or advanced while a cap is set (see the top of the loop below).
|
|
683
|
+
let nextTickAt = 0;
|
|
681
684
|
let hoveredId = null;
|
|
682
685
|
// How much this tick's simulated time is scaled by (see
|
|
683
686
|
// RunEngineProps.timeScale) — set once per tick, right before that
|
|
@@ -1000,6 +1003,27 @@ const runEngine = async (props) => {
|
|
|
1000
1003
|
const intervalId = setInterval(() => {
|
|
1001
1004
|
const now = Date.now();
|
|
1002
1005
|
const rawDelta = now - lastFrame;
|
|
1006
|
+
// Skipped rather than delayed (see RunEngineProps.maxFps) — returning
|
|
1007
|
+
// before anything else runs leaves `events`, keyboard/mouse state and
|
|
1008
|
+
// lastFrame all untouched, so whatever happened in the meantime is
|
|
1009
|
+
// still delivered by the next tick that does run, and its TIME delta
|
|
1010
|
+
// covers this skipped one too.
|
|
1011
|
+
const maxFps = props.maxFps?.(state);
|
|
1012
|
+
if (maxFps !== undefined && maxFps > 0) {
|
|
1013
|
+
if (now < nextTickAt) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
// Scheduled from the previous target rather than from `now`: the
|
|
1017
|
+
// interval only fires every ~4ms, so "at least 1000/maxFps since
|
|
1018
|
+
// the last tick" would always round late (a 60 cap landing on
|
|
1019
|
+
// 20ms ticks, i.e. 50 FPS), while this lets the early and late
|
|
1020
|
+
// ticks average out to the cap itself. Falling more than a whole
|
|
1021
|
+
// interval behind (a hitch, a backgrounded tab, the cap having
|
|
1022
|
+
// just been turned on) restarts the schedule from now instead of
|
|
1023
|
+
// bursting through every missed tick to catch up.
|
|
1024
|
+
const interval = 1000 / maxFps;
|
|
1025
|
+
nextTickAt = now - nextTickAt > interval ? now + interval : nextTickAt + interval;
|
|
1026
|
+
}
|
|
1003
1027
|
// Read from state as this tick starts (i.e. still last tick's own
|
|
1004
1028
|
// result) — resolveAnimatedSprite (below, during this same tick's
|
|
1005
1029
|
// render pass) reads this same currentTimeScale rather than calling
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/package.json
CHANGED