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.
Files changed (208) hide show
  1. package/README.md +2198 -968
  2. package/dist/types/index.d.ts +75 -32
  3. package/dist/types/src/Animator.d.ts +50 -0
  4. package/dist/types/{BitmapText.d.ts → src/BitmapText.d.ts} +19 -21
  5. package/dist/types/src/Camera.d.ts +122 -0
  6. package/dist/types/src/CameraController.d.ts +107 -0
  7. package/dist/types/src/CanvasText.d.ts +91 -0
  8. package/dist/types/src/CollisionLayers.d.ts +58 -0
  9. package/dist/types/{Color.d.ts → src/Color.d.ts} +6 -6
  10. package/dist/types/src/Coroutine.d.ts +111 -0
  11. package/dist/types/src/DebugOverlay.d.ts +86 -0
  12. package/dist/types/src/Drawable.d.ts +271 -0
  13. package/dist/types/src/Easing.d.ts +22 -0
  14. package/dist/types/src/Emerald.d.ts +420 -0
  15. package/dist/types/src/EmeraldDB.d.ts +159 -0
  16. package/dist/types/{FPSCounter.d.ts → src/FPSCounter.d.ts} +1 -3
  17. package/dist/types/src/GLUtils.d.ts +4 -0
  18. package/dist/types/{Instance.d.ts → src/Instance.d.ts} +37 -15
  19. package/dist/types/{InstancedTexture.d.ts → src/InstancedTexture.d.ts} +75 -23
  20. package/dist/types/src/Interpolator.d.ts +66 -0
  21. package/dist/types/src/Material.d.ts +87 -0
  22. package/dist/types/src/MathUtils.d.ts +81 -0
  23. package/dist/types/src/ParticleEmitter.d.ts +131 -0
  24. package/dist/types/src/Physics.d.ts +337 -0
  25. package/dist/types/src/Pool.d.ts +53 -0
  26. package/dist/types/src/PostEffects.d.ts +68 -0
  27. package/dist/types/src/PostProcessor.d.ts +124 -0
  28. package/dist/types/src/RenderTarget.d.ts +56 -0
  29. package/dist/types/src/Scene.d.ts +62 -0
  30. package/dist/types/src/ScreenEffects.d.ts +111 -0
  31. package/dist/types/src/Serializer.d.ts +86 -0
  32. package/dist/types/src/Shaders.d.ts +2 -0
  33. package/dist/types/{Shapes.d.ts → src/Shapes.d.ts} +4 -6
  34. package/dist/types/src/SpatialGrid.d.ts +50 -0
  35. package/dist/types/src/SpriteBatch.d.ts +89 -0
  36. package/dist/types/src/StateMachine.d.ts +59 -0
  37. package/dist/types/src/Storage.d.ts +89 -0
  38. package/dist/types/{Texture.d.ts → src/Texture.d.ts} +3 -4
  39. package/dist/types/src/TextureAtlas.d.ts +55 -0
  40. package/dist/types/src/Tilemap.d.ts +91 -0
  41. package/dist/types/src/Time.d.ts +53 -0
  42. package/dist/types/src/Timer.d.ts +54 -0
  43. package/dist/types/src/Transform.d.ts +94 -0
  44. package/dist/types/src/Tween.d.ts +81 -0
  45. package/dist/types/src/UI.d.ts +121 -0
  46. package/dist/types/src/components/Behaviour.d.ts +71 -0
  47. package/dist/types/{components → src/components}/BoxCollider.d.ts +14 -14
  48. package/dist/types/src/components/BoxColliderDebug.d.ts +15 -0
  49. package/dist/types/{components → src/components}/CircleCollider.d.ts +14 -12
  50. package/dist/types/src/components/CircleColliderDebug.d.ts +15 -0
  51. package/dist/types/src/components/Collider.d.ts +102 -0
  52. package/dist/types/src/components/GameObject.d.ts +135 -0
  53. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  54. package/dist/types/src/components/RigidBody.d.ts +456 -0
  55. package/dist/types/src/importers/Aseprite.d.ts +79 -0
  56. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  57. package/dist/types/src/importers/TiledMap.d.ts +62 -0
  58. package/dist/types/{lights → src/lights}/DirectionalLight.d.ts +7 -9
  59. package/dist/types/{lights → src/lights}/PointLight.d.ts +6 -9
  60. package/dist/types/src/managers/AssetManager.d.ts +116 -0
  61. package/dist/types/src/managers/AudioManager.d.ts +259 -0
  62. package/dist/types/{managers → src/managers}/CameraManager.d.ts +8 -8
  63. package/dist/types/{managers → src/managers}/EventManager.d.ts +46 -32
  64. package/dist/types/src/managers/GLManager.d.ts +84 -0
  65. package/dist/types/src/managers/GLState.d.ts +37 -0
  66. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  67. package/dist/types/src/managers/IDManager.d.ts +31 -0
  68. package/dist/types/src/managers/InputManager.d.ts +381 -0
  69. package/dist/types/src/managers/NetworkManager.d.ts +93 -0
  70. package/dist/types/src/managers/RenderStats.d.ts +34 -0
  71. package/dist/types/src/managers/SceneManager.d.ts +44 -0
  72. package/dist/types/src/managers/ShaderManager.d.ts +55 -0
  73. package/dist/types/src/managers/TextureManager.d.ts +102 -0
  74. package/dist/types/src/particlesystem/Particle.d.ts +64 -0
  75. package/dist/types/{particlesystem → src/particlesystem}/ParticleSettings.d.ts +32 -24
  76. package/dist/types/{particlesystem → src/particlesystem}/Particles.d.ts +20 -12
  77. package/dist/types/src/physics/AABB.d.ts +92 -0
  78. package/dist/types/src/physics/Body.d.ts +435 -0
  79. package/dist/types/src/physics/BodyType.d.ts +6 -0
  80. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  81. package/dist/types/src/physics/Collision.d.ts +102 -0
  82. package/dist/types/src/physics/Contact.d.ts +206 -0
  83. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  84. package/dist/types/src/physics/Distance.d.ts +54 -0
  85. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  86. package/dist/types/src/physics/Fixture.d.ts +221 -0
  87. package/dist/types/src/physics/Island.d.ts +52 -0
  88. package/dist/types/src/physics/Joint.d.ts +59 -0
  89. package/dist/types/src/physics/Math2D.d.ts +371 -0
  90. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  91. package/dist/types/src/physics/Settings.d.ts +22 -0
  92. package/dist/types/src/physics/Shapes.d.ts +207 -0
  93. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  94. package/dist/types/src/physics/World.d.ts +274 -0
  95. package/dist/types/src/physics/index.d.ts +34 -0
  96. package/index.js +78 -0
  97. package/package.json +73 -60
  98. package/src/Animator.js +95 -0
  99. package/src/BitmapText.js +6 -5
  100. package/src/Camera.js +183 -0
  101. package/src/CameraController.js +192 -0
  102. package/src/CanvasText.js +281 -0
  103. package/src/CollisionLayers.js +86 -0
  104. package/src/Color.js +26 -18
  105. package/src/Coroutine.js +259 -0
  106. package/src/DebugOverlay.js +246 -0
  107. package/src/Drawable.js +842 -582
  108. package/src/Easing.js +57 -0
  109. package/src/Emerald.js +1150 -459
  110. package/src/EmeraldDB.js +328 -0
  111. package/src/FPSCounter.js +43 -43
  112. package/src/GLUtils.js +60 -67
  113. package/src/Instance.js +41 -5
  114. package/src/InstancedTexture.js +308 -127
  115. package/src/Interpolator.js +124 -0
  116. package/src/Material.js +202 -0
  117. package/src/MathUtils.js +134 -0
  118. package/src/ParticleEmitter.js +284 -0
  119. package/src/Physics.js +431 -40
  120. package/src/Pool.js +85 -0
  121. package/src/PostEffects.js +296 -0
  122. package/src/PostProcessor.js +304 -0
  123. package/src/RenderTarget.js +134 -0
  124. package/src/Scene.js +115 -83
  125. package/src/ScreenEffects.js +266 -0
  126. package/src/Serializer.js +131 -0
  127. package/src/Shaders.js +150 -165
  128. package/src/Shapes.js +118 -129
  129. package/src/SpatialGrid.js +111 -0
  130. package/src/SpriteBatch.js +299 -0
  131. package/src/StateMachine.js +82 -0
  132. package/src/Storage.js +175 -47
  133. package/src/Texture.js +58 -67
  134. package/src/TextureAtlas.js +96 -0
  135. package/src/Tilemap.js +274 -0
  136. package/src/Time.js +51 -6
  137. package/src/Timer.js +99 -0
  138. package/src/Transform.js +100 -7
  139. package/src/Tween.js +160 -0
  140. package/src/UI.js +394 -0
  141. package/src/components/Behaviour.js +90 -0
  142. package/src/components/BoxCollider.js +29 -12
  143. package/src/components/BoxColliderDebug.js +3 -4
  144. package/src/components/CircleCollider.js +26 -12
  145. package/src/components/CircleColliderDebug.js +25 -24
  146. package/src/components/Collider.js +150 -34
  147. package/src/components/GameObject.js +130 -21
  148. package/src/components/PolygonCollider.js +55 -0
  149. package/src/components/RigidBody.js +556 -8
  150. package/src/importers/Aseprite.js +142 -0
  151. package/src/importers/ForgeLevel.js +581 -0
  152. package/src/importers/TiledMap.js +158 -0
  153. package/src/lights/DirectionalLight.js +6 -15
  154. package/src/lights/PointLight.js +4 -4
  155. package/src/managers/AssetManager.js +239 -0
  156. package/src/managers/AudioManager.js +565 -146
  157. package/src/managers/EventManager.js +488 -477
  158. package/src/managers/GLManager.js +57 -0
  159. package/src/managers/GLState.js +70 -0
  160. package/src/managers/Gamepad.js +126 -0
  161. package/src/managers/IDManager.js +24 -2
  162. package/src/managers/InputManager.js +905 -0
  163. package/src/managers/NetworkManager.js +178 -0
  164. package/src/managers/RenderStats.js +34 -0
  165. package/src/managers/SceneManager.js +30 -0
  166. package/src/managers/ShaderManager.js +0 -2
  167. package/src/managers/TextureManager.js +218 -0
  168. package/src/particlesystem/Particle.js +82 -7
  169. package/src/particlesystem/ParticleSettings.js +21 -3
  170. package/src/particlesystem/Particles.js +80 -31
  171. package/src/physics/AABB.js +207 -0
  172. package/src/physics/Body.js +862 -0
  173. package/src/physics/BodyType.js +16 -0
  174. package/src/physics/BroadPhase.js +641 -0
  175. package/src/physics/Collision.js +534 -0
  176. package/src/physics/Contact.js +500 -0
  177. package/src/physics/ContactSolver.js +526 -0
  178. package/src/physics/Distance.js +403 -0
  179. package/src/physics/DistanceJoint.js +227 -0
  180. package/src/physics/Fixture.js +346 -0
  181. package/src/physics/Island.js +203 -0
  182. package/src/physics/Joint.js +78 -0
  183. package/src/physics/Math2D.js +573 -0
  184. package/src/physics/RevoluteJoint.js +278 -0
  185. package/src/physics/Settings.js +78 -0
  186. package/src/physics/Shapes.js +549 -0
  187. package/src/physics/TimeOfImpact.js +87 -0
  188. package/src/physics/World.js +731 -0
  189. package/src/physics/index.js +79 -0
  190. package/dist/types/Drawable.d.ts +0 -157
  191. package/dist/types/Emerald.d.ts +0 -73
  192. package/dist/types/GLUtils.d.ts +0 -6
  193. package/dist/types/Physics.d.ts +0 -139
  194. package/dist/types/Scene.d.ts +0 -39
  195. package/dist/types/Shaders.d.ts +0 -4
  196. package/dist/types/Storage.d.ts +0 -46
  197. package/dist/types/Time.d.ts +0 -22
  198. package/dist/types/Transform.d.ts +0 -41
  199. package/dist/types/components/BoxColliderDebug.d.ts +0 -19
  200. package/dist/types/components/CircleColliderDebug.d.ts +0 -19
  201. package/dist/types/components/Collider.d.ts +0 -53
  202. package/dist/types/components/GameObject.d.ts +0 -72
  203. package/dist/types/components/RigidBody.d.ts +0 -104
  204. package/dist/types/managers/AudioManager.d.ts +0 -60
  205. package/dist/types/managers/GLManager.d.ts +0 -47
  206. package/dist/types/managers/IDManager.d.ts +0 -21
  207. package/dist/types/managers/SceneManager.d.ts +0 -22
  208. package/dist/types/particlesystem/Particle.d.ts +0 -42
@@ -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;