littlejsengine 1.1.17 → 1.2.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.
@@ -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
  ///////////////////////////////////////////////////////////////////////////////
@@ -156,22 +163,25 @@ const debugUpdate = ()=>
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;
246
+
247
+ const distance = mousePos.distanceSquared(o.pos);
248
+ if (distance < bestDistance)
249
+ {
250
+ bestDistance = distance;
251
+ bestObject = o
252
+ }
235
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
372
 
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
-
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
+ }
@@ -92,17 +92,13 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
92
92
  {
93
93
  // calculate uvs and render
94
94
  const cols = tileImageSize.x / tileSize.x |0;
95
- const uvSizeX = tileSize.x * tileImageSizeInverse.x;
96
- const uvSizeY = tileSize.y * tileImageSizeInverse.y;
95
+ const uvSizeX = tileSize.x / tileImageSize.x;
96
+ const uvSizeY = tileSize.y / tileImageSize.y;
97
97
  const uvX = (tileIndex%cols)*uvSizeX, uvY = (tileIndex/cols|0)*uvSizeY;
98
-
99
- // shrink uvs to prevent bleeding
100
- const shrinkTilesX = tileBleedShrinkFix * tileImageSizeInverse.x;
101
- const shrinkTilesY = tileBleedShrinkFix * tileImageSizeInverse.y;
102
98
 
103
99
  glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
104
- uvX + shrinkTilesX, uvY + shrinkTilesY,
105
- uvX - shrinkTilesX + uvSizeX, uvY - shrinkTilesX + uvSizeY,
100
+ uvX + tileImageFixBleed.x, uvY + tileImageFixBleed.y,
101
+ uvX - tileImageFixBleed.x + uvSizeX, uvY - tileImageFixBleed.y + uvSizeY,
106
102
  color.rgbaInt(), additiveColor.rgbaInt());
107
103
  }
108
104
  }
@@ -121,10 +117,10 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
121
117
  {
122
118
  // calculate uvs and render
123
119
  const cols = tileImageSize.x / tileSize.x |0;
124
- const sX = (tileIndex%cols)*tileSize.x + tileBleedShrinkFix;
125
- const sY = (tileIndex/cols|0)*tileSize.y + tileBleedShrinkFix;
126
- const sWidth = tileSize.x - 2*tileBleedShrinkFix;
127
- const sHeight = tileSize.y - 2*tileBleedShrinkFix;
120
+ const sX = (tileIndex%cols)*tileSize.x + tileFixBleedScale;
121
+ const sY = (tileIndex/cols|0)*tileSize.y + tileFixBleedScale;
122
+ const sWidth = tileSize.x - 2*tileFixBleedScale;
123
+ const sHeight = tileSize.y - 2*tileFixBleedScale;
128
124
  context.globalAlpha = color.a; // only alpha is supported
129
125
  context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
130
126
  }
@@ -207,6 +203,18 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
207
203
  context.restore();
208
204
  }
209
205
 
206
+ /** Enable normal or additive blend mode
207
+ * @param {Boolean} [additive=0]
208
+ * @param {Boolean} [useWebGL=glEnable]
209
+ * @memberof Draw */
210
+ function setBlendMode(additive, useWebGL=glEnable)
211
+ {
212
+ if (glEnable && useWebGL)
213
+ glSetBlendMode(additive);
214
+ else
215
+ mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
216
+ }
217
+
210
218
  /** Draw text on overlay canvas in world space
211
219
  * @param {String} text
212
220
  * @param {Vector2} pos
@@ -232,16 +240,93 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
232
240
  overlayContext.fillText(text, pos.x, pos.y);
233
241
  }
234
242
 
235
- /** Enable normal or additive blend mode
236
- * @param {Boolean} [additive=0]
237
- * @param {Boolean} [useWebGL=glEnable]
238
- * @memberof Draw */
239
- function setBlendMode(additive, useWebGL=glEnable)
243
+ ///////////////////////////////////////////////////////////////////////////////
244
+
245
+ /**
246
+ * Font Image Object - Draw text on a 2D canvas by using characters in an image
247
+ * <br> - 96 characters (from space to tilde) are stored in an image
248
+ * <br> - Uses a default 8x8 font if none is supplied
249
+ * <br> - You can also use fonts from the main tile sheet
250
+ * @example
251
+ * // use built in font
252
+ * const font = new ImageFont;
253
+ *
254
+ * // draw text
255
+ * font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
256
+ */
257
+ class FontImage
240
258
  {
241
- if (glEnable && useWebGL)
242
- glSetBlendMode(additive);
243
- else
244
- mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
259
+ /** Create an image font
260
+ * @param {Image} [image] - The image the font is stored in, if undefined the default font is used
261
+ * @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
262
+ * @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
263
+ * @param {Number} [startTileIndex=0] - Tile index in image where font starts
264
+ * @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
265
+ */
266
+ constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
267
+ {
268
+ if (!image)
269
+ {
270
+ // use default font image
271
+ image = new Image();
272
+ image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6MCIBdFY+Q0PMNgf0yCgsSAaZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWw1hPWNRcEyWh2npdpBmSUBrRaN1WSlQAWXaWLF0kkwR7C/N7XUvIaQr4xpeqeQigjLwBRZ/6KkufAACcv5z7GYhvgWHII8xN/4C2hvLCkmZSVgIJMHNTxpYOsJOiitEjUQnIQAFFP+opoQfCbjWmfzWiNzCjEvxAPFARmw/Y5Te0sl2zEp4M+m7cToJjdJS7y1xcFlFT7EtWBZ8nl7uLSff7UgYewtlyY6yHM4RrjNrnsfn/W0DBK9xNZctZ7gsTeXTe5eqLqXFlKnAn+vkNXzH6TqPW10ksKoSlauKk1TcncZLSTxNnQ8Av/8RGPXOFIuMCZ/fpwIZx5vU4QIRQ44HwUyHx/QrZhm5VtXFex7ja2se0KBpnPT0DSwS4xd86uAy+SqnLBgkPY5JolhJ33ENyK6n0VyRzCnzebIkFdY/NUtM66lTdhix1KUss6q5snH5ftVJXG0QITb1gUDvzD3z/BbyOLMirwCtPzAAAAAElFTkSuQmCC';
273
+ }
274
+
275
+ this.image = image;
276
+ this.tileSize = tileSize;
277
+ this.paddingSize = paddingSize;
278
+ this.startTileIndex = startTileIndex;
279
+ this.context = context;
280
+ }
281
+
282
+ /** Draw text in screen space using the image font
283
+ * @param {String} text
284
+ * @param {Vector2} pos
285
+ * @param {Number} [scale=4]
286
+ * @param {Boolean} [center]
287
+ */
288
+ drawTextScreen(text, pos, scale=4, center)
289
+ {
290
+ const context = this.context;
291
+ context.save();
292
+ context.imageSmoothingEnabled = !cavasPixelated;
293
+
294
+ const size = this.tileSize;
295
+ const drawSize = size.add(this.paddingSize).scale(scale);
296
+ const cols = this.image.width / this.tileSize.x |0;
297
+ text.split('\n').forEach((line, i)=>
298
+ {
299
+ const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
300
+ for(let j=line.length; j--;)
301
+ {
302
+ // draw each character
303
+ let charCode = line[j].charCodeAt();
304
+ if (charCode < 32 || charCode > 127)
305
+ charCode = 127; // unknown character
306
+
307
+ // get the character source location and draw it
308
+ const tile = this.startTileIndex + charCode - 32;
309
+ const x = tile % cols;
310
+ const y = tile / cols |0;
311
+ const drawPos = pos.add(vec2(j,i).multiply(drawSize));
312
+ context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
313
+ drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
314
+ }
315
+ });
316
+
317
+ context.restore();
318
+ }
319
+
320
+ /** Draw text in world space using the image font
321
+ * @param {String} text
322
+ * @param {Vector2} pos
323
+ * @param {Number} [scale=.25]
324
+ * @param {Boolean} [center]
325
+ */
326
+ drawText(text, pos, scale=1, center)
327
+ {
328
+ this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
329
+ }
245
330
  }
246
331
 
247
332
  ///////////////////////////////////////////////////////////////////////////////