littlejsengine 1.7.21 → 1.8.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 (52) hide show
  1. package/README.md +28 -55
  2. package/build/littlejs.d.ts +595 -552
  3. package/build/littlejs.esm.js +626 -535
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +542 -264
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +542 -264
  8. package/examples/breakout/game.js +17 -14
  9. package/examples/breakout/gameObjects.js +29 -8
  10. package/examples/breakout/index.html +3 -3
  11. package/examples/breakoutTutorial/README.md +3 -3
  12. package/examples/breakoutTutorial/game.js +4 -4
  13. package/examples/breakoutTutorial/index.html +2 -2
  14. package/examples/electron/game.js +3 -2
  15. package/examples/electron/index.html +2 -2
  16. package/examples/empty/game.js +1 -1
  17. package/examples/favicon.png +0 -0
  18. package/examples/js13k/game.js +20 -15
  19. package/examples/js13k/index.html +14 -14
  20. package/examples/module/game.js +5 -4
  21. package/examples/module/index.html +1 -1
  22. package/examples/particles/index.html +19 -22
  23. package/examples/platformer/game.js +3 -3
  24. package/examples/platformer/gameEffects.js +21 -19
  25. package/examples/platformer/gameLevel.js +6 -6
  26. package/examples/platformer/gameObjects.js +18 -18
  27. package/examples/platformer/gamePlayer.js +4 -3
  28. package/examples/platformer/index.html +6 -6
  29. package/examples/platformer/tiles.png +0 -0
  30. package/examples/platformer/tilesLevel.png +0 -0
  31. package/examples/puzzle/game.js +10 -8
  32. package/examples/puzzle/index.html +2 -2
  33. package/examples/screenshot.jpg +0 -0
  34. package/examples/starter/game.js +32 -21
  35. package/examples/starter/index.html +13 -13
  36. package/examples/starter/tiles.png +0 -0
  37. package/examples/stress/index.html +4 -4
  38. package/examples/typescript/game.js +4 -3
  39. package/examples/typescript/game.ts +5 -4
  40. package/examples/typescript/index.html +1 -1
  41. package/package.json +2 -1
  42. package/src/engine.js +59 -52
  43. package/src/engineAudio.js +2 -1
  44. package/src/engineDraw.js +171 -55
  45. package/src/engineExport.js +84 -271
  46. package/src/engineMedals.js +13 -45
  47. package/src/engineObject.js +13 -13
  48. package/src/engineParticles.js +17 -17
  49. package/src/engineSettings.js +195 -2
  50. package/src/engineTileLayer.js +23 -22
  51. package/src/engineUtilities.js +6 -6
  52. package/src/engineWebGL.js +43 -50
package/src/engineDraw.js CHANGED
@@ -47,13 +47,109 @@ let overlayContext;
47
47
  * @memberof Draw */
48
48
  let mainCanvasSize = vec2();
49
49
 
50
- /** Tile sheet for batch rendering system
51
- * @type {CanvasImageSource}
50
+ /** Array containing texture info for batch rendering system
51
+ * @type {Array}
52
52
  * @memberof Draw */
53
- const tileImage = new Image;
53
+ let textureInfos = [];
54
54
 
55
55
  // Engine internal variables not exposed to documentation
56
- let tileImageSize, tileImageFixBleed, drawCount;
56
+ let drawCount;
57
+
58
+ ///////////////////////////////////////////////////////////////////////////////
59
+
60
+ /**
61
+ * Create a tile info object
62
+ * - This can take vecs or floats for easier use and conversion
63
+ * - If an index is passed in, the tile size and index will determine the position
64
+ * @param {(Number|Vector2)} [pos=Vector2()] - Top left corner of tile in pixels or index
65
+ * @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
66
+ * @param {Number} [textureIndex=0] - Texture index to use
67
+ * @return {TileInfo}
68
+ * @example
69
+ * tile(2) // a tile at index 2 using the default tile size of 16
70
+ * tile(5, 8) // a tile at index 5 using a tile size of 8
71
+ * tile(1, 16, 3) // a tile at index 1 of size 16 on texture 3
72
+ * tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
73
+ * @memberof Draw
74
+ */
75
+ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
76
+ {
77
+ // if size is a number, make it a vector
78
+ if (size.x == undefined)
79
+ {
80
+ ASSERT(size > 0);
81
+ size = vec2(size);
82
+ }
83
+
84
+ // if pos is a number, use it as a tile index
85
+ if (pos.x == undefined)
86
+ {
87
+ const textureInfo = textureInfos[textureIndex];
88
+ if (textureInfo)
89
+ {
90
+ const cols = textureInfo.size.x / size.x |0;
91
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
92
+ }
93
+ else
94
+ pos = vec2();
95
+ }
96
+
97
+ // return a tile info object
98
+ return new TileInfo(pos, size, textureIndex);
99
+ }
100
+
101
+ /**
102
+ * Tile Info - Stores info about how to draw a tile
103
+ */
104
+ class TileInfo
105
+ {
106
+ /** Create a tile info object
107
+ * @param {Vector2} [pos=Vector2()] - Top left corner of tile in pixels
108
+ * @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
109
+ * @param {Number} [textureIndex=0] - Texture index to use
110
+ */
111
+ constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
112
+ {
113
+ /** @property {Vector2} - Top left corner of tile in pixels */
114
+ this.pos = pos;
115
+ /** @property {Vector2} - Size of tile in pixels */
116
+ this.size = size;
117
+ /** @property {Number} - Texture index to use */
118
+ this.textureIndex = textureIndex;
119
+ }
120
+
121
+ /** Returns an offset copy of this tile, useful for animation
122
+ * @param {Vector2} offset - Offset to apply in pixels
123
+ * @return {TileInfo}
124
+ */
125
+ offset(offset)
126
+ { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
127
+
128
+ /** Returns the texture info for this tile
129
+ * @return {TextureInfo}
130
+ */
131
+ getTextureInfo()
132
+ { return textureInfos[this.textureIndex]; }
133
+ }
134
+
135
+ /** Texture Info - Stores info about each texture */
136
+ class TextureInfo
137
+ {
138
+ // create a TextureInfo, called automatically by the engine
139
+ constructor(image)
140
+ {
141
+ /** @property {CanvasImageSource} - image source */
142
+ this.image = image;
143
+ /** @property {Vector2} - size of the image */
144
+ this.size = vec2(image.width, image.height);
145
+ /** @property {WebGLTexture} - webgl texture */
146
+ this.glTexture = glEnable && glCreateTexture(image);
147
+ /** @property {Vector2} - size to adjust tile to fix bleeding */
148
+ this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
149
+ }
150
+ }
151
+
152
+ ///////////////////////////////////////////////////////////////////////////////
57
153
 
58
154
  /** Convert from screen to world space coordinates
59
155
  * @param {Vector2} screenPos
@@ -84,7 +180,7 @@ function worldToScreen(worldPos)
84
180
  /** Draw textured tile centered in world space, with color applied if using WebGL
85
181
  * @param {Vector2} pos - Center of the tile in world space
86
182
  * @param {Vector2} [size=Vector2(1,1)] - Size of the tile in world space
87
- * @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
183
+ * @param {TileInfo}[tileInfo] - Tile info to use, untextured if undefined
88
184
  * @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
89
185
  * @param {Color} [color=Color()] - Color to modulate with
90
186
  * @param {Number} [angle=0] - Angle to rotate by
@@ -92,13 +188,18 @@ function worldToScreen(worldPos)
92
188
  * @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
93
189
  * @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
94
190
  * @param {Boolean} [screenSpace=0] - If true the pos and size are in screen space
191
+ * @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
95
192
  * @memberof Draw */
96
- function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
97
- angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace)
193
+ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
194
+ angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace, context)
98
195
  {
196
+ ASSERT(!context || !useWebGL); // context only supported in canvas 2D mode
197
+ ASSERT(typeof tileInfo !== 'number' || !tileInfo); // prevent old style calls
198
+ // to fix old calls, replace with tile(tileIndex, tileSize)
199
+
99
200
  showWatermark && ++drawCount;
100
-
101
- if (glEnable && useWebGL)
201
+ const textureInfo = tileInfo && tileInfo.getTextureInfo();
202
+ if (useWebGL)
102
203
  {
103
204
  if (screenSpace)
104
205
  {
@@ -106,48 +207,50 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
106
207
  pos = screenToWorld(pos);
107
208
  size = size.scale(1/cameraScale);
108
209
  }
109
- if (tileIndex < 0 || !tileImage.width)
110
- {
111
- // if negative tile index or image not found, force untextured
112
- glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
113
- }
114
- else
210
+
211
+ if (textureInfo)
115
212
  {
116
213
  // calculate uvs and render
117
- const cols = tileImageSize.x / tileSize.x |0;
118
- const uvSizeX = tileSize.x / tileImageSize.x;
119
- const uvSizeY = tileSize.y / tileImageSize.y;
120
- const uvX = (tileIndex%cols)*uvSizeX, uvY = (tileIndex/cols|0)*uvSizeY;
121
-
214
+ const x = tileInfo.pos.x / textureInfo.size.x;
215
+ const y = tileInfo.pos.y / textureInfo.size.y;
216
+ const w = tileInfo.size.x / textureInfo.size.x;
217
+ const h = tileInfo.size.y / textureInfo.size.y;
218
+ const tileImageFixBleed = textureInfo.fixBleedSize;
219
+ glSetTexture(textureInfo.glTexture);
122
220
  glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
123
- uvX + tileImageFixBleed.x, uvY + tileImageFixBleed.y,
124
- uvX - tileImageFixBleed.x + uvSizeX, uvY - tileImageFixBleed.y + uvSizeY,
221
+ x + tileImageFixBleed.x, y + tileImageFixBleed.y,
222
+ x - tileImageFixBleed.x + w, y - tileImageFixBleed.y + h,
125
223
  color.rgbaInt(), additiveColor.rgbaInt());
126
224
  }
225
+ else
226
+ {
227
+ // if no tile info, force untextured
228
+ glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
229
+ }
127
230
  }
128
231
  else
129
232
  {
130
233
  // normal canvas 2D rendering method (slower)
131
234
  drawCanvas2D(pos, size, angle, mirror, (context)=>
132
235
  {
133
- if (tileIndex < 0)
236
+ if (textureInfo)
134
237
  {
135
- // if negative tile index, force untextured
136
- context.fillStyle = color;
137
- context.fillRect(-.5, -.5, 1, 1);
238
+ // calculate uvs and render
239
+ const x = tileInfo.pos.x + tileFixBleedScale;
240
+ const y = tileInfo.pos.y + tileFixBleedScale;
241
+ const w = tileInfo.size.x - 2*tileFixBleedScale;
242
+ const h = tileInfo.size.y - 2*tileFixBleedScale;
243
+ context.globalAlpha = color.a; // only alpha is supported
244
+ context.drawImage(textureInfo.image, x, y, w, h, -.5, -.5, 1, 1);
245
+ context.globalAlpha = 1; // set back to full alpha
138
246
  }
139
247
  else
140
248
  {
141
- // calculate uvs and render
142
- const cols = tileImageSize.x / tileSize.x |0;
143
- const sX = (tileIndex%cols)*tileSize.x + tileFixBleedScale;
144
- const sY = (tileIndex/cols|0)*tileSize.y + tileFixBleedScale;
145
- const sWidth = tileSize.x - 2*tileFixBleedScale;
146
- const sHeight = tileSize.y - 2*tileFixBleedScale;
147
- context.globalAlpha = color.a; // only alpha is supported
148
- context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
249
+ // if no tile info, force untextured
250
+ context.fillStyle = color;
251
+ context.fillRect(-.5, -.5, 1, 1);
149
252
  }
150
- }, undefined, screenSpace);
253
+ }, screenSpace, context);
151
254
  }
152
255
  }
153
256
 
@@ -158,28 +261,36 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
158
261
  * @param {Number} [angle=0]
159
262
  * @param {Boolean} [useWebGL=glEnable]
160
263
  * @param {Boolean} [screenSpace=0]
264
+ * @param {CanvasRenderingContext2D} [context]
161
265
  * @memberof Draw */
162
- function drawRect(pos, size, color, angle, useWebGL, screenSpace)
163
- { drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, undefined, useWebGL, screenSpace); }
266
+ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
267
+ {
268
+ drawTile(pos, size, undefined, color, angle, 0, undefined, useWebGL, screenSpace, context);
269
+ }
164
270
 
165
271
  /** Draw colored polygon using passed in points
166
272
  * @param {Array} points - Array of Vector2 points
167
273
  * @param {Color} [color=Color()]
168
274
  * @param {Boolean} [useWebGL=glEnable]
169
275
  * @param {Boolean} [screenSpace=0]
276
+ * @param {CanvasRenderingContext2D} [context]
170
277
  * @memberof Draw */
171
- function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
278
+ function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace, context)
172
279
  {
280
+ ASSERT(!context || !useWebGL); // context only supported in canvas 2D mode
281
+
173
282
  if (useWebGL)
174
283
  glDrawPoints(screenSpace ? points.map(screenToWorld) : points, color.rgbaInt());
175
284
  else
176
285
  {
177
286
  // draw using canvas
178
- mainContext.fillStyle = color;
179
- mainContext.beginPath();
287
+ if (!context)
288
+ context = mainContext;
289
+ context.fillStyle = color;
290
+ context.beginPath();
180
291
  for (const point of screenSpace ? points : points.map(worldToScreen))
181
- mainContext.lineTo(point.x, point.y);
182
- mainContext.fill();
292
+ context.lineTo(point.x, point.y);
293
+ context.fill();
183
294
  }
184
295
  }
185
296
 
@@ -190,12 +301,13 @@ function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
190
301
  * @param {Color} [color=Color()]
191
302
  * @param {Boolean} [useWebGL=glEnable]
192
303
  * @param {Boolean} [screenSpace=0]
304
+ * @param {CanvasRenderingContext2D} [context]
193
305
  * @memberof Draw */
194
- function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace)
306
+ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, context)
195
307
  {
196
308
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
197
309
  const size = vec2(thickness, halfDelta.length()*2);
198
- drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace);
310
+ drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace, context);
199
311
  }
200
312
 
201
313
  /** Draw directly to a 2d canvas context in world space
@@ -204,10 +316,10 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace)
204
316
  * @param {Number} angle
205
317
  * @param {Boolean} mirror
206
318
  * @param {Function} drawFunction
207
- * @param {CanvasRenderingContext2D} [context=mainContext]
208
319
  * @param {Boolean} [screenSpace=0]
320
+ * @param {CanvasRenderingContext2D} [context=mainContext]
209
321
  * @memberof Draw */
210
- function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainContext, screenSpace)
322
+ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, context=mainContext)
211
323
  {
212
324
  if (!screenSpace)
213
325
  {
@@ -226,13 +338,19 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
226
338
  /** Enable normal or additive blend mode
227
339
  * @param {Boolean} [additive=0]
228
340
  * @param {Boolean} [useWebGL=glEnable]
341
+ * @param {CanvasRenderingContext2D} [context=mainContext]
229
342
  * @memberof Draw */
230
- function setBlendMode(additive, useWebGL=glEnable)
343
+ function setBlendMode(additive, useWebGL=glEnable, context)
231
344
  {
232
- if (glEnable && useWebGL)
233
- glSetBlendMode(additive);
345
+ ASSERT(!context || !useWebGL); // context only supported in canvas 2D mode
346
+ if (useWebGL)
347
+ glAdditive = additive;
234
348
  else
235
- mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
349
+ {
350
+ if (!context)
351
+ context = mainContext;
352
+ context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
353
+ }
236
354
  }
237
355
 
238
356
  /** Draw text on overlay canvas in world space
@@ -305,10 +423,9 @@ class FontImage
305
423
  * @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
306
424
  * @param {Vector2} [tileSize=vec2(8)] - Size of the font source tiles
307
425
  * @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
308
- * @param {Number} [startTileIndex=0] - Tile index in image where font starts
309
426
  * @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
310
427
  */
311
- constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
428
+ constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
312
429
  {
313
430
  // load default font image
314
431
  if (!engineFontImage)
@@ -317,7 +434,6 @@ class FontImage
317
434
  this.image = image || engineFontImage;
318
435
  this.tileSize = tileSize;
319
436
  this.paddingSize = paddingSize;
320
- this.startTileIndex = startTileIndex;
321
437
  this.context = context;
322
438
  }
323
439
 
@@ -358,7 +474,7 @@ class FontImage
358
474
  charCode = 127; // unknown character
359
475
 
360
476
  // get the character source location and draw it
361
- const tile = this.startTileIndex + charCode - 32;
477
+ const tile = charCode - 32;
362
478
  const x = tile % cols;
363
479
  const y = tile / cols |0;
364
480
  const drawPos = pos.add(vec2(j,i).multiply(drawSize));
@@ -390,4 +506,4 @@ function toggleFullscreen()
390
506
  }
391
507
  else if (document.body.requestFullscreen)
392
508
  document.body.requestFullscreen();
393
- }
509
+ }