emeraldengine 2.2.1 → 3.1.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 +2198 -968
- package/dist/types/index.d.ts +75 -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} +6 -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} +75 -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 +81 -0
- package/dist/types/src/ParticleEmitter.d.ts +131 -0
- package/dist/types/src/Physics.d.ts +337 -0
- 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 +102 -0
- package/dist/types/src/components/GameObject.d.ts +135 -0
- package/dist/types/src/components/PolygonCollider.d.ts +33 -0
- package/dist/types/src/components/RigidBody.d.ts +456 -0
- package/dist/types/src/importers/Aseprite.d.ts +79 -0
- package/dist/types/src/importers/ForgeLevel.d.ts +97 -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/Gamepad.d.ts +102 -0
- package/dist/types/src/managers/IDManager.d.ts +31 -0
- package/dist/types/src/managers/InputManager.d.ts +381 -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/dist/types/src/physics/AABB.d.ts +92 -0
- package/dist/types/src/physics/Body.d.ts +435 -0
- package/dist/types/src/physics/BodyType.d.ts +6 -0
- package/dist/types/src/physics/BroadPhase.d.ts +210 -0
- package/dist/types/src/physics/Collision.d.ts +102 -0
- package/dist/types/src/physics/Contact.d.ts +206 -0
- package/dist/types/src/physics/ContactSolver.d.ts +108 -0
- package/dist/types/src/physics/Distance.d.ts +54 -0
- package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
- package/dist/types/src/physics/Fixture.d.ts +221 -0
- package/dist/types/src/physics/Island.d.ts +52 -0
- package/dist/types/src/physics/Joint.d.ts +59 -0
- package/dist/types/src/physics/Math2D.d.ts +371 -0
- package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
- package/dist/types/src/physics/Settings.d.ts +22 -0
- package/dist/types/src/physics/Shapes.d.ts +207 -0
- package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
- package/dist/types/src/physics/World.d.ts +274 -0
- package/dist/types/src/physics/index.d.ts +34 -0
- package/index.js +78 -0
- package/package.json +73 -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 +26 -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 +308 -127
- package/src/Interpolator.js +124 -0
- package/src/Material.js +202 -0
- package/src/MathUtils.js +134 -0
- package/src/ParticleEmitter.js +284 -0
- package/src/Physics.js +431 -40
- 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 +29 -12
- package/src/components/BoxColliderDebug.js +3 -4
- package/src/components/CircleCollider.js +26 -12
- package/src/components/CircleColliderDebug.js +25 -24
- package/src/components/Collider.js +150 -34
- package/src/components/GameObject.js +130 -21
- package/src/components/PolygonCollider.js +55 -0
- package/src/components/RigidBody.js +556 -8
- package/src/importers/Aseprite.js +142 -0
- package/src/importers/ForgeLevel.js +581 -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/Gamepad.js +126 -0
- package/src/managers/IDManager.js +24 -2
- package/src/managers/InputManager.js +905 -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/src/physics/AABB.js +207 -0
- package/src/physics/Body.js +862 -0
- package/src/physics/BodyType.js +16 -0
- package/src/physics/BroadPhase.js +641 -0
- package/src/physics/Collision.js +534 -0
- package/src/physics/Contact.js +500 -0
- package/src/physics/ContactSolver.js +526 -0
- package/src/physics/Distance.js +403 -0
- package/src/physics/DistanceJoint.js +227 -0
- package/src/physics/Fixture.js +346 -0
- package/src/physics/Island.js +203 -0
- package/src/physics/Joint.js +78 -0
- package/src/physics/Math2D.js +573 -0
- package/src/physics/RevoluteJoint.js +278 -0
- package/src/physics/Settings.js +78 -0
- package/src/physics/Shapes.js +549 -0
- package/src/physics/TimeOfImpact.js +87 -0
- package/src/physics/World.js +731 -0
- package/src/physics/index.js +79 -0
- 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/Physics.d.ts +0 -139
- 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
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;
|