littlejsengine 1.16.2 → 1.17.1

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 (47) hide show
  1. package/dist/littlejs.d.ts +200 -166
  2. package/dist/littlejs.esm.js +1231 -1086
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +888 -735
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +884 -731
  7. package/examples/box2d/gameObjects.js +1 -1
  8. package/examples/breakout/game.js +5 -6
  9. package/examples/electron/index.html +2 -2
  10. package/examples/electron/tiles.png +0 -0
  11. package/examples/htmlMenu/tiles.png +0 -0
  12. package/examples/index.html +1 -1
  13. package/examples/logo.png +0 -0
  14. package/examples/module/tiles.png +0 -0
  15. package/examples/platformer/gameEffects.js +23 -22
  16. package/examples/platformer/gameLevel.js +24 -25
  17. package/examples/shorts/base.html +1 -1
  18. package/examples/shorts/clock.js +3 -3
  19. package/examples/shorts/fontImage.js +4 -3
  20. package/examples/shorts/parallax.js +1 -1
  21. package/examples/shorts/sequencer.js +1 -1
  22. package/examples/shorts/shapes.js +1 -1
  23. package/examples/shorts/texture.js +10 -6
  24. package/examples/shorts/tiles.png +0 -0
  25. package/examples/shorts/tiltedView.js +2 -0
  26. package/examples/starter/index.html +2 -2
  27. package/examples/starter/tiles.png +0 -0
  28. package/examples/typescript/tiles.png +0 -0
  29. package/examples/uiSystem/game.js +1 -1
  30. package/examples/uiSystem/tiles.png +0 -0
  31. package/package.json +1 -1
  32. package/plugins/postProcess.js +47 -28
  33. package/plugins/uiSystem.js +15 -15
  34. package/reference.md +1 -1
  35. package/src/engine.js +174 -174
  36. package/src/engineAudio.js +4 -6
  37. package/src/engineDebug.js +4 -4
  38. package/src/engineDraw.js +179 -122
  39. package/src/engineExport.js +326 -334
  40. package/src/engineFont.png +0 -0
  41. package/src/engineInput.js +14 -20
  42. package/src/engineMath.js +15 -104
  43. package/src/engineObject.js +26 -25
  44. package/src/engineParticles.js +2 -2
  45. package/src/engineTileLayer.js +196 -170
  46. package/src/engineUtilities.js +100 -4
  47. package/src/engineWebGL.js +123 -72
package/src/engineDraw.js CHANGED
@@ -77,10 +77,11 @@ let drawCount;
77
77
  * Create a tile info object using a grid based system
78
78
  * - This can take vecs or floats for easier use and conversion
79
79
  * - If an index is passed in, the tile size and index will determine the position
80
- * @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
80
+ * @param {Vector2|number} [index=0] - Index of the tile in 1d or 2d form
81
81
  * @param {Vector2|number} [size] - Size of tile in pixels
82
- * @param {number} [textureIndex] - Texture index to use
82
+ * @param {TextureInfo|number} [texture] - Texture index or info to use
83
83
  * @param {number} [padding] - How many pixels padding around tiles
84
+ * @param {number} [bleed] - How many pixels smaller to draw tiles
84
85
  * @return {TileInfo}
85
86
  * @example
86
87
  * tile(2) // a tile at index 2 using the default tile size of 16
@@ -88,36 +89,43 @@ let drawCount;
88
89
  * tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
89
90
  * tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
90
91
  * @memberof Draw */
91
- function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding)
92
+ function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
92
93
  {
93
- if (headlessMode)
94
- return new TileInfo;
94
+ ASSERT(isVector2(index) || typeof index === 'number', 'index must be a vec2 or number');
95
+ ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
96
+ ASSERT(isNumber(texture) || texture instanceof TextureInfo, 'texture must be a number or TextureInfo');
97
+ ASSERT(isNumber(padding), 'padding must be a number');
98
+
99
+ if (headlessMode) return new TileInfo;
95
100
 
96
- // if size is a number, make it a vector
97
101
  if (typeof size === 'number')
98
102
  {
103
+ // if size is a number, make it a vector
99
104
  ASSERT(size > 0);
100
105
  size = new Vector2(size, size);
101
106
  }
102
107
 
103
108
  // create tile info object
104
- const tileInfo = new TileInfo(new Vector2, size, textureIndex, padding);
109
+ const textureInfo = typeof texture === 'number' ?
110
+ textureInfos[texture] : texture;
105
111
 
106
112
  // get the position of the tile
107
- const textureInfo = textureInfos[textureIndex];
108
- ASSERT(!!textureInfo, 'Texture not loaded');
109
113
  const sizePaddedX = size.x + padding*2;
110
114
  const sizePaddedY = size.y + padding*2;
111
- if (typeof pos === 'number')
115
+ let x, y;
116
+ if (typeof index === 'number')
112
117
  {
113
118
  const cols = textureInfo.size.x / sizePaddedX |0;
114
- ASSERT(cols > 0, 'Tile size is too big for texture');
115
- const posX = pos % cols, posY = (pos / cols) |0;
116
- tileInfo.pos.set(posX*sizePaddedX+padding, posY*sizePaddedY+padding);
119
+ x = index % cols;
120
+ y = index / cols |0;
117
121
  }
118
122
  else
119
- tileInfo.pos.set(pos.x*sizePaddedX+padding, pos.y*sizePaddedY+padding);
120
- return tileInfo;
123
+ {
124
+ x = index.x;
125
+ y = index.y;
126
+ }
127
+ const pos = new Vector2(x*sizePaddedX + padding, y*sizePaddedY + padding);
128
+ return new TileInfo(pos, size, textureInfo, padding, bleed);
121
129
  }
122
130
 
123
131
  /**
@@ -127,24 +135,22 @@ function tile(pos=new Vector2, size=tileDefaultSize, textureIndex=0, padding=til
127
135
  class TileInfo
128
136
  {
129
137
  /** Create a tile info object
130
- * @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
138
+ * @param {Vector2} [pos=vec2()] - Top left corner of tile in pixels
131
139
  * @param {Vector2} [size] - Size of tile in pixels
132
- * @param {number} [textureIndex] - Texture index to use
133
- * @param {number} [padding] - How many pixels padding around tiles
134
- * @param {number} [bleed] - How many pixels smaller to draw tiles
140
+ * @param {TextureInfo} [textureInfo] - Texture info to use
141
+ * @param {number} [padding] - How many pixels padding around tiles
142
+ * @param {number} [bleed] - How many pixels smaller to draw tiles
135
143
  */
136
- constructor(pos=vec2(), size=tileDefaultSize, textureIndex=0, padding=tileDefaultPadding, bleed=tileDefaultBleed)
144
+ constructor(pos=vec2(), size=tileDefaultSize, textureInfo=textureInfos[0], padding=tileDefaultPadding, bleed=tileDefaultBleed)
137
145
  {
138
146
  /** @property {Vector2} - Top left corner of tile in pixels */
139
147
  this.pos = pos.copy();
140
148
  /** @property {Vector2} - Size of tile in pixels */
141
149
  this.size = size.copy();
142
- /** @property {number} - Texture index to use */
143
- this.textureIndex = textureIndex;
144
150
  /** @property {number} - How many pixels padding around tiles */
145
151
  this.padding = padding;
146
152
  /** @property {TextureInfo} - The texture info for this tile */
147
- this.textureInfo = textureInfos[this.textureIndex];
153
+ this.textureInfo = textureInfo;
148
154
  /** @property {number} - Shrinks tile by this many pixels to prevent neighbors bleeding */
149
155
  this.bleed = bleed;
150
156
  }
@@ -154,7 +160,7 @@ class TileInfo
154
160
  * @return {TileInfo}
155
161
  */
156
162
  offset(offset)
157
- { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex, this.padding, this.bleed); }
163
+ { return new TileInfo(this.pos.add(offset), this.size, this.textureInfo, this.padding, this.bleed); }
158
164
 
159
165
  /** Returns a copy of this tile offset by a number of animation frames
160
166
  * @param {number} frame - Offset to apply in animation frames
@@ -163,23 +169,33 @@ class TileInfo
163
169
  frame(frame)
164
170
  {
165
171
  ASSERT(typeof frame === 'number');
166
- return this.offset(new Vector2(frame*(this.size.x+this.padding*2), 0));
172
+ const w = this.size.x + this.padding*2;
173
+ const x = frame*w;
174
+ ASSERT(x < this.textureInfo.size.x, 'frame extends beyond texture width!');
175
+ return this.offset(new Vector2(x));
167
176
  }
168
177
 
169
178
  /**
170
179
  * Set this tile to use a full image in a texture info
171
- * @param {TextureInfo} textureInfo
180
+ * @param {TextureInfo} [textureInfo]
172
181
  * @return {TileInfo}
173
182
  */
174
- setFullImage(textureInfo)
183
+ setFullImage(textureInfo=this.textureInfo)
175
184
  {
185
+ this.textureInfo = textureInfo;
176
186
  this.pos = new Vector2;
177
187
  this.size = textureInfo.size.copy();
178
- this.textureInfo = textureInfo;
179
- // do not use padding or bleed
180
188
  this.bleed = this.padding = 0;
181
189
  return this;
182
190
  }
191
+
192
+ /**
193
+ * Returns a tile info for an index using this tile as refrence
194
+ * @param {Vector2|number} [index=0]
195
+ * @return {TileInfo}
196
+ */
197
+ tile(index)
198
+ { return tile(index, this.size, this.textureInfo, this.padding, this.bleed); }
183
199
  }
184
200
 
185
201
  /**
@@ -220,19 +236,19 @@ class TextureInfo
220
236
  ///////////////////////////////////////////////////////////////////////////////
221
237
  // Drawing functions
222
238
 
223
- /** Draw textured tile centered in world space, with color applied if using WebGL
224
- * @param {Vector2} pos - Center of the tile in world space
225
- * @param {Vector2} [size=(1,1)] - Size of the tile in world space
226
- * @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
227
- * @param {Color} [color=(1,1,1,1)] - Color to modulate with
228
- * @param {number} [angle] - Angle to rotate by
229
- * @param {boolean} [mirror] - Is image flipped along the Y axis?
230
- * @param {Color} [additiveColor] - Additive color to be applied if any
239
+ /** Draw textured tile centered in world space
240
+ * @param {Vector2} pos - Center of the tile in world space
241
+ * @param {Vector2} [size=vec2(1)] - Size of the tile in world space
242
+ * @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
243
+ * @param {Color} [color=WHITE] - Color to modulate with
244
+ * @param {number} [angle] - Angle to rotate by
245
+ * @param {boolean} [mirror] - Is image flipped along the Y axis?
246
+ * @param {Color} [additiveColor] - Additive color to be applied if any
231
247
  * @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering?
232
248
  * @param {boolean} [screenSpace=false] - Are the pos and size are in screen space?
233
249
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
234
250
  * @memberof Draw */
235
- function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
251
+ function drawTile(pos, size=vec2(1), tileInfo, color=WHITE,
236
252
  angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
237
253
  {
238
254
  ASSERT(isVector2(pos), 'pos must be a vec2');
@@ -307,8 +323,8 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
307
323
 
308
324
  /** Draw colored rect centered on pos
309
325
  * @param {Vector2} pos
310
- * @param {Vector2} [size=(1,1)]
311
- * @param {Color} [color=(1,1,1,1)]
326
+ * @param {Vector2} [size=vec2(1)]
327
+ * @param {Color} [color=WHITE]
312
328
  * @param {number} [angle]
313
329
  * @param {boolean} [useWebGL=glEnable]
314
330
  * @param {boolean} [screenSpace]
@@ -321,9 +337,9 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
321
337
 
322
338
  /** Draw a rect centered on pos with a gradient from top to bottom
323
339
  * @param {Vector2} pos
324
- * @param {Vector2} [size=(1,1)]
325
- * @param {Color} [colorTop=(1,1,1,1)]
326
- * @param {Color} [colorBottom=(0,0,0,1)]
340
+ * @param {Vector2} [size=vec2(1)]
341
+ * @param {Color} [colorTop=WHITE]
342
+ * @param {Color} [colorBottom=BLACK]
327
343
  * @param {number} [angle]
328
344
  * @param {boolean} [useWebGL=glEnable]
329
345
  * @param {boolean} [screenSpace]
@@ -385,9 +401,9 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
385
401
  /** Draw connected lines between a series of points
386
402
  * @param {Array<Vector2>} points
387
403
  * @param {number} [width]
388
- * @param {Color} [color=(1,1,1,1)]
404
+ * @param {Color} [color=WHITE]
389
405
  * @param {boolean} [wrap] - Should the last point connect to the first?
390
- * @param {Vector2} [pos=(0,0)] - Offset to apply
406
+ * @param {Vector2} [pos=vec2()] - Offset to apply
391
407
  * @param {number} [angle] - Angle to rotate by
392
408
  * @param {boolean} [useWebGL=glEnable]
393
409
  * @param {boolean} [screenSpace]
@@ -422,13 +438,9 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
422
438
  for (let i=0; i<points.length; ++i)
423
439
  {
424
440
  const point = points[i];
425
- if (i)
426
- context.lineTo(point.x, point.y);
427
- else
428
- context.moveTo(point.x, point.y);
441
+ context.lineTo(point.x, point.y);
429
442
  }
430
- if (wrap)
431
- context.closePath();
443
+ wrap && context.closePath();
432
444
  context.stroke();
433
445
  }, screenSpace, context);
434
446
  }
@@ -438,8 +450,8 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
438
450
  * @param {Vector2} posA
439
451
  * @param {Vector2} posB
440
452
  * @param {number} [width]
441
- * @param {Color} [color=(1,1,1,1)]
442
- * @param {Vector2} [pos=(0,0)] - Offset to apply
453
+ * @param {Color} [color=WHITE]
454
+ * @param {Vector2} [pos=vec2()] - Offset to apply
443
455
  * @param {number} [angle] - Angle to rotate by
444
456
  * @param {boolean} [useWebGL=glEnable]
445
457
  * @param {boolean} [screenSpace]
@@ -458,12 +470,12 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
458
470
 
459
471
  /** Draw colored regular polygon using passed in number of sides
460
472
  * @param {Vector2} pos
461
- * @param {Vector2} [size=(1,1)]
473
+ * @param {Vector2} [size=vec2(1)]
462
474
  * @param {number} [sides]
463
- * @param {Color} [color=(1,1,1,1)]
475
+ * @param {Color} [color=WHITE]
464
476
  * @param {number} [angle]
465
477
  * @param {number} [lineWidth]
466
- * @param {Color} [lineColor=(0,0,0,1)]
478
+ * @param {Color} [lineColor=BLACK]
467
479
  * @param {boolean} [useWebGL=glEnable]
468
480
  * @param {boolean} [screenSpace]
469
481
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
@@ -486,10 +498,10 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
486
498
 
487
499
  /** Draw colored polygon using passed in points
488
500
  * @param {Array<Vector2>} points - Array of Vector2 points
489
- * @param {Color} [color=(1,1,1,1)]
501
+ * @param {Color} [color=WHITE]
490
502
  * @param {number} [lineWidth]
491
- * @param {Color} [lineColor=(0,0,0,1)]
492
- * @param {Vector2} [pos=(0,0)] - Offset to apply
503
+ * @param {Color} [lineColor=BLACK]
504
+ * @param {Vector2} [pos=vec2()] - Offset to apply
493
505
  * @param {number} [angle] - Angle to rotate by
494
506
  * @param {boolean} [useWebGL=glEnable]
495
507
  * @param {boolean} [screenSpace]
@@ -536,11 +548,11 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
536
548
 
537
549
  /** Draw colored ellipse using passed in point
538
550
  * @param {Vector2} pos
539
- * @param {Vector2} [size=(1,1)] - Width and height diameter
540
- * @param {Color} [color=(1,1,1,1)]
551
+ * @param {Vector2} [size=vec2(1)] - Width and height diameter
552
+ * @param {Color} [color=WHITE]
541
553
  * @param {number} [angle]
542
554
  * @param {number} [lineWidth]
543
- * @param {Color} [lineColor=(0,0,0,1)]
555
+ * @param {Color} [lineColor=BLACK]
544
556
  * @param {boolean} [useWebGL=glEnable]
545
557
  * @param {boolean} [screenSpace]
546
558
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
@@ -582,9 +594,9 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
582
594
  /** Draw colored circle using passed in point
583
595
  * @param {Vector2} pos
584
596
  * @param {number} [size=1] - Diameter
585
- * @param {Color} [color=(1,1,1,1)]
597
+ * @param {Color} [color=WHITE]
586
598
  * @param {number} [lineWidth=0]
587
- * @param {Color} [lineColor=(0,0,0,1)]
599
+ * @param {Color} [lineColor=BLACK]
588
600
  * @param {boolean} [useWebGL=glEnable]
589
601
  * @param {boolean} [screenSpace]
590
602
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
@@ -639,9 +651,9 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
639
651
  * @param {string|number} text
640
652
  * @param {Vector2} pos
641
653
  * @param {number} [size]
642
- * @param {Color} [color=(1,1,1,1)]
654
+ * @param {Color} [color=WHITE]
643
655
  * @param {number} [lineWidth]
644
- * @param {Color} [lineColor=(0,0,0,1)]
656
+ * @param {Color} [lineColor=BLACK]
645
657
  * @param {CanvasTextAlign} [textAlign='center']
646
658
  * @param {string} [font=fontDefault]
647
659
  * @param {string} [fontStyle]
@@ -665,10 +677,10 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
665
677
  * Automatically splits new lines into rows
666
678
  * @param {string|number} text
667
679
  * @param {Vector2} pos
668
- * @param {number} [size]
669
- * @param {Color} [color=(1,1,1,1)]
680
+ * @param {number} size
681
+ * @param {Color} [color=WHITE]
670
682
  * @param {number} [lineWidth]
671
- * @param {Color} [lineColor=(0,0,0,1)]
683
+ * @param {Color} [lineColor=BLACK]
672
684
  * @param {CanvasTextAlign} [textAlign]
673
685
  * @param {string} [font=fontDefault]
674
686
  * @param {string} [fontStyle]
@@ -676,7 +688,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
676
688
  * @param {number} [angle]
677
689
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
678
690
  * @memberof Draw */
679
- function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=drawContext)
691
+ function drawTextScreen(text, pos, size, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=drawContext)
680
692
  {
681
693
  ASSERT(isString(text), 'text must be a string');
682
694
  ASSERT(isVector2(pos), 'pos must be a vec2');
@@ -800,7 +812,7 @@ function worldToScreenDelta(worldDelta)
800
812
 
801
813
  /** Convert screen space transform to world space
802
814
  * @param {Vector2} screenPos
803
- * @param {Vector2} screenSize
815
+ * @param {Vector2} screenSize
804
816
  * @param {number} [screenAngle]
805
817
  * @return {[Vector2, Vector2, number]} - [pos, size, angle]
806
818
  * @memberof Draw */
@@ -861,10 +873,9 @@ function isOnScreen(pos, size=0)
861
873
  * @param {boolean} [additive]
862
874
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
863
875
  * @memberof Draw */
864
- function setBlendMode(additive=false, context)
876
+ function setBlendMode(additive=false, context=drawContext)
865
877
  {
866
878
  glAdditive = additive;
867
- context ||= drawContext;
868
879
  context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
869
880
  }
870
881
 
@@ -879,7 +890,6 @@ function combineCanvases()
879
890
  workContext.fillRect(0,0,w,h); // remove background alpha
880
891
  glCopyToContext(workContext);
881
892
  workContext.drawImage(mainCanvas, 0, 0);
882
- mainCanvas.width |= 0;
883
893
  mainContext.drawImage(workCanvas, 0, 0);
884
894
  }
885
895
 
@@ -984,17 +994,22 @@ function setCursor(cursorStyle = 'auto')
984
994
 
985
995
  ///////////////////////////////////////////////////////////////////////////////
986
996
 
997
+ /** Engine font image, 8x8 font provided by the engine
998
+ * @type {FontImage}
999
+ * @memberof Draw */
987
1000
  let engineFontImage;
988
1001
 
989
1002
  /**
990
- * Font Image Object - Draw text on a 2D canvas by using characters in an image
1003
+ * Font Image Object - Draw text by using tiles in an image
991
1004
  * - 96 characters (from space to tilde) are stored in an image
992
- * - Uses a default 8x8 font if none is supplied
993
- * - You can also use fonts from the main tile sheet
1005
+ * - A 8x8 default engine font is supplied for general use
1006
+ * - This system is WebGL enabled for fast text rendering
1007
+ * - Fonts can also be colored and scaled along each axis
1008
+ *
994
1009
  * @memberof Draw
995
1010
  * @example
996
1011
  * // use built in font
997
- * const font = new FontImage;
1012
+ * const font = engineFontImage;
998
1013
  *
999
1014
  * // draw text
1000
1015
  * font.drawTextScreen('LittleJS\nHello World!', vec2(200, 50));
@@ -1002,68 +1017,110 @@ let engineFontImage;
1002
1017
  class FontImage
1003
1018
  {
1004
1019
  /** Create an image font
1005
- * @param {HTMLImageElement} [image] - Image for the font, default if undefined
1006
- * @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
1007
- * @param {Vector2} [paddingSize=(0,1)] - How much space between characters
1020
+ * @param {TileInfo} tileInfo - Tile info of first characeter in font
1008
1021
  */
1009
- constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
1022
+ constructor(tileInfo)
1010
1023
  {
1011
- // load default font image
1012
- if (!image && !engineFontImage)
1013
- {
1014
- engineFontImage = new Image;
1015
- engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
1016
- }
1017
-
1018
- this.image = image || engineFontImage;
1019
- this.tileSize = tileSize;
1020
- this.paddingSize = paddingSize;
1024
+ ASSERT(!!tileInfo, 'tileInfo is required for FontImage');
1025
+
1026
+ /** @property {TileInfo} - Tile info for the font */
1027
+ this.tileInfo = tileInfo.frame(0);
1021
1028
  }
1022
1029
 
1023
1030
  /** Draw text in world space using the image font
1024
- * @param {string|number} text
1031
+ * @param {string|number} text
1025
1032
  * @param {Vector2} pos
1026
- * @param {number} [scale=.25]
1027
- * @param {boolean} [center]
1028
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
1033
+ * @param {Vector2|number} [size]
1034
+ * @param {boolean} [center=true]
1035
+ * @param {Color} [color=WHITE]
1036
+ * @param {boolean} [useWebGL=glEnable]
1037
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
1029
1038
  */
1030
- drawText(text, pos, scale=1, center, context=drawContext)
1039
+ drawText(text, pos, size=1, center, color, useWebGL, context)
1031
1040
  {
1032
- this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center, context);
1041
+ ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
1042
+
1043
+ if (typeof size === 'number')
1044
+ {
1045
+ // if size is a number, make it a vector
1046
+ ASSERT(size > 0);
1047
+ size *= cameraScale;
1048
+ size = new Vector2(size, size);
1049
+ }
1050
+ else
1051
+ size = size.scale(cameraScale);
1052
+ this.drawTextScreen(text, worldToScreen(pos), size, center, color, useWebGL, context);
1033
1053
  }
1034
1054
 
1035
1055
  /** Draw text in screen space using the image font
1036
- * @param {string|number} text
1056
+ * @param {string|number} text
1037
1057
  * @param {Vector2} pos
1038
- * @param {number} [scale]
1058
+ * @param {Vector2|number} size
1039
1059
  * @param {boolean} [center]
1040
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
1060
+ * @param {Color} [color=WHITE]
1061
+ * @param {boolean} [useWebGL=glEnable]
1062
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
1041
1063
  */
1042
- drawTextScreen(text, pos, scale=4, center=true, context=drawContext)
1064
+ drawTextScreen(text, pos, size, center=true, color=WHITE, useWebGL=glEnable, context)
1043
1065
  {
1044
- context.save();
1045
- const size = this.tileSize;
1046
- const drawSize = size.add(this.paddingSize).scale(scale);
1047
- const cols = this.image.width / this.tileSize.x |0;
1048
- (text+'').split('\n').forEach((line, i)=>
1066
+ ASSERT(isString(text), 'text must be a string');
1067
+ ASSERT(isVector2(pos), 'pos must be a vec2');
1068
+ ASSERT(isVector2(size) || typeof size === 'number', 'size must be a vec2 or number');
1069
+ ASSERT(isColor(color), 'color must be a color');
1070
+
1071
+ // if size is a number, make it a vector
1072
+ size = typeof size === 'number' ? new Vector2(size, size) : size;
1073
+
1074
+ // precache objects for drawing
1075
+ const drawPos = new Vector2;
1076
+ const tileInfo = this.tileInfo;
1077
+ const padding = tileInfo.padding;
1078
+ const sizePaddedX = tileInfo.size.x + padding*2;
1079
+ const sizePaddedY = tileInfo.size.y + padding*2;
1080
+ const cols = tileInfo.textureInfo.size.x / sizePaddedX |0;
1081
+
1082
+ // draw each line of text
1083
+ (text+'').split('\n').forEach((line, j)=>
1049
1084
  {
1050
- const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
1051
- for (let j=line.length; j--;)
1085
+ const centerOffset = center ? (line.length-1) * size.x / 2 : 0;
1086
+ for (let i=line.length; i--;)
1052
1087
  {
1053
- // draw each character
1054
- let charCode = line[j].charCodeAt(0);
1055
- if (charCode < 32 || charCode > 127)
1056
- charCode = 127; // unknown character
1057
-
1058
- // get the character source location and draw it
1059
- const tile = charCode - 32;
1060
- const x = tile % cols;
1061
- const y = tile / cols |0;
1062
- const drawPos = pos.add(vec2(j,i).multiply(drawSize));
1063
- context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
1064
- drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
1088
+ // get the character index
1089
+ const charCode = line.charCodeAt(i);
1090
+ const index = charCode < 32 || charCode > 127 ?
1091
+ 95 : charCode - 32; // handle out of range characters
1092
+
1093
+ // get the position of the tile
1094
+ const x = index % cols;
1095
+ const y = index / cols |0;
1096
+ tileInfo.pos.x = x*sizePaddedX + padding;
1097
+ tileInfo.pos.y = y*sizePaddedY + padding;
1098
+
1099
+ // draw the tile
1100
+ drawPos.x = pos.x + i * size.x - centerOffset |0;
1101
+ drawPos.y = pos.y + j * size.y |0;
1102
+ drawTile(drawPos, size, tileInfo, color, 0, false, undefined, useWebGL, true, context);
1065
1103
  }
1066
1104
  });
1067
- context.restore();
1068
1105
  }
1106
+ }
1107
+
1108
+ // load engine font, called automatically on startup
1109
+ function fontImageInit()
1110
+ {
1111
+ return new Promise(resolve =>
1112
+ {
1113
+ // create the engine font
1114
+ const image = new Image;
1115
+ image.onerror = image.onload = ()=>
1116
+ {
1117
+ const tilePos=vec2(), tileSize=vec2(8), padding=1, bleed=0;
1118
+ const textureInfo = new TextureInfo(image);
1119
+ const tileInfo = new TileInfo(tilePos, tileSize, textureInfo, padding, bleed);
1120
+ engineFontImage = new FontImage(tileInfo);
1121
+ resolve();
1122
+ }
1123
+ image.crossOrigin = 'anonymous';
1124
+ image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAAeAQMAAABnrVXaAAAABlBMVEUAAAD///+l2Z/dAAAAAXRSTlMAQObYZgAAAjpJREFUOMu9kzFu2zAUhn+CAROgqrk+B2l0BWYxMjlXeYaAtFtbdA1sGgHqRQfI0CNkSG5AwYB0BQ8d5Bsomwah6CPVeGg6tEPzAxLwyI+P78cP4u9lNO9OoMKnLMOobG5020/yaj/MrRcCGh1gBbyiLTPJEYaIiom5KM9Jq7KgynMGtb6L4GL4MF2H4LQKCXTvDVw2I4MsgZT7QLExdiutH+D08VOP3INXRrWX1/mmpbkNgAPYRVANb4xpcegYvhiNbIXauQICEjBuYLfMakaakWQeXxiZ0VDtuJCKs3ztMV59QtsHJNcRxDzfdL21ty3PrfIcXTN+E+GFAv6T5nbT9jd50/WFxb5ksdAv49qS6ouymG66ji08UMT6moykYLAo+V0j23GN4m829ZySAD5K7QsBfQTvOG8eE+gTeGYRAmnNAubN3hf5Zv9tJWDHp/VTuaSm7SN4fyINQqaNO3RMVxvpSPXnOChnRNvFcGY0gnwiPswYwTKVPE0zVtX3mTEIOoFzaqLrGuJaV+Uqumb71fVk/VoOH3cdLNQP/FHi8hV0CQNoqBZsUPlLPMsdCJro9QAaQQ0woDy9BJm0eTxCFnO9srcYlhNVlfR2EyTrph1uUtbUtAJifwRgrKuYdXVHeb0YI3QpawohQHkloI3J5FuVwI5ORxC9k2Tuz9Ir1IjgeIPGMHYkAZe2RuYkmWFmt3gGbTPOmBUWVTmRmHtGrfpzG/yuQNOKa6gBB/WA9khitPgl6/GP+gl2Af6tCbvaygAAAABJRU5ErkJggg==';
1125
+ });
1069
1126
  }