littlejsengine 1.1.18 → 1.2.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.
Files changed (39) hide show
  1. package/README.md +17 -11
  2. package/build.bat +1 -0
  3. package/engine/engine.all.js +495 -504
  4. package/engine/engine.all.min.js +1 -1
  5. package/engine/engine.all.release.js +418 -259
  6. package/engine/engine.js +31 -23
  7. package/engine/engineAudio.js +21 -7
  8. package/engine/engineBuild.bat +9 -1
  9. package/engine/engineBuildSetup.bat +2 -1
  10. package/engine/engineDebug.js +81 -247
  11. package/engine/engineDraw.js +109 -21
  12. package/engine/engineFont.png +0 -0
  13. package/engine/engineInput.js +103 -50
  14. package/engine/engineMedals.js +17 -20
  15. package/engine/engineObject.js +14 -23
  16. package/engine/engineParticles.js +8 -6
  17. package/engine/engineRelease.js +3 -1
  18. package/engine/engineSettings.js +20 -10
  19. package/engine/engineTileLayer.js +19 -18
  20. package/engine/engineUtilities.js +14 -12
  21. package/engine/engineWebGL.js +59 -68
  22. package/engine/index.d.ts +1617 -0
  23. package/examples/breakout/game.js +9 -7
  24. package/examples/breakout/gameObjects.js +3 -3
  25. package/examples/breakout/index.html +6 -4
  26. package/examples/platformer/game.js +5 -5
  27. package/examples/platformer/gameEffects.js +11 -14
  28. package/examples/platformer/gameLevel.js +14 -7
  29. package/examples/platformer/gameObjects.js +3 -3
  30. package/examples/platformer/gamePlayer.js +13 -8
  31. package/examples/platformer/index.html +9 -7
  32. package/examples/platformer/tiles.png +0 -0
  33. package/examples/puzzle/game.js +2 -2
  34. package/examples/puzzle/index.html +5 -3
  35. package/examples/screenshot.jpg +0 -0
  36. package/examples/stress/index.html +7 -5
  37. package/game.js +9 -8
  38. package/index.html +16 -14
  39. package/package.json +1 -1
package/engine/engine.js CHANGED
@@ -22,7 +22,7 @@
22
22
  const engineName = 'LittleJS';
23
23
 
24
24
  /** Version of engine */
25
- const engineVersion = '1.1.18';
25
+ const engineVersion = '1.2.3';
26
26
 
27
27
  /** Frames per second to update objects
28
28
  * @default */
@@ -35,7 +35,7 @@ const timeDelta = 1/frameRate;
35
35
  /** Array containing all engine objects */
36
36
  let engineObjects = [];
37
37
 
38
- /** Array containing only objects that are set to collide with other objects (for optimization) */
38
+ /** Array containing only objects that are set to collide with other objects this frame (for optimization) */
39
39
  let engineObjectsCollide = [];
40
40
 
41
41
  /** Current update frame, used to calculate time */
@@ -51,8 +51,15 @@ let timeReal = 0;
51
51
  let paused = 0;
52
52
 
53
53
  // Engine internal variables not exposed to documentation
54
- let frameTimeLastMS = 0, frameTimeBufferMS = 0, debugFPS = 0,
55
- shrinkTilesX, shrinkTilesY, drawCount, tileImageSize, tileImageSizeInverse;
54
+ let frameTimeLastMS = 0, frameTimeBufferMS = 0, tileImageSize, tileImageFixBleed;
55
+
56
+ // Engine stat tracking, if showWatermark is true
57
+ let averageFPS, drawCount;
58
+
59
+ // css text used for elements created by engine
60
+ const styleBody = 'margin:0;overflow:hidden;background:#000' +
61
+ ';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
62
+ const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
56
63
 
57
64
  ///////////////////////////////////////////////////////////////////////////////
58
65
 
@@ -70,17 +77,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
70
77
  tileImage.onerror = tileImage.onload = ()=>
71
78
  {
72
79
  // save tile image info
73
- tileImageSizeInverse = vec2(1).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
80
+ tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
74
81
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
75
- shrinkTilesX = tileBleedShrinkFix/tileImageSize.x;
76
- shrinkTilesY = tileBleedShrinkFix/tileImageSize.y;
77
82
 
78
83
  // setup html
84
+ document.body.style = styleBody;
79
85
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
80
- document.body.style = 'margin:0;overflow:hidden;background:#000' +
81
- (touchMouseEnable?';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none':'');
82
- mainCanvas.style = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
83
86
  mainContext = mainCanvas.getContext('2d');
87
+ mainCanvas.style = styleCanvas;
84
88
 
85
89
  // init stuff and start engine
86
90
  debugInit();
@@ -88,8 +92,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
88
92
 
89
93
  // create overlay canvas for hud to appear above gl canvas
90
94
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
91
- overlayCanvas.style = mainCanvas.style.cssText;
92
95
  overlayContext = overlayCanvas.getContext('2d');
96
+ overlayCanvas.style = styleCanvas;
93
97
 
94
98
  gameInit();
95
99
  touchGamepadCreate();
@@ -105,7 +109,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
105
109
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
106
110
  frameTimeLastMS = frameTimeMS;
107
111
  if (debug || showWatermark)
108
- debugFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1), debugFPS);
112
+ averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
109
113
  if (debug)
110
114
  frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
111
115
  timeReal += frameTimeDeltaMS / 1e3;
@@ -192,7 +196,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
192
196
  overlayContext.font = '1em monospace';
193
197
  overlayContext.fillStyle = '#000';
194
198
  const text = engineName + ' ' + 'v' + engineVersion + ' / '
195
- + drawCount + ' / ' + engineObjects.length + ' / ' + debugFPS.toFixed(1);
199
+ + drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
200
+ + ' ' + (glEnable ? 'GL' : '2D') ;
196
201
  overlayContext.fillText(text, mainCanvas.width-3, 3);
197
202
  overlayContext.fillStyle = '#fff';
198
203
  overlayContext.fillText(text, mainCanvas.width-2, 2);
@@ -209,7 +214,11 @@ function enginePreRender()
209
214
  {
210
215
  // save canvas size and clear canvases
211
216
  mainCanvasSize = vec2(overlayCanvas.width = mainCanvas.width, overlayCanvas.height = mainCanvas.height);
212
- mainContext.imageSmoothingEnabled = !cavasPixelated; // disable smoothing for pixel art
217
+
218
+ // disable smoothing for pixel art
219
+ mainContext.imageSmoothingEnabled = !cavasPixelated;
220
+
221
+ // setup gl rendering if enabled
213
222
  glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
214
223
  }
215
224
 
@@ -218,6 +227,9 @@ function enginePreRender()
218
227
  /** Calls update on each engine object (recursively if child), removes destroyed objects, and updated time */
219
228
  function engineObjectsUpdate()
220
229
  {
230
+ // get list of solid objects for physics optimzation
231
+ engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
232
+
221
233
  // recursive object update
222
234
  const updateObject = (o)=>
223
235
  {
@@ -233,17 +245,16 @@ function engineObjectsUpdate()
233
245
 
234
246
  // remove destroyed objects
235
247
  engineObjects = engineObjects.filter(o=>!o.destroyed);
236
- engineObjectsCollide = engineObjectsCollide.filter(o=>!o.destroyed);
237
248
 
238
249
  // increment frame and update time
239
250
  time = ++frame / frameRate;
240
251
  }
241
252
 
242
- /** Detroy and remove all objects that are not persistent or descendants of a persistent object */
253
+ /** Destroy and remove all objects */
243
254
  function engineObjectsDestroy()
244
255
  {
245
256
  for (const o of engineObjects)
246
- o.persistent || o.parent || o.destroy();
257
+ o.parent || o.destroy();
247
258
  engineObjects = engineObjects.filter(o=>!o.destroyed);
248
259
  }
249
260
 
@@ -254,21 +265,18 @@ function engineObjectsDestroy()
254
265
  * @param {Array} [objects=engineObjects] - List of objects to check */
255
266
  function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
256
267
  {
257
- if (!pos)
268
+ if (!pos) // all objects
258
269
  {
259
- // all objects
260
270
  for (const o of objects)
261
271
  callbackFunction(o);
262
272
  }
263
- else if (size.x != undefined)
273
+ else if (size.x != undefined) // bounding box test
264
274
  {
265
- // aabb test
266
275
  for (const o of objects)
267
276
  isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
268
277
  }
269
- else
278
+ else // circle test
270
279
  {
271
- // circle test
272
280
  const sizeSquared = size*size;
273
281
  for (const o of objects)
274
282
  pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
@@ -69,7 +69,7 @@ class Sound
69
69
  return; // out of range
70
70
 
71
71
  // attenuate volume by distance
72
- volume *= percent(lengthSquared**.5, range*this.taper, range);
72
+ volume *= percent(lengthSquared**.5, range, range*this.taper);
73
73
  }
74
74
 
75
75
  // get pan from screen space coords
@@ -195,7 +195,7 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
195
195
 
196
196
  /** Stop all queued speech
197
197
  * @memberof Audio */
198
- const stopSpeech = ()=> speechSynthesis && speechSynthesis.cancel();
198
+ const speakStop = ()=> speechSynthesis && speechSynthesis.cancel();
199
199
 
200
200
  /** Get frequency of a note on a musical scale
201
201
  * @param {Number} semitoneOffset - How many semitones away from the root note
@@ -226,6 +226,13 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
226
226
  if (!audioContext)
227
227
  audioContext = new (window.AudioContext||webkitAudioContext);
228
228
 
229
+ // fix stalled audio
230
+ audioContext.resume();
231
+
232
+ // prevent sounds from building up if they can't be played
233
+ if (audioContext.state != 'running')
234
+ return;
235
+
229
236
  // create buffer and source
230
237
  const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, zzfxR),
231
238
  source = audioContext.createBufferSource();
@@ -236,11 +243,18 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
236
243
  source.playbackRate.value = rate;
237
244
  source.loop = loop;
238
245
 
239
- // create pan and gain nodes
240
- source
241
- .connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1, -1)}))
242
- .connect(new GainNode(audioContext, {'gain':soundVolume*volume}))
243
- .connect(audioContext.destination);
246
+ // create and connect gain node (createGain is more widley spported then GainNode construtor)
247
+ const gainNode = audioContext.createGain();
248
+ gainNode.gain.value = soundVolume*volume;
249
+ gainNode.connect(audioContext.destination);
250
+
251
+ // connect source to gain
252
+ (
253
+ window.StereoPannerNode ? // create pan node if possible
254
+ source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
255
+ : source
256
+ )
257
+ .connect(gainNode);
244
258
 
245
259
  // play and return sound
246
260
  source.start();
@@ -87,7 +87,15 @@ if %ERRORLEVEL% NEQ 0 (
87
87
  )
88
88
 
89
89
  rem lightly minify with uglify
90
- call uglifyjs -o %OUTPUT_FILENAME% -- %OUTPUT_FILENAME%
90
+ call uglifyjs -o %OUTPUT_FILENAME% -- %OUTPUT_FILENAME%
91
+ if %ERRORLEVEL% NEQ 0 (
92
+ pause
93
+ exit /b %ERRORLEVEL%
94
+ )
95
+
96
+ rem --- BUILD TYPESCRIPT DEFINITIONS ---
97
+
98
+ call tsc engine.all.js --declaration --allowJs --emitDeclarationOnly --outFile index.d.ts
91
99
  if %ERRORLEVEL% NEQ 0 (
92
100
  pause
93
101
  exit /b %ERRORLEVEL%
@@ -3,4 +3,5 @@ rem Install these tools to build LittleJS
3
3
  call npm install -g google-closure-compiler
4
4
  call npm install -g uglify-js
5
5
  call npm install -g roadroller
6
- call npm install -g ect-bin
6
+ call npm install -g ect-bin
7
+ call npm install -g typescript
@@ -1,8 +1,15 @@
1
+ /*
2
+ LittleJS - Debug Build
3
+ MIT License - Copyright 2021 Frank Force
4
+ */
5
+
1
6
  /**
2
7
  * LittleJS Debug System
3
- * <br> - Debug overlay with mouse pick
8
+ * <br> - Press ~ to show debug overlay with mouse pick
9
+ * <br> - Number keys toggle debug functions
10
+ * <br> - +/- apply time scale
4
11
  * <br> - Debug primitive rendering
5
- * <br> - Save screenshots to disk
12
+ * <br> - Save a 2d canvas as an image
6
13
  * @namespace Debug
7
14
  */
8
15
 
@@ -34,7 +41,7 @@ let showWatermark = 1;
34
41
  let godMode = 0;
35
42
 
36
43
  // Engine internal variables not exposed to documentation
37
- let debugPrimitives = [], debugOverlay = 0, debugPhysics = 0, debugRaycast = 0,
44
+ let debugPrimitives = [], debugOverlay = 0, debugPhysics = 0, debugRaycast = 0,
38
45
  debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, downloadLink;
39
46
 
40
47
  ///////////////////////////////////////////////////////////////////////////////
@@ -153,25 +160,28 @@ const debugUpdate = ()=>
153
160
  {
154
161
  if (!debug)
155
162
  return;
156
-
163
+
157
164
  if (keyWasPressed(192)) // ~
158
165
  debugOverlay = !debugOverlay;
159
- if (keyWasPressed(49)) // 1
160
- debugPhysics = !debugPhysics, debugParticles = 0;
161
- if (keyWasPressed(50)) // 2
162
- debugParticles = !debugParticles, debugPhysics = 0;
163
- if (keyWasPressed(51)) // 3
164
- godMode = !godMode;
165
- if (keyWasPressed(53)) // 5
166
- debugTakeScreenshot = 1;
167
- //if (keyWasPressed(54)) // 6
168
- // debugToggleParticleEditor();
169
- if (keyWasPressed(55)) // 7
170
- debugGamepads = !debugGamepads;
171
- //if (keyWasPressed(56)) // 8
172
- //if (keyWasPressed(57)) // 9
173
- if (keyWasPressed(48)) // 0
174
- showWatermark = !showWatermark;
166
+ if (debugOverlay)
167
+ {
168
+ if (keyWasPressed(48)) // 0
169
+ showWatermark = !showWatermark;
170
+ if (keyWasPressed(49)) // 1
171
+ debugPhysics = !debugPhysics, debugParticles = 0;
172
+ if (keyWasPressed(50)) // 2
173
+ debugParticles = !debugParticles, debugPhysics = 0;
174
+ if (keyWasPressed(51)) // 3
175
+ debugGamepads = !debugGamepads;
176
+ if (keyWasPressed(52)) // 4
177
+ godMode = !godMode;
178
+ if (keyWasPressed(53)) // 5
179
+ debugTakeScreenshot = 1;
180
+ //if (keyWasPressed(54)) // 6
181
+ //if (keyWasPressed(55)) // 7
182
+ //if (keyWasPressed(56)) // 8
183
+ //if (keyWasPressed(57)) // 9
184
+ }
175
185
  }
176
186
 
177
187
  const debugRender = ()=>
@@ -191,21 +201,20 @@ const debugRender = ()=>
191
201
 
192
202
  if (debugGamepads && gamepadsEnable && navigator.getGamepads)
193
203
  {
194
- // poll gamepads
204
+ // gamepad debug display
195
205
  const gamepads = navigator.getGamepads();
196
206
  for (let i = gamepads.length; i--;)
197
207
  {
198
208
  const gamepad = gamepads[i];
199
209
  if (gamepad)
200
210
  {
201
- // gamepad debug display
202
211
  const stickScale = 1;
203
212
  const buttonScale = .2;
204
213
  const centerPos = cameraPos;
205
214
  const sticks = stickData[i];
206
215
  for (let j = sticks.length; j--;)
207
216
  {
208
- const drawPos = centerPos.add(vec2(j*stickScale*2,i*stickScale*3));
217
+ const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
209
218
  const stickPos = drawPos.add(sticks[j].scale(stickScale));
210
219
  debugCircle(drawPos, stickScale, '#fff7',0,1);
211
220
  debugLine(drawPos, stickPos, '#f00');
@@ -224,38 +233,36 @@ const debugRender = ()=>
224
233
 
225
234
  if (debugOverlay)
226
235
  {
236
+ // mouse pick
237
+ let bestDistance = Infinity, bestObject;
227
238
  for (const o of engineObjects)
228
239
  {
229
- if (o.canvas)
240
+ if (o.canvas || o.destroyed)
230
241
  continue; // skip tile layers
231
242
 
232
243
  const size = o.size.copy();
233
- size.x = max(size.x, .2);
234
- size.y = max(size.y, .2);
244
+ if (!size.x || !size.y)
245
+ continue;
235
246
 
247
+ const distance = mousePos.distanceSquared(o.pos);
248
+ if (distance < bestDistance)
249
+ {
250
+ bestDistance = distance;
251
+ bestObject = o
252
+ }
253
+
254
+ // show object info
236
255
  const color = new Color(
237
256
  o.collideTiles?1:0,
238
257
  o.collideSolidObjects?1:0,
239
258
  o.isSolid?1:0,
240
259
  o.parent ? .2 : .5);
241
-
242
- // show object info
260
+ size.x = max(size.x, .2);
261
+ size.y = max(size.y, .2);
243
262
  drawRect(o.pos, size, color);
244
263
  drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
245
264
  o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
246
265
  }
247
-
248
- // mouse pick
249
- let bestDistance = Infinity, bestObject;
250
- for (const o of engineObjects)
251
- {
252
- const distance = mousePos.distanceSquared(o.pos);
253
- if (distance < bestDistance)
254
- {
255
- bestDistance = distance;
256
- bestObject = o
257
- }
258
- }
259
266
 
260
267
  if (bestObject)
261
268
  {
@@ -275,7 +282,8 @@ const debugRender = ()=>
275
282
  pos = pos.add(height), ...args);
276
283
  drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
277
284
  drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
278
- drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height), ...args);
285
+ drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
286
+ drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
279
287
  mainContext = saveContext;
280
288
  }
281
289
 
@@ -283,16 +291,15 @@ const debugRender = ()=>
283
291
  }
284
292
 
285
293
  {
286
- // render debug rects
287
- overlayContext.lineWidth = 1;
294
+ // draw debug primitives
295
+ overlayContext.save();
296
+ overlayContext.lineWidth = 2;
288
297
  const pointSize = debugPointSize * cameraScale;
289
298
  debugPrimitives.forEach(p=>
290
299
  {
291
300
  // create canvas transform from world space to screen space
292
301
  const pos = worldToScreen(p.pos);
293
302
 
294
- overlayContext.save();
295
- overlayContext.lineWidth = 2;
296
303
  overlayContext.translate(pos.x|0, pos.y|0);
297
304
  overlayContext.rotate(p.angle);
298
305
  overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
@@ -325,15 +332,17 @@ const debugRender = ()=>
325
332
  p.fill && overlayContext.fill();
326
333
  overlayContext.stroke();
327
334
  }
328
-
329
- overlayContext.restore();
330
335
  });
331
336
 
332
- overlayContext.fillStyle = overlayContext.strokeStyle = '#fff';
337
+ // remove expired pritives
338
+ debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
339
+
340
+ overlayContext.restore();
333
341
  }
334
342
 
335
343
  {
336
- let x = 9, y = -20, h = 30;
344
+ // draw debug overlay
345
+ overlayContext.save();
337
346
  overlayContext.fillStyle = '#fff';
338
347
  overlayContext.textAlign = 'left';
339
348
  overlayContext.textBaseline = 'top';
@@ -341,6 +350,7 @@ const debugRender = ()=>
341
350
  overlayContext.shadowColor = '#000';
342
351
  overlayContext.shadowBlur = 9;
343
352
 
353
+ let x = 9, y = -20, h = 30;
344
354
  if (debugOverlay)
345
355
  {
346
356
  overlayContext.fillText(engineName, x, y += h);
@@ -353,214 +363,38 @@ const debugRender = ()=>
353
363
  overlayContext.fillText('1: Debug Physics', x, y += h);
354
364
  overlayContext.fillStyle = debugParticles ? '#f00' : '#fff';
355
365
  overlayContext.fillText('2: Debug Particles', x, y += h);
366
+ overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
367
+ overlayContext.fillText('3: Debug Gamepads', x, y += h);
356
368
  overlayContext.fillStyle = godMode ? '#f00' : '#fff';
357
- overlayContext.fillText('3: God Mode', x, y += h);
369
+ overlayContext.fillText('4: God Mode', x, y += h);
358
370
  overlayContext.fillStyle = '#fff';
359
371
  overlayContext.fillText('5: Save Screenshot', x, y += h);
360
- //overlayContext.fillStyle = debugParticleEditor ? '#f00' : '#fff';
361
- //overlayContext.fillText('6: Particle Editor', x, y += h);
362
- overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
363
- overlayContext.fillText('7: Debug Gamepads', x, y += h);
364
- }
365
- else
366
- {
367
- overlayContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
368
- overlayContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
369
- overlayContext.fillText(godMode ? 'God Mode' : '', x, y += h);
370
- overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
371
- }
372
-
373
- overlayContext.shadowBlur = 0;
374
- }
375
-
376
- debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
377
- }
378
-
379
- ///////////////////////////////////////////////////////////////////////////////
380
- // particle system editor (work in progress)
381
- let debugParticleEditor = 0, debugParticleSystem, debugParticleSystemDiv, particleSystemCode;
382
-
383
- const debugToggleParticleEditor = ()=>
384
- {
385
- debugParticleEditor = !debugParticleEditor;
386
372
 
387
- if (debugParticleEditor)
388
- {
389
- if (!debugParticleSystem || debugParticleSystem.destroyed)
390
- debugParticleSystem = new ParticleEmitter(cameraPos);
391
- }
392
- else if (debugParticleSystem && !debugParticleSystem.destroyed)
393
- debugParticleSystem.destroy();
394
-
395
-
396
- const colorToHex = (color)=>
397
- {
398
- const componentToHex = (c)=>
399
- {
400
- const hex = (c*255|0).toString(16);
401
- return hex.length == 1 ? '0' + hex : hex;
402
- }
403
-
404
- return '#' + componentToHex(color.r) + componentToHex(color.g) + componentToHex(color.b);
405
- }
406
- const hexToColor = (hex)=>
407
- {
408
- return new Color(
409
- parseInt(hex.substr(1,2), 16)/255,
410
- parseInt(hex.substr(3,2), 16)/255,
411
- parseInt(hex.substr(5,2), 16)/255)
412
- }
413
-
414
- if (!debugParticleSystemDiv)
415
- {
416
- const div = debugParticleSystemDiv = document.createElement('div');
417
- div.innerHTML = '<big><b>Particle Editor';
418
- div.style = 'position:absolute;top:10;left:10;color:#fff';
419
- document.body.appendChild(div);
420
-
421
- for ( const setting of debugParticleSettings)
422
- {
423
- const input = setting[2] = document.createElement('input');
424
- const name = setting[0];
425
- const type = setting[1];
426
- if (type)
373
+ let keysPressed = '';
374
+ for(const i in inputData[0])
427
375
  {
428
- if (type == 'color')
429
- {
430
- input.type = type;
431
- const color = debugParticleSystem[name];
432
- input.value = colorToHex(color);
433
- }
434
- else if (type == 'alpha' && name == 'colorStartAlpha')
435
- input.value = debugParticleSystem.colorStartA.a;
436
- else if (type == 'alpha' && name == 'colorEndAlpha')
437
- input.value = debugParticleSystem.colorEndA.a;
438
- else if (name == 'tileSizeX')
439
- input.value = debugParticleSystem.tileSize.x;
440
- else if (name == 'tileSizeY')
441
- input.value = debugParticleSystem.tileSize.y;
376
+ if (i && keyIsDown(i, 0))
377
+ keysPressed += i + ' ' ;
442
378
  }
443
- else
444
- input.value = debugParticleSystem[name] || '0';
379
+ keysPressed && overlayContext.fillText('Keys Down: ' + keysPressed, x, y += h);
445
380
 
446
- input.oninput = (e)=>
381
+ let buttonsPressed = '';
382
+ if (inputData[1])
383
+ for(const i in inputData[1])
447
384
  {
448
- const inputFloat = parseFloat(input.value) || 0;
449
- if (type)
450
- {
451
- if (type == 'color')
452
- {
453
- const color = hexToColor(input.value);
454
- debugParticleSystem[name].r = color.r;
455
- debugParticleSystem[name].g = color.g;
456
- debugParticleSystem[name].b = color.b;
457
- }
458
- else if (type == 'alpha' && name == 'colorStartAlpha')
459
- {
460
- debugParticleSystem.colorStartA.a = clamp(inputFloat);
461
- debugParticleSystem.colorStartB.a = clamp(inputFloat);
462
- }
463
- else if (type == 'alpha' && name == 'colorEndAlpha')
464
- {
465
- debugParticleSystem.colorEndA.a = clamp(inputFloat);
466
- debugParticleSystem.colorEndB.a = clamp(inputFloat);
467
- }
468
- else if (name == 'tileSizeX')
469
- {
470
- debugParticleSystem.tileSize = vec2(parseInt(input.value), debugParticleSystem.tileSize.y);
471
- }
472
- else if (name == 'tileSizeY')
473
- {
474
- debugParticleSystem.tileSize.y = vec2(debugParticleSystem.tileSize.x, parseInt(input.value));
475
- }
476
- }
477
- else
478
- debugParticleSystem[name] = inputFloat;
479
-
480
- updateCode();
385
+ if (i && keyIsDown(i, 1))
386
+ buttonsPressed += i + ' ' ;
481
387
  }
482
- div.appendChild(document.createElement('br'));
483
- div.appendChild(input);
484
- div.appendChild(document.createTextNode(' ' + name));
388
+ buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
485
389
  }
486
-
487
- div.appendChild(document.createElement('br'));
488
- div.appendChild(document.createElement('br'));
489
- div.appendChild(particleSystemCode = document.createElement('input'));
490
- particleSystemCode.disabled = true;
491
- div.appendChild(document.createTextNode(' code'));
492
-
493
- div.appendChild(document.createElement('br'));
494
- const button = document.createElement('button')
495
- div.appendChild(button);
496
- button.innerHTML = 'Copy To Clipboard';
497
-
498
- button.onclick = (e)=> navigator.clipboard.writeText(particleSystemCode.value);
499
-
500
- const updateCode = ()=>
390
+ else
501
391
  {
502
- let code = '';
503
- let count = 0;
504
- for ( const setting of debugParticleSettings)
505
- {
506
- const name = setting[0];
507
- const type = setting[1];
508
- let value;
509
- if (name == 'tileSizeX' || type == 'alpha')
510
- continue;
511
-
512
- if (count++)
513
- code += ', ';
514
-
515
- if (name == 'tileSizeY')
516
- {
517
- value = `vec2(${debugParticleSystem.tileSize.x},${debugParticleSystem.tileSize.y})`;
518
- }
519
- else if (type == 'color')
520
- {
521
- const c = debugParticleSystem[name];
522
- value = `new Color(${c.r},${c.g},${c.b},${c.a})`;
523
- }
524
- else
525
- value = debugParticleSystem[name];
526
- code += value;
527
- }
528
-
529
- particleSystemCode.value = '...[' + code + ']';
392
+ overlayContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
393
+ overlayContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
394
+ overlayContext.fillText(godMode ? 'God Mode' : '', x, y += h);
395
+ overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
530
396
  }
531
- updateCode();
397
+
398
+ overlayContext.restore();
532
399
  }
533
- debugParticleSystemDiv.style.display = debugParticleEditor ? '' : 'none'
534
- }
535
-
536
- const debugParticleSettings =
537
- [
538
- ['emitSize'],
539
- ['emitTime'],
540
- ['emitRate'],
541
- ['emitConeAngle'],
542
- ['tileIndex'],
543
- ['tileSizeX', 'tileSize'],
544
- ['tileSizeY', 'tileSize'],
545
- ['colorStartA', 'color'],
546
- ['colorStartB', 'color'],
547
- ['colorStartAlpha', 'alpha'],
548
- ['colorEndA', 'color'],
549
- ['colorEndB', 'color'],
550
- ['colorEndAlpha', 'alpha'],
551
- ['particleTime'],
552
- ['sizeStart'],
553
- ['sizeEnd'],
554
- ['speed'],
555
- ['angleSpeed'],
556
- ['damping'],
557
- ['angleDamping'],
558
- ['gravityScale'],
559
- ['particleConeAngle'],
560
- ['fadeRate'],
561
- ['randomness'],
562
- ['collideTiles'],
563
- ['additive'],
564
- ['randomColorComponents'],
565
- ['renderOrder'],
566
- ];
400
+ }