littlejsengine 1.18.28 → 1.19.3
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/FAQ.md +5 -0
- package/README.md +207 -199
- package/dist/littlejs.d.ts +2099 -48
- package/dist/littlejs.esm.js +16251 -10315
- package/dist/littlejs.esm.min.js +13 -1
- package/dist/littlejs.js +15747 -9888
- package/dist/littlejs.min.js +13 -1
- package/dist/littlejs.release.js +15719 -9867
- package/package.json +1 -1
- package/plugins/audioEffects.js +371 -0
- package/plugins/lightSystem.js +5 -3
- package/plugins/math3d.js +870 -0
- package/plugins/medalSystem.js +280 -280
- package/plugins/pluginExport.js +181 -115
- package/plugins/postProcess.js +241 -166
- package/plugins/render3d.js +4006 -0
- package/plugins/textureSheet.js +458 -458
- package/plugins/threejs.js +6 -1
- package/plugins/tweenSystem.js +528 -526
- package/plugins/zzfxm.js +159 -166
- package/src/engine.js +172 -137
- package/src/engineAudio.js +918 -775
- package/src/engineBuild.mjs +3 -0
- package/src/engineDebug.js +15 -8
- package/src/engineDraw.js +1672 -1510
- package/src/engineExport.js +407 -396
- package/src/engineInput.js +1 -1
- package/src/engineLogo.js +4 -3
- package/src/engineMath.js +69 -0
- package/src/engineObject.js +560 -551
- package/src/engineSettings.js +759 -725
- package/src/engineTileLayer.js +3 -3
- package/src/engineUtilities.js +13 -0
- package/src/engineWebGL.js +1005 -941
package/src/engine.js
CHANGED
|
@@ -32,7 +32,7 @@ const engineName = 'LittleJS';
|
|
|
32
32
|
* @type {string}
|
|
33
33
|
* @default
|
|
34
34
|
* @memberof Engine */
|
|
35
|
-
const engineVersion = '1.
|
|
35
|
+
const engineVersion = '1.19.3';
|
|
36
36
|
|
|
37
37
|
/** Frames per second to update
|
|
38
38
|
* @type {number}
|
|
@@ -89,6 +89,7 @@ function setPaused(isPaused=true) { paused = isPaused; }
|
|
|
89
89
|
|
|
90
90
|
// Engine internal variables
|
|
91
91
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
92
|
+
let windowWidthLast = 0, windowHeightLast = 0, windowPixelRatioLast = 0;
|
|
92
93
|
let engineUpdateInternal; // assigned by engineInit so engineStep can drive it
|
|
93
94
|
let showEngineVersion = true;
|
|
94
95
|
|
|
@@ -98,12 +99,13 @@ let showEngineVersion = true;
|
|
|
98
99
|
const pluginList = [];
|
|
99
100
|
class EnginePlugin
|
|
100
101
|
{
|
|
101
|
-
constructor(update, render, glContextLost, glContextRestored)
|
|
102
|
+
constructor(update, render, glContextLost, glContextRestored, preRender)
|
|
102
103
|
{
|
|
103
104
|
this.update = update;
|
|
104
105
|
this.render = render;
|
|
105
106
|
this.glContextLost = glContextLost;
|
|
106
107
|
this.glContextRestored = glContextRestored;
|
|
108
|
+
this.preRender = preRender;
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
|
|
@@ -117,16 +119,18 @@ class EnginePlugin
|
|
|
117
119
|
* @param {PluginCallback} [render]
|
|
118
120
|
* @param {PluginCallback} [glContextLost]
|
|
119
121
|
* @param {PluginCallback} [glContextRestored]
|
|
122
|
+
* @param {PluginCallback} [preRender] - Called after the canvas is cleared and before gameRender
|
|
120
123
|
* @memberof Engine */
|
|
121
|
-
function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
124
|
+
function engineAddPlugin(update, render, glContextLost, glContextRestored, preRender)
|
|
122
125
|
{
|
|
123
126
|
// make sure plugin functions are unique
|
|
124
127
|
ASSERT(!pluginList.find(p=>
|
|
125
128
|
p.update === update && p.render === render &&
|
|
126
129
|
p.glContextLost === glContextLost &&
|
|
127
|
-
p.glContextRestored === glContextRestored
|
|
130
|
+
p.glContextRestored === glContextRestored &&
|
|
131
|
+
p.preRender === preRender));
|
|
128
132
|
|
|
129
|
-
const plugin = new EnginePlugin(update, render, glContextLost, glContextRestored);
|
|
133
|
+
const plugin = new EnginePlugin(update, render, glContextLost, glContextRestored, preRender);
|
|
130
134
|
pluginList.push(plugin);
|
|
131
135
|
}
|
|
132
136
|
|
|
@@ -186,14 +190,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
186
190
|
// Called automatically by engine to setup render system
|
|
187
191
|
function enginePreRender()
|
|
188
192
|
{
|
|
189
|
-
//
|
|
190
|
-
|
|
193
|
+
// mainCanvasSize is set by engineUpdateCanvas which always runs first,
|
|
194
|
+
// it is css pixels so it does not match the canvas backing store
|
|
191
195
|
|
|
192
196
|
// disable smoothing for pixel art
|
|
193
197
|
mainContext.imageSmoothingEnabled = !tilesPixelated;
|
|
194
198
|
|
|
195
199
|
// setup gl rendering if enabled
|
|
196
200
|
glPreRender();
|
|
201
|
+
|
|
202
|
+
// plugins that draw underneath the 2D layer
|
|
203
|
+
pluginList.forEach(plugin=>plugin.preRender?.());
|
|
197
204
|
}
|
|
198
205
|
|
|
199
206
|
// internal update loop for engine
|
|
@@ -212,25 +219,47 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
212
219
|
const debugScale = debugSpeedUp ? 10 : debugSpeedDown ? .1 : 1;
|
|
213
220
|
|
|
214
221
|
// apply time deltas
|
|
222
|
+
const frameTimeDeltaUnscaledMS = frameTimeDeltaMS;
|
|
215
223
|
timeReal += frameTimeDeltaMS * debugScale / 1e3;
|
|
216
224
|
const combinedScale = timeScale * debugScale;
|
|
217
225
|
frameTimeDeltaMS *= combinedScale;
|
|
218
|
-
|
|
219
|
-
|
|
226
|
+
// when paused tick on unscaled time so the pause update rate stays
|
|
227
|
+
// fixed instead of following however fast the display refreshes
|
|
228
|
+
frameTimeBufferMS += paused ? frameTimeDeltaUnscaledMS : frameTimeDeltaMS;
|
|
229
|
+
if (paused || combinedScale <= 1)
|
|
220
230
|
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
221
231
|
|
|
222
|
-
|
|
223
|
-
|
|
232
|
+
// apply time delta smoothing, improves smoothness of framerate in some browsers
|
|
233
|
+
let wasUpdated = false, deltaSmooth = 0;
|
|
234
|
+
if (frameTimeBufferMS < 0 && frameTimeBufferMS > -9)
|
|
224
235
|
{
|
|
225
|
-
// update
|
|
236
|
+
// force at least one update each frame since it is waiting for refresh
|
|
237
|
+
deltaSmooth = frameTimeBufferMS;
|
|
238
|
+
frameTimeBufferMS = 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// update multiple frames if necessary in case of slow framerate
|
|
242
|
+
for (; frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
243
|
+
{
|
|
244
|
+
// increment frame and update time, paused does not advance time
|
|
245
|
+
if (!paused)
|
|
246
|
+
time = frame++ / frameRate;
|
|
247
|
+
|
|
248
|
+
// update game and objects, when paused update everything except them
|
|
226
249
|
wasUpdated = true;
|
|
227
|
-
|
|
250
|
+
engineUpdateCanvas();
|
|
228
251
|
inputUpdate();
|
|
252
|
+
if (!paused)
|
|
253
|
+
gameUpdate();
|
|
229
254
|
pluginList.forEach(plugin=>plugin.update?.());
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
o
|
|
255
|
+
if (paused)
|
|
256
|
+
{
|
|
257
|
+
// update object transforms even when paused
|
|
258
|
+
for (const o of engineObjects)
|
|
259
|
+
o.parent || o.updateTransforms();
|
|
260
|
+
}
|
|
261
|
+
else
|
|
262
|
+
engineObjectsUpdate();
|
|
234
263
|
|
|
235
264
|
// do post update
|
|
236
265
|
debugUpdate();
|
|
@@ -239,44 +268,26 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
239
268
|
if (debugVideoCaptureIsActive())
|
|
240
269
|
renderFrame();
|
|
241
270
|
}
|
|
242
|
-
else
|
|
243
|
-
{
|
|
244
|
-
// apply time delta smoothing, improves smoothness of framerate in some browsers
|
|
245
|
-
let deltaSmooth = 0;
|
|
246
|
-
if (frameTimeBufferMS < 0 && frameTimeBufferMS > -9)
|
|
247
|
-
{
|
|
248
|
-
// force at least one update each frame since it is waiting for refresh
|
|
249
|
-
deltaSmooth = frameTimeBufferMS;
|
|
250
|
-
frameTimeBufferMS = 0;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// update multiple frames if necessary in case of slow framerate
|
|
254
|
-
for (; frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
255
|
-
{
|
|
256
|
-
// increment frame and update time
|
|
257
|
-
time = frame++ / frameRate;
|
|
258
|
-
|
|
259
|
-
// update game and objects
|
|
260
|
-
wasUpdated = true;
|
|
261
|
-
updateCanvas();
|
|
262
|
-
inputUpdate();
|
|
263
|
-
gameUpdate();
|
|
264
|
-
pluginList.forEach(plugin=>plugin.update?.());
|
|
265
|
-
engineObjectsUpdate();
|
|
266
271
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
gameUpdatePost();
|
|
270
|
-
inputUpdatePost();
|
|
271
|
-
if (debugVideoCaptureIsActive())
|
|
272
|
-
renderFrame();
|
|
273
|
-
}
|
|
272
|
+
// add the time smoothing back in
|
|
273
|
+
frameTimeBufferMS += deltaSmooth;
|
|
274
274
|
|
|
275
|
-
|
|
276
|
-
|
|
275
|
+
// check if the window changed so a resize is picked up even when
|
|
276
|
+
// the game is not updating, for example when timeScale is 0
|
|
277
|
+
let windowChanged = false;
|
|
278
|
+
if (!headlessMode)
|
|
279
|
+
{
|
|
280
|
+
const dpr = devicePixelRatio;
|
|
281
|
+
windowChanged = windowWidthLast !== innerWidth ||
|
|
282
|
+
windowHeightLast !== innerHeight || windowPixelRatioLast !== dpr;
|
|
283
|
+
windowWidthLast = innerWidth;
|
|
284
|
+
windowHeightLast = innerHeight;
|
|
285
|
+
windowPixelRatioLast = dpr;
|
|
277
286
|
}
|
|
278
287
|
|
|
279
|
-
|
|
288
|
+
// render only when something changed, displays that refresh faster
|
|
289
|
+
// than the fixed update rate would otherwise redraw identical frames
|
|
290
|
+
if (!debugVideoCaptureIsActive() && (wasUpdated || windowChanged))
|
|
280
291
|
renderFrame();
|
|
281
292
|
if (!engineManualStep)
|
|
282
293
|
requestAnimationFrame(engineUpdate);
|
|
@@ -287,14 +298,19 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
287
298
|
|
|
288
299
|
// canvas must be updated before rendering
|
|
289
300
|
if (!wasUpdated)
|
|
290
|
-
|
|
301
|
+
engineUpdateCanvas();
|
|
291
302
|
|
|
292
303
|
// render the game and objects
|
|
293
304
|
enginePreRender();
|
|
294
305
|
gameRender();
|
|
295
306
|
engineObjects.sort((a,b)=> a.renderOrder - b.renderOrder);
|
|
296
307
|
for (const o of engineObjects)
|
|
297
|
-
|
|
308
|
+
{
|
|
309
|
+
if (o.destroyed) continue;
|
|
310
|
+
setShader(o.shader); // each object draws with its own shader, or none
|
|
311
|
+
o.render();
|
|
312
|
+
}
|
|
313
|
+
setShader(); // back to the engine's for gameRenderPost
|
|
298
314
|
|
|
299
315
|
// post rendering
|
|
300
316
|
gameRenderPost();
|
|
@@ -309,84 +325,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
309
325
|
}
|
|
310
326
|
engineUpdateInternal = engineUpdate;
|
|
311
327
|
|
|
312
|
-
function updateCanvas()
|
|
313
|
-
{
|
|
314
|
-
if (headlessMode) return;
|
|
315
|
-
|
|
316
|
-
if (canvasFixedSize.x)
|
|
317
|
-
{
|
|
318
|
-
// set canvas fixed size
|
|
319
|
-
mainCanvasSize = canvasFixedSize.copy();
|
|
320
|
-
|
|
321
|
-
// fit to window using css width and height
|
|
322
|
-
const innerAspect = innerWidth / innerHeight;
|
|
323
|
-
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
324
|
-
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
325
|
-
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
326
|
-
mainCanvas.style.width = w;
|
|
327
|
-
mainCanvas.style.height = h;
|
|
328
|
-
if (glCanvas)
|
|
329
|
-
{
|
|
330
|
-
glCanvas.style.width = w;
|
|
331
|
-
glCanvas.style.height = h;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
else
|
|
335
|
-
{
|
|
336
|
-
// apply device pixel ratio for crisp rendering
|
|
337
|
-
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
338
|
-
const viewWidth = innerWidth * dpr | 0;
|
|
339
|
-
const viewHeight = innerHeight * dpr | 0;
|
|
340
|
-
|
|
341
|
-
// get main canvas size based on window size
|
|
342
|
-
mainCanvasSize.x = min(viewWidth, canvasMaxSize.x);
|
|
343
|
-
mainCanvasSize.y = min(viewHeight, canvasMaxSize.y);
|
|
344
|
-
|
|
345
|
-
// responsive aspect ratio with native resolution
|
|
346
|
-
const innerAspect = viewWidth / viewHeight;
|
|
347
|
-
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
348
|
-
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
349
|
-
{
|
|
350
|
-
// full height
|
|
351
|
-
const w = mainCanvasSize.y * canvasMaxAspect | 0;
|
|
352
|
-
mainCanvasSize.x = min(w, canvasMaxSize.x);
|
|
353
|
-
}
|
|
354
|
-
else if (innerAspect < canvasMinAspect)
|
|
355
|
-
{
|
|
356
|
-
// full width
|
|
357
|
-
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
358
|
-
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// set CSS display size so backing store renders at viewport size
|
|
362
|
-
const cssW = (mainCanvasSize.x / dpr | 0) + 'px';
|
|
363
|
-
const cssH = (mainCanvasSize.y / dpr | 0) + 'px';
|
|
364
|
-
mainCanvas.style.width = cssW;
|
|
365
|
-
mainCanvas.style.height = cssH;
|
|
366
|
-
if (glCanvas)
|
|
367
|
-
{
|
|
368
|
-
glCanvas.style.width = cssW;
|
|
369
|
-
glCanvas.style.height = cssH;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// clear main canvas and set size
|
|
374
|
-
mainCanvas.width = mainCanvasSize.x;
|
|
375
|
-
mainCanvas.height = mainCanvasSize.y;
|
|
376
|
-
|
|
377
|
-
// apply the clear color to main canvas
|
|
378
|
-
if (canvasClearColor.a > 0 && !glEnable)
|
|
379
|
-
{
|
|
380
|
-
mainContext.fillStyle = canvasClearColor.toString();
|
|
381
|
-
mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
382
|
-
mainContext.fillStyle = BLACK.toString();
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// set default line join and cap
|
|
386
|
-
mainContext.lineJoin = 'round';
|
|
387
|
-
mainContext.lineCap = 'round';
|
|
388
|
-
}
|
|
389
|
-
|
|
390
328
|
// skip setup if headless
|
|
391
329
|
if (headlessMode) return startEngine();
|
|
392
330
|
|
|
@@ -419,14 +357,14 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
419
357
|
if (glCanvas)
|
|
420
358
|
glCanvas.style.cssText = styleCanvas;
|
|
421
359
|
setCanvasPixelated(canvasPixelated);
|
|
422
|
-
|
|
360
|
+
engineUpdateCanvas();
|
|
423
361
|
glPreRender();
|
|
424
362
|
|
|
425
363
|
// create offscreen canvases for image processing
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
364
|
+
workContext = createCanvasContext(64);
|
|
365
|
+
workCanvas = workContext.canvas;
|
|
366
|
+
workReadContext = createCanvasContext(64, 64, true);
|
|
367
|
+
workReadCanvas = workReadContext.canvas;
|
|
430
368
|
|
|
431
369
|
// create promises for loading images
|
|
432
370
|
const promises = imageSources.map((src, i)=> loadTexture(i, src));
|
|
@@ -466,6 +404,102 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
466
404
|
}
|
|
467
405
|
}
|
|
468
406
|
|
|
407
|
+
// Resize the canvas to fit the window and prepare it for a new frame
|
|
408
|
+
// Called automatically each frame and by the splash screen before the loop starts
|
|
409
|
+
// mainCanvasSize is css pixels and the backing store is that scaled by the
|
|
410
|
+
// pixel ratio, so the ratio only changes sharpness, never how big things look
|
|
411
|
+
function engineUpdateCanvas()
|
|
412
|
+
{
|
|
413
|
+
if (headlessMode) return;
|
|
414
|
+
|
|
415
|
+
// the backing store is scaled by this, every size below is css pixels
|
|
416
|
+
const dpr = getCanvasPixelRatio();
|
|
417
|
+
|
|
418
|
+
if (canvasFixedSize.x)
|
|
419
|
+
{
|
|
420
|
+
// set canvas fixed size
|
|
421
|
+
mainCanvasSize = canvasFixedSize.copy();
|
|
422
|
+
|
|
423
|
+
// fit to window using css width and height
|
|
424
|
+
const innerAspect = innerWidth / innerHeight;
|
|
425
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
426
|
+
const w = innerAspect < fixedAspect ? '100%' : '';
|
|
427
|
+
const h = innerAspect < fixedAspect ? '' : '100%';
|
|
428
|
+
mainCanvas.style.width = w;
|
|
429
|
+
mainCanvas.style.height = h;
|
|
430
|
+
if (glCanvas)
|
|
431
|
+
{
|
|
432
|
+
glCanvas.style.width = w;
|
|
433
|
+
glCanvas.style.height = h;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
else
|
|
437
|
+
{
|
|
438
|
+
// get main canvas size based on window size, in css pixels so
|
|
439
|
+
// canvasMaxSize caps how big the canvas looks, not its resolution
|
|
440
|
+
mainCanvasSize.x = min(innerWidth, canvasMaxSize.x) | 0;
|
|
441
|
+
mainCanvasSize.y = min(innerHeight, canvasMaxSize.y) | 0;
|
|
442
|
+
|
|
443
|
+
// responsive aspect ratio
|
|
444
|
+
const innerAspect = innerWidth / innerHeight;
|
|
445
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
446
|
+
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
447
|
+
{
|
|
448
|
+
// full height
|
|
449
|
+
const w = mainCanvasSize.y * canvasMaxAspect | 0;
|
|
450
|
+
mainCanvasSize.x = min(w, canvasMaxSize.x);
|
|
451
|
+
}
|
|
452
|
+
else if (innerAspect < canvasMinAspect)
|
|
453
|
+
{
|
|
454
|
+
// full width
|
|
455
|
+
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
456
|
+
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// css size is the canvas size, the backing store is scaled up below
|
|
460
|
+
mainCanvas.style.width = mainCanvasSize.x + 'px';
|
|
461
|
+
mainCanvas.style.height = mainCanvasSize.y + 'px';
|
|
462
|
+
if (glCanvas)
|
|
463
|
+
{
|
|
464
|
+
glCanvas.style.width = mainCanvasSize.x + 'px';
|
|
465
|
+
glCanvas.style.height = mainCanvasSize.y + 'px';
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// clear main canvas and set size
|
|
470
|
+
// only set the size when it changes, setting it invalidates the canvas
|
|
471
|
+
// frame which makes the browser rebuild the display list for the page
|
|
472
|
+
const bufferSizeX = mainCanvasSize.x * dpr | 0;
|
|
473
|
+
const bufferSizeY = mainCanvasSize.y * dpr | 0;
|
|
474
|
+
if (mainCanvas.width !== bufferSizeX || mainCanvas.height !== bufferSizeY)
|
|
475
|
+
{
|
|
476
|
+
mainCanvas.width = bufferSizeX;
|
|
477
|
+
mainCanvas.height = bufferSizeY;
|
|
478
|
+
}
|
|
479
|
+
else
|
|
480
|
+
{
|
|
481
|
+
// setting the size also resets the context state, match that
|
|
482
|
+
mainContext.setTransform(1, 0, 0, 1, 0, 0);
|
|
483
|
+
mainContext.globalCompositeOperation = 'source-over';
|
|
484
|
+
mainContext.clearRect(0, 0, bufferSizeX, bufferSizeY);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// scale the context so 2d drawing is in css pixels
|
|
488
|
+
mainContext.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
489
|
+
|
|
490
|
+
// apply the clear color to main canvas
|
|
491
|
+
if (canvasClearColor.a > 0 && !glEnable)
|
|
492
|
+
{
|
|
493
|
+
mainContext.fillStyle = canvasClearColor.toString();
|
|
494
|
+
mainContext.fillRect(0, 0, mainCanvasSize.x, mainCanvasSize.y);
|
|
495
|
+
mainContext.fillStyle = BLACK.toString();
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// set default line join and cap
|
|
499
|
+
mainContext.lineJoin = 'round';
|
|
500
|
+
mainContext.lineCap = 'round';
|
|
501
|
+
}
|
|
502
|
+
|
|
469
503
|
// max frames engineStep can advance in one call, 10 minutes at 60fps
|
|
470
504
|
// large counts block until they finish, so this catches runaway values
|
|
471
505
|
const engineStepMaxFrames = 36000;
|
|
@@ -534,13 +568,14 @@ function engineObjectsUpdate()
|
|
|
534
568
|
|
|
535
569
|
/** Destroy and remove all objects
|
|
536
570
|
* - This can be used to clear out all objects when restarting a level
|
|
571
|
+
* - Objects with the persistent flag set are left alone, for things that outlive a level
|
|
537
572
|
* - Objects can override their destroy function to do cleanup or stick around
|
|
538
573
|
* @param {boolean} [immediate] - should attached effects be allowed to die off?
|
|
539
574
|
* @memberof Engine */
|
|
540
575
|
function engineObjectsDestroy(immediate=true)
|
|
541
576
|
{
|
|
542
577
|
for (const o of engineObjects)
|
|
543
|
-
o.parent || o.destroy(immediate);
|
|
578
|
+
o.parent || o.persistent || o.destroy(immediate);
|
|
544
579
|
engineObjects = engineObjects.filter(o=>!o.destroyed);
|
|
545
580
|
}
|
|
546
581
|
|