emeraldengine 2.2.1 → 3.0.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/README.md +2359 -968
- package/dist/types/index.d.ts +72 -32
- package/dist/types/src/Animator.d.ts +50 -0
- package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
- package/dist/types/src/Camera.d.ts +122 -0
- package/dist/types/src/CameraController.d.ts +107 -0
- package/dist/types/src/CanvasText.d.ts +91 -0
- package/dist/types/src/CollisionLayers.d.ts +58 -0
- package/dist/types/{Color.d.ts → src/Color.d.ts} +5 -6
- package/dist/types/src/Coroutine.d.ts +111 -0
- package/dist/types/src/DebugOverlay.d.ts +86 -0
- package/dist/types/src/Drawable.d.ts +271 -0
- package/dist/types/src/Easing.d.ts +22 -0
- package/dist/types/src/Emerald.d.ts +420 -0
- package/dist/types/src/EmeraldDB.d.ts +159 -0
- package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
- package/dist/types/src/GLUtils.d.ts +4 -0
- package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
- package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +60 -23
- package/dist/types/src/Interpolator.d.ts +66 -0
- package/dist/types/src/Material.d.ts +87 -0
- package/dist/types/src/MathUtils.d.ts +80 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/{Physics.d.ts → src/Physics.d.ts} +88 -20
- package/dist/types/src/Pool.d.ts +53 -0
- package/dist/types/src/PostEffects.d.ts +68 -0
- package/dist/types/src/PostProcessor.d.ts +124 -0
- package/dist/types/src/RenderTarget.d.ts +56 -0
- package/dist/types/src/Scene.d.ts +62 -0
- package/dist/types/src/ScreenEffects.d.ts +111 -0
- package/dist/types/src/Serializer.d.ts +86 -0
- package/dist/types/src/Shaders.d.ts +2 -0
- package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
- package/dist/types/src/SpatialGrid.d.ts +50 -0
- package/dist/types/src/SpriteBatch.d.ts +89 -0
- package/dist/types/src/StateMachine.d.ts +59 -0
- package/dist/types/src/Storage.d.ts +89 -0
- package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
- package/dist/types/src/TextureAtlas.d.ts +55 -0
- package/dist/types/src/Tilemap.d.ts +91 -0
- package/dist/types/src/Time.d.ts +53 -0
- package/dist/types/src/Timer.d.ts +54 -0
- package/dist/types/src/Transform.d.ts +94 -0
- package/dist/types/src/Tween.d.ts +81 -0
- package/dist/types/src/UI.d.ts +121 -0
- package/dist/types/src/components/Behaviour.d.ts +71 -0
- package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
- package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
- package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
- package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
- package/dist/types/src/components/Collider.d.ts +96 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/RigidBody.d.ts +183 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -0
- package/dist/types/src/importers/TiledMap.d.ts +62 -0
- package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
- package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
- package/dist/types/src/managers/AssetManager.d.ts +116 -0
- package/dist/types/src/managers/AudioManager.d.ts +259 -0
- package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
- package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
- package/dist/types/src/managers/GLManager.d.ts +84 -0
- package/dist/types/src/managers/GLState.d.ts +37 -0
- package/dist/types/src/managers/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +290 -0
- package/dist/types/src/managers/NetworkManager.d.ts +93 -0
- package/dist/types/src/managers/RenderStats.d.ts +34 -0
- package/dist/types/src/managers/SceneManager.d.ts +44 -0
- package/dist/types/src/managers/ShaderManager.d.ts +55 -0
- package/dist/types/src/managers/TextureManager.d.ts +102 -0
- package/dist/types/src/particlesystem/Particle.d.ts +64 -0
- package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
- package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
- package/index.js +72 -0
- package/package.json +74 -60
- package/src/Animator.js +95 -0
- package/src/BitmapText.js +6 -5
- package/src/Camera.js +183 -0
- package/src/CameraController.js +192 -0
- package/src/CanvasText.js +281 -0
- package/src/CollisionLayers.js +86 -0
- package/src/Color.js +18 -18
- package/src/Coroutine.js +259 -0
- package/src/DebugOverlay.js +246 -0
- package/src/Drawable.js +842 -582
- package/src/Easing.js +57 -0
- package/src/Emerald.js +1150 -459
- package/src/EmeraldDB.js +328 -0
- package/src/FPSCounter.js +43 -43
- package/src/GLUtils.js +60 -67
- package/src/Instance.js +41 -5
- package/src/InstancedTexture.js +251 -118
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +133 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +186 -5
- package/src/Pool.js +85 -0
- package/src/PostEffects.js +296 -0
- package/src/PostProcessor.js +304 -0
- package/src/RenderTarget.js +134 -0
- package/src/Scene.js +115 -83
- package/src/ScreenEffects.js +266 -0
- package/src/Serializer.js +131 -0
- package/src/Shaders.js +150 -165
- package/src/Shapes.js +118 -129
- package/src/SpatialGrid.js +111 -0
- package/src/SpriteBatch.js +299 -0
- package/src/StateMachine.js +82 -0
- package/src/Storage.js +175 -47
- package/src/Texture.js +58 -67
- package/src/TextureAtlas.js +96 -0
- package/src/Tilemap.js +274 -0
- package/src/Time.js +51 -6
- package/src/Timer.js +99 -0
- package/src/Transform.js +100 -7
- package/src/Tween.js +160 -0
- package/src/UI.js +394 -0
- package/src/components/Behaviour.js +90 -0
- package/src/components/BoxCollider.js +22 -3
- package/src/components/CircleCollider.js +19 -3
- package/src/components/CircleColliderDebug.js +24 -24
- package/src/components/Collider.js +140 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/RigidBody.js +123 -2
- package/src/importers/Aseprite.js +142 -0
- package/src/importers/TiledMap.js +158 -0
- package/src/lights/DirectionalLight.js +6 -15
- package/src/lights/PointLight.js +4 -4
- package/src/managers/AssetManager.js +239 -0
- package/src/managers/AudioManager.js +565 -146
- package/src/managers/EventManager.js +488 -477
- package/src/managers/GLManager.js +57 -0
- package/src/managers/GLState.js +70 -0
- package/src/managers/IDManager.js +24 -2
- package/src/managers/InputManager.js +779 -0
- package/src/managers/NetworkManager.js +178 -0
- package/src/managers/RenderStats.js +34 -0
- package/src/managers/SceneManager.js +30 -0
- package/src/managers/ShaderManager.js +0 -2
- package/src/managers/TextureManager.js +218 -0
- package/src/particlesystem/Particle.js +82 -7
- package/src/particlesystem/ParticleSettings.js +21 -3
- package/src/particlesystem/Particles.js +80 -31
- package/dist/types/Drawable.d.ts +0 -157
- package/dist/types/Emerald.d.ts +0 -73
- package/dist/types/GLUtils.d.ts +0 -6
- package/dist/types/Scene.d.ts +0 -39
- package/dist/types/Shaders.d.ts +0 -4
- package/dist/types/Storage.d.ts +0 -46
- package/dist/types/Time.d.ts +0 -22
- package/dist/types/Transform.d.ts +0 -41
- package/dist/types/components/BoxColliderDebug.d.ts +0 -19
- package/dist/types/components/CircleColliderDebug.d.ts +0 -19
- package/dist/types/components/Collider.d.ts +0 -53
- package/dist/types/components/GameObject.d.ts +0 -72
- package/dist/types/components/RigidBody.d.ts +0 -104
- package/dist/types/managers/AudioManager.d.ts +0 -60
- package/dist/types/managers/GLManager.d.ts +0 -47
- package/dist/types/managers/IDManager.d.ts +0 -21
- package/dist/types/managers/SceneManager.d.ts +0 -22
- package/dist/types/particlesystem/Particle.d.ts +0 -42
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class CollisionLayers
|
|
3
|
+
* @description A small registry that maps human-readable layer names to the
|
|
4
|
+
* category bits planck uses for collision filtering, so games can say
|
|
5
|
+
* "players collide with ground and enemies, but not with each other" without
|
|
6
|
+
* juggling raw bitmasks.
|
|
7
|
+
*
|
|
8
|
+
* Two fixtures collide only if each one's category is in the other's mask, so
|
|
9
|
+
* filtering is symmetric by construction. Up to 16 distinct layers are
|
|
10
|
+
* supported (planck filter bits are 16-bit).
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* CollisionLayers.define("ground", "player", "enemy", "pickup");
|
|
14
|
+
* playerCollider.setCategory("player");
|
|
15
|
+
* playerCollider.setCollidesWith(["ground", "enemy", "pickup"]);
|
|
16
|
+
* enemyCollider.setCategory("enemy");
|
|
17
|
+
* enemyCollider.setCollidesWith(["ground", "player"]); // enemies ignore each other
|
|
18
|
+
*/
|
|
19
|
+
const ALL_BITS = 0xffff;
|
|
20
|
+
|
|
21
|
+
class CollisionLayers {
|
|
22
|
+
/**
|
|
23
|
+
* @method define
|
|
24
|
+
* @description Registers one or more named layers (in order), assigning each
|
|
25
|
+
* the next free category bit. Re-defining an existing name returns its
|
|
26
|
+
* existing bit. Returns the bit of the last name passed.
|
|
27
|
+
* @param {...string} names
|
|
28
|
+
* @returns {number}
|
|
29
|
+
*/
|
|
30
|
+
static define(...names) {
|
|
31
|
+
let last = 0;
|
|
32
|
+
for (const name of names) last = CollisionLayers.bit(name);
|
|
33
|
+
return last;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @method bit
|
|
38
|
+
* @description Returns the category bit for a layer name, registering it on
|
|
39
|
+
* first use.
|
|
40
|
+
* @param {string} name
|
|
41
|
+
* @returns {number}
|
|
42
|
+
*/
|
|
43
|
+
static bit(name) {
|
|
44
|
+
if (CollisionLayers._bits.has(name)) return CollisionLayers._bits.get(name);
|
|
45
|
+
if (CollisionLayers._next > 15) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
"[CollisionLayers] Exceeded 16 collision layers (planck filter limit)."
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const bit = 1 << CollisionLayers._next++;
|
|
51
|
+
CollisionLayers._bits.set(name, bit);
|
|
52
|
+
return bit;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @method mask
|
|
57
|
+
* @description Computes a combined mask from layer names. Accepts a single
|
|
58
|
+
* name, an array of names, a number (passed through), or "all"/null for
|
|
59
|
+
* "collide with everything".
|
|
60
|
+
* @param {string|string[]|number|null} layers
|
|
61
|
+
* @returns {number}
|
|
62
|
+
*/
|
|
63
|
+
static mask(layers) {
|
|
64
|
+
if (layers == null || layers === "all") return ALL_BITS;
|
|
65
|
+
if (typeof layers === "number") return layers;
|
|
66
|
+
const list = Array.isArray(layers) ? layers : [layers];
|
|
67
|
+
let m = 0;
|
|
68
|
+
for (const name of list) m |= CollisionLayers.bit(name);
|
|
69
|
+
return m;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @method reset
|
|
74
|
+
* @description Clears all registered layers (mainly for tests).
|
|
75
|
+
*/
|
|
76
|
+
static reset() {
|
|
77
|
+
CollisionLayers._bits.clear();
|
|
78
|
+
CollisionLayers._next = 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
CollisionLayers._bits = new Map();
|
|
83
|
+
CollisionLayers._next = 0;
|
|
84
|
+
CollisionLayers.ALL = ALL_BITS;
|
|
85
|
+
|
|
86
|
+
export default CollisionLayers;
|
package/src/Color.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @class Color
|
|
3
|
-
* @description Represents a color
|
|
4
|
-
* @param {number} r - The red value
|
|
5
|
-
* @param {number} g - The green value
|
|
6
|
-
* @param {number} b - The blue value
|
|
7
|
-
* @param {number} a - The alpha value
|
|
8
|
-
*/
|
|
9
|
-
class Color {
|
|
10
|
-
constructor(r, g, b, a = 255) {
|
|
11
|
-
this.r = r;
|
|
12
|
-
this.g = g;
|
|
13
|
-
this.b = b;
|
|
14
|
-
this.a = a;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default Color;
|
|
1
|
+
/**
|
|
2
|
+
* @class Color
|
|
3
|
+
* @description Represents a color
|
|
4
|
+
* @param {number} r - The red value
|
|
5
|
+
* @param {number} g - The green value
|
|
6
|
+
* @param {number} b - The blue value
|
|
7
|
+
* @param {number} a - The alpha value
|
|
8
|
+
*/
|
|
9
|
+
class Color {
|
|
10
|
+
constructor(r, g, b, a = 255) {
|
|
11
|
+
this.r = r;
|
|
12
|
+
this.g = g;
|
|
13
|
+
this.b = b;
|
|
14
|
+
this.a = a;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default Color;
|
package/src/Coroutine.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @class Coroutine
|
|
3
|
+
* @description Generator-based sequencing for time-based logic, layered on top of
|
|
4
|
+
* the same per-frame delta the rest of the engine uses. Write a generator that
|
|
5
|
+
* `yield`s instructions, and the runner advances it each frame:
|
|
6
|
+
*
|
|
7
|
+
* - `yield seconds` (a number) -> wait that many seconds
|
|
8
|
+
* - `yield Coroutine.waitFrames(n)` -> wait n frames
|
|
9
|
+
* - `yield Coroutine.waitUntil(fn)` -> wait until fn() is truthy
|
|
10
|
+
* - `yield Coroutine.waitWhile(fn)` -> wait while fn() is truthy
|
|
11
|
+
* - `yield somePromise` -> wait for the promise to resolve
|
|
12
|
+
* - `yield anotherCoroutine` -> wait for a nested coroutine to finish
|
|
13
|
+
* - `yield Coroutine.tween(...)` -> drive a value over time, then continue
|
|
14
|
+
*
|
|
15
|
+
* Coroutines are driven by `Coroutine.update(dt)` (called automatically from
|
|
16
|
+
* `Emerald.drawScene`, so they honor pause/slow-mo via Time.timeScale).
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* Coroutine.start(function* () {
|
|
20
|
+
* door.open();
|
|
21
|
+
* yield 1.5; // wait 1.5s
|
|
22
|
+
* yield Coroutine.waitUntil(() => player.isReady);
|
|
23
|
+
* boss.spawn();
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
class Coroutine {
|
|
27
|
+
/**
|
|
28
|
+
* @method start
|
|
29
|
+
* @description Starts a coroutine from a generator function (or an existing
|
|
30
|
+
* generator/iterator). Returns a handle with `cancel()` and `done`.
|
|
31
|
+
* @param {GeneratorFunction|Generator} gen - The generator (function) to run
|
|
32
|
+
* @param {...*} args - Arguments forwarded to the generator function
|
|
33
|
+
* @returns {Object} - A handle: { cancel(), done, isRunning() }
|
|
34
|
+
*/
|
|
35
|
+
static start(gen, ...args) {
|
|
36
|
+
const iterator = typeof gen === "function" ? gen(...args) : gen;
|
|
37
|
+
const routine = {
|
|
38
|
+
iterator,
|
|
39
|
+
wait: null,
|
|
40
|
+
cancelled: false,
|
|
41
|
+
done: false,
|
|
42
|
+
_resolve: null,
|
|
43
|
+
};
|
|
44
|
+
routine.promise = new Promise((resolve) => (routine._resolve = resolve));
|
|
45
|
+
routine.cancel = () => {
|
|
46
|
+
routine.cancelled = true;
|
|
47
|
+
};
|
|
48
|
+
routine.isRunning = () => !routine.done && !routine.cancelled;
|
|
49
|
+
Coroutine._routines.push(routine);
|
|
50
|
+
Coroutine._advance(routine, undefined, 0);
|
|
51
|
+
return routine;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @method waitFrames
|
|
56
|
+
* @description Yieldable that waits a number of frames.
|
|
57
|
+
* @param {number} count - Frames to wait
|
|
58
|
+
*/
|
|
59
|
+
static waitFrames(count) {
|
|
60
|
+
return { __coroutine: "frames", count: Math.max(0, count | 0) };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @method waitUntil
|
|
65
|
+
* @description Yieldable that blocks until the predicate returns truthy.
|
|
66
|
+
* @param {Function} predicate
|
|
67
|
+
*/
|
|
68
|
+
static waitUntil(predicate) {
|
|
69
|
+
return { __coroutine: "until", predicate };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @method waitWhile
|
|
74
|
+
* @description Yieldable that blocks while the predicate returns truthy.
|
|
75
|
+
* @param {Function} predicate
|
|
76
|
+
*/
|
|
77
|
+
static waitWhile(predicate) {
|
|
78
|
+
return { __coroutine: "until", predicate: () => !predicate() };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @method tween
|
|
83
|
+
* @description Yieldable that interpolates from `from` to `to` over `duration`
|
|
84
|
+
* seconds, calling `onUpdate(value)` each frame, then resumes the coroutine.
|
|
85
|
+
* @param {number} from
|
|
86
|
+
* @param {number} to
|
|
87
|
+
* @param {number} duration - Seconds
|
|
88
|
+
* @param {Function} onUpdate - Receives the current value each step
|
|
89
|
+
* @param {Function} [easing] - Optional easing t -> t
|
|
90
|
+
*/
|
|
91
|
+
static tween(from, to, duration, onUpdate, easing) {
|
|
92
|
+
return {
|
|
93
|
+
__coroutine: "tween",
|
|
94
|
+
from,
|
|
95
|
+
to,
|
|
96
|
+
duration: Math.max(0, duration),
|
|
97
|
+
onUpdate,
|
|
98
|
+
easing: easing || ((t) => t),
|
|
99
|
+
elapsed: 0,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @method update
|
|
105
|
+
* @description Advances all running coroutines by dt seconds. Driven by
|
|
106
|
+
* Emerald.drawScene.
|
|
107
|
+
* @param {number} dt - Seconds since the last frame (time-scaled)
|
|
108
|
+
*/
|
|
109
|
+
static update(dt) {
|
|
110
|
+
const routines = Coroutine._routines;
|
|
111
|
+
for (let i = routines.length - 1; i >= 0; i--) {
|
|
112
|
+
const routine = routines[i];
|
|
113
|
+
if (routine.cancelled || routine.done) {
|
|
114
|
+
Coroutine._finish(routine);
|
|
115
|
+
routines.splice(i, 1);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
Coroutine._step(routine, dt);
|
|
119
|
+
if (routine.cancelled || routine.done) {
|
|
120
|
+
Coroutine._finish(routine);
|
|
121
|
+
routines.splice(i, 1);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @method clearAll
|
|
128
|
+
* @description Cancels and removes every running coroutine.
|
|
129
|
+
*/
|
|
130
|
+
static clearAll() {
|
|
131
|
+
for (const r of Coroutine._routines) r.cancelled = true;
|
|
132
|
+
Coroutine._routines.length = 0;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @method count
|
|
137
|
+
* @returns {number} - Number of currently running coroutines
|
|
138
|
+
*/
|
|
139
|
+
static count() {
|
|
140
|
+
return Coroutine._routines.length;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** @private */
|
|
144
|
+
static _finish(routine) {
|
|
145
|
+
if (routine._resolve) {
|
|
146
|
+
const resolve = routine._resolve;
|
|
147
|
+
routine._resolve = null;
|
|
148
|
+
resolve();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** @private */
|
|
153
|
+
static _step(routine, dt) {
|
|
154
|
+
const wait = routine.wait;
|
|
155
|
+
if (!wait) {
|
|
156
|
+
Coroutine._advance(routine, undefined, dt);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
switch (wait.kind) {
|
|
161
|
+
case "time":
|
|
162
|
+
wait.remaining -= dt;
|
|
163
|
+
if (wait.remaining <= 0) {
|
|
164
|
+
const overflow = -wait.remaining;
|
|
165
|
+
Coroutine._advance(routine, undefined, overflow);
|
|
166
|
+
}
|
|
167
|
+
break;
|
|
168
|
+
case "frames":
|
|
169
|
+
wait.remaining -= 1;
|
|
170
|
+
if (wait.remaining <= 0) Coroutine._advance(routine, undefined, 0);
|
|
171
|
+
break;
|
|
172
|
+
case "until":
|
|
173
|
+
if (wait.predicate()) Coroutine._advance(routine, undefined, 0);
|
|
174
|
+
break;
|
|
175
|
+
case "tween": {
|
|
176
|
+
const t = wait.spec;
|
|
177
|
+
t.elapsed += dt;
|
|
178
|
+
const k = t.duration <= 0 ? 1 : Math.min(1, t.elapsed / t.duration);
|
|
179
|
+
const eased = t.easing(k);
|
|
180
|
+
t.onUpdate(t.from + (t.to - t.from) * eased);
|
|
181
|
+
if (k >= 1) Coroutine._advance(routine, undefined, 0);
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
case "promise":
|
|
185
|
+
if (wait.resolved) {
|
|
186
|
+
Coroutine._advance(routine, wait.value, 0);
|
|
187
|
+
}
|
|
188
|
+
break;
|
|
189
|
+
case "nested":
|
|
190
|
+
if (wait.routine.done || wait.routine.cancelled) {
|
|
191
|
+
Coroutine._advance(routine, undefined, 0);
|
|
192
|
+
}
|
|
193
|
+
break;
|
|
194
|
+
default:
|
|
195
|
+
Coroutine._advance(routine, undefined, dt);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @private */
|
|
200
|
+
static _advance(routine, sendValue, leftoverDt) {
|
|
201
|
+
if (routine.cancelled || routine.done) return;
|
|
202
|
+
let result;
|
|
203
|
+
try {
|
|
204
|
+
result = routine.iterator.next(sendValue);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error("[Coroutine] Error in coroutine:", err);
|
|
207
|
+
routine.done = true;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (result.done) {
|
|
211
|
+
routine.done = true;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
routine.wait = Coroutine._interpret(result.value, routine);
|
|
215
|
+
if (routine.wait && routine.wait.kind === "time" && leftoverDt > 0) {
|
|
216
|
+
Coroutine._step(routine, leftoverDt);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** @private */
|
|
221
|
+
static _interpret(value, routine) {
|
|
222
|
+
if (value == null) {
|
|
223
|
+
return { kind: "time", remaining: 0 };
|
|
224
|
+
}
|
|
225
|
+
if (typeof value === "number") {
|
|
226
|
+
return { kind: "time", remaining: value };
|
|
227
|
+
}
|
|
228
|
+
if (typeof value.then === "function") {
|
|
229
|
+
const wait = { kind: "promise", resolved: false, value: undefined };
|
|
230
|
+
value.then(
|
|
231
|
+
(v) => {
|
|
232
|
+
wait.resolved = true;
|
|
233
|
+
wait.value = v;
|
|
234
|
+
},
|
|
235
|
+
() => {
|
|
236
|
+
wait.resolved = true;
|
|
237
|
+
}
|
|
238
|
+
);
|
|
239
|
+
return wait;
|
|
240
|
+
}
|
|
241
|
+
if (value.iterator && typeof value.cancel === "function") {
|
|
242
|
+
return { kind: "nested", routine: value };
|
|
243
|
+
}
|
|
244
|
+
switch (value.__coroutine) {
|
|
245
|
+
case "frames":
|
|
246
|
+
return { kind: "frames", remaining: value.count };
|
|
247
|
+
case "until":
|
|
248
|
+
return { kind: "until", predicate: value.predicate };
|
|
249
|
+
case "tween":
|
|
250
|
+
return { kind: "tween", spec: value };
|
|
251
|
+
default:
|
|
252
|
+
return { kind: "time", remaining: 0 };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
Coroutine._routines = [];
|
|
258
|
+
|
|
259
|
+
export default Coroutine;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import RigidBody from "./components/RigidBody.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @class DebugOverlay
|
|
5
|
+
* @description A DOM panel that profiles the running game: FPS, frame-time stats
|
|
6
|
+
* with a live sparkline graph, object/camera counts, the primary camera's
|
|
7
|
+
* position/zoom, JS heap usage (where available), and any extra metrics you
|
|
8
|
+
* push. It can also toggle physics collider debug shapes on a scene.
|
|
9
|
+
*
|
|
10
|
+
* Call `update(emerald, scene)` each frame; toggle visibility with `setVisible`.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* const debug = new DebugOverlay();
|
|
14
|
+
* // each frame, after drawScene:
|
|
15
|
+
* debug.update(emerald, scene);
|
|
16
|
+
* debug.setMetric("draws", spriteBatch.drawCalls);
|
|
17
|
+
* debug.showColliders(scene, true); // visualize physics colliders
|
|
18
|
+
*/
|
|
19
|
+
class DebugOverlay {
|
|
20
|
+
constructor(options = {}) {
|
|
21
|
+
this.element = document.createElement("div");
|
|
22
|
+
Object.assign(this.element.style, {
|
|
23
|
+
position: "absolute",
|
|
24
|
+
top: options.top || "10px",
|
|
25
|
+
right: options.right || "10px",
|
|
26
|
+
padding: "8px 10px",
|
|
27
|
+
background: "rgba(0,0,0,0.6)",
|
|
28
|
+
color: "#9be7c4",
|
|
29
|
+
font: "12px/1.5 monospace",
|
|
30
|
+
whiteSpace: "pre",
|
|
31
|
+
borderRadius: "6px",
|
|
32
|
+
pointerEvents: "none",
|
|
33
|
+
zIndex: "9999",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/** @private */
|
|
37
|
+
this._graph = document.createElement("canvas");
|
|
38
|
+
this._graph.width = options.graphWidth || 160;
|
|
39
|
+
this._graph.height = options.graphHeight || 36;
|
|
40
|
+
this._graph.style.display = "block";
|
|
41
|
+
this._graph.style.marginTop = "6px";
|
|
42
|
+
this._graph.style.borderRadius = "3px";
|
|
43
|
+
/** @private */
|
|
44
|
+
this._gctx = this._graph.getContext("2d");
|
|
45
|
+
|
|
46
|
+
/** @private */
|
|
47
|
+
this._text = document.createElement("div");
|
|
48
|
+
this.element.appendChild(this._text);
|
|
49
|
+
this.element.appendChild(this._graph);
|
|
50
|
+
|
|
51
|
+
(options.parent || document.body).appendChild(this.element);
|
|
52
|
+
|
|
53
|
+
/** @private */
|
|
54
|
+
this._last = performance.now();
|
|
55
|
+
/** @private */
|
|
56
|
+
this._frames = 0;
|
|
57
|
+
/** @private */
|
|
58
|
+
this._accum = 0;
|
|
59
|
+
this.fps = 0;
|
|
60
|
+
|
|
61
|
+
/** @private */
|
|
62
|
+
this._frameTimes = [];
|
|
63
|
+
/** @private */
|
|
64
|
+
this._maxSamples = this._graph.width;
|
|
65
|
+
/** @private */
|
|
66
|
+
this._dtMs = 0;
|
|
67
|
+
/** @private */
|
|
68
|
+
this._minMs = 0;
|
|
69
|
+
/** @private */
|
|
70
|
+
this._maxMs = 0;
|
|
71
|
+
/** @private */
|
|
72
|
+
this._avgMs = 0;
|
|
73
|
+
|
|
74
|
+
/** @private */
|
|
75
|
+
this._metrics = {};
|
|
76
|
+
/** @private */
|
|
77
|
+
this._colliderScene = null;
|
|
78
|
+
/** @private */
|
|
79
|
+
this._collidersShown = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @method setMetric
|
|
84
|
+
* @description Adds/updates a custom metric line shown in the panel.
|
|
85
|
+
* @param {string} key
|
|
86
|
+
* @param {*} value
|
|
87
|
+
*/
|
|
88
|
+
setMetric(key, value) {
|
|
89
|
+
this._metrics[key] = value;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @method update
|
|
94
|
+
* @description Updates the overlay. Pass the engine and current scene.
|
|
95
|
+
* @param {Emerald} emerald
|
|
96
|
+
* @param {Scene} scene
|
|
97
|
+
*/
|
|
98
|
+
update(emerald, scene) {
|
|
99
|
+
const now = performance.now();
|
|
100
|
+
const dt = now - this._last;
|
|
101
|
+
this._last = now;
|
|
102
|
+
this._dtMs = dt;
|
|
103
|
+
this._accum += dt;
|
|
104
|
+
this._frames++;
|
|
105
|
+
|
|
106
|
+
this._frameTimes.push(dt);
|
|
107
|
+
if (this._frameTimes.length > this._maxSamples) this._frameTimes.shift();
|
|
108
|
+
|
|
109
|
+
if (this._accum >= 500) {
|
|
110
|
+
this.fps = Math.round((this._frames * 1000) / this._accum);
|
|
111
|
+
this._frames = 0;
|
|
112
|
+
this._accum = 0;
|
|
113
|
+
|
|
114
|
+
let min = Infinity;
|
|
115
|
+
let max = 0;
|
|
116
|
+
let sum = 0;
|
|
117
|
+
for (const t of this._frameTimes) {
|
|
118
|
+
if (t < min) min = t;
|
|
119
|
+
if (t > max) max = t;
|
|
120
|
+
sum += t;
|
|
121
|
+
}
|
|
122
|
+
this._minMs = min === Infinity ? 0 : min;
|
|
123
|
+
this._maxMs = max;
|
|
124
|
+
this._avgMs = this._frameTimes.length ? sum / this._frameTimes.length : 0;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const objectCount = scene && scene.objects ? scene.objects.length : 0;
|
|
128
|
+
const cameras = emerald && emerald.cameras ? emerald.cameras.length : 0;
|
|
129
|
+
let camLine = "";
|
|
130
|
+
if (emerald && emerald.camera && emerald.camera.getPosition) {
|
|
131
|
+
const p = emerald.camera.getPosition();
|
|
132
|
+
const z = emerald.camera.getZoom ? emerald.camera.getZoom() : 1;
|
|
133
|
+
camLine = `\ncam ${p.x.toFixed(0)}, ${p.y.toFixed(0)} zoom ${z.toFixed(2)}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
let renderLines = "";
|
|
137
|
+
if (emerald && typeof emerald.getRenderStats === "function") {
|
|
138
|
+
const rs = emerald.getRenderStats();
|
|
139
|
+
renderLines =
|
|
140
|
+
`\ndraws ${rs.drawCalls} quads ${rs.quads}` +
|
|
141
|
+
`\nbinds ${rs.textureBinds}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
let memLine = "";
|
|
145
|
+
if (performance && performance.memory) {
|
|
146
|
+
const mb = performance.memory.usedJSHeapSize / (1024 * 1024);
|
|
147
|
+
memLine = `\nheap ${mb.toFixed(1)} MB`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let metricLines = "";
|
|
151
|
+
for (const key in this._metrics) {
|
|
152
|
+
metricLines += `\n${key.padEnd(5)} ${this._metrics[key]}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
this._text.textContent =
|
|
156
|
+
`FPS ${this.fps}` +
|
|
157
|
+
`\nframe ${this._dtMs.toFixed(1)}ms` +
|
|
158
|
+
`\nmin/avg/max ${this._minMs.toFixed(1)}/${this._avgMs.toFixed(1)}/${this._maxMs.toFixed(1)}` +
|
|
159
|
+
`\nobjs ${objectCount}` +
|
|
160
|
+
`\ncams ${cameras}` +
|
|
161
|
+
renderLines +
|
|
162
|
+
camLine +
|
|
163
|
+
memLine +
|
|
164
|
+
metricLines;
|
|
165
|
+
|
|
166
|
+
this._drawGraph();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @private */
|
|
170
|
+
_drawGraph() {
|
|
171
|
+
const ctx = this._gctx;
|
|
172
|
+
const w = this._graph.width;
|
|
173
|
+
const h = this._graph.height;
|
|
174
|
+
ctx.clearRect(0, 0, w, h);
|
|
175
|
+
ctx.fillStyle = "rgba(255,255,255,0.05)";
|
|
176
|
+
ctx.fillRect(0, 0, w, h);
|
|
177
|
+
|
|
178
|
+
const scale = h / 50;
|
|
179
|
+
ctx.strokeStyle = "rgba(120,220,160,0.35)";
|
|
180
|
+
ctx.beginPath();
|
|
181
|
+
ctx.moveTo(0, h - 16.7 * scale);
|
|
182
|
+
ctx.lineTo(w, h - 16.7 * scale);
|
|
183
|
+
ctx.stroke();
|
|
184
|
+
ctx.strokeStyle = "rgba(220,180,120,0.3)";
|
|
185
|
+
ctx.beginPath();
|
|
186
|
+
ctx.moveTo(0, h - 33.3 * scale);
|
|
187
|
+
ctx.lineTo(w, h - 33.3 * scale);
|
|
188
|
+
ctx.stroke();
|
|
189
|
+
|
|
190
|
+
ctx.strokeStyle = "#7fdcff";
|
|
191
|
+
ctx.beginPath();
|
|
192
|
+
const n = this._frameTimes.length;
|
|
193
|
+
for (let i = 0; i < n; i++) {
|
|
194
|
+
const x = w - n + i;
|
|
195
|
+
const y = h - Math.min(h, this._frameTimes[i] * scale);
|
|
196
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
197
|
+
else ctx.lineTo(x, y);
|
|
198
|
+
}
|
|
199
|
+
ctx.stroke();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @method showColliders
|
|
204
|
+
* @description Toggles physics collider debug shapes for every RigidBody in a
|
|
205
|
+
* scene's objects (uses the lazy debug shapes on each collider).
|
|
206
|
+
* @param {Scene} scene
|
|
207
|
+
* @param {boolean} show
|
|
208
|
+
*/
|
|
209
|
+
showColliders(scene, show) {
|
|
210
|
+
this._collidersShown = show;
|
|
211
|
+
this._colliderScene = scene;
|
|
212
|
+
if (!scene || !scene.objects) return;
|
|
213
|
+
for (const obj of scene.objects) {
|
|
214
|
+
if (!obj.getComponent) continue;
|
|
215
|
+
const rb = obj.getComponent(RigidBody);
|
|
216
|
+
if (!rb) continue;
|
|
217
|
+
const collider = rb.getCollider();
|
|
218
|
+
if (!collider) continue;
|
|
219
|
+
if (show && typeof collider.showDebugShape === "function") {
|
|
220
|
+
collider.showDebugShape();
|
|
221
|
+
} else if (!show && typeof collider.hideDebugShape === "function") {
|
|
222
|
+
collider.hideDebugShape();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* @method setVisible
|
|
229
|
+
* @param {boolean} visible
|
|
230
|
+
*/
|
|
231
|
+
setVisible(visible) {
|
|
232
|
+
this.element.style.display = visible ? "block" : "none";
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* @method destroy
|
|
237
|
+
* @description Removes the overlay from the DOM.
|
|
238
|
+
*/
|
|
239
|
+
destroy() {
|
|
240
|
+
if (this.element.parentNode) {
|
|
241
|
+
this.element.parentNode.removeChild(this.element);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export default DebugOverlay;
|