littlejsengine 1.14.10 → 1.14.16

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 (76) hide show
  1. package/dist/littlejs.d.ts +264 -120
  2. package/dist/littlejs.esm.js +603 -288
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +600 -287
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +521 -232
  7. package/examples/box2d/gameObjects.js +2 -2
  8. package/examples/breakout/gameObjects.js +2 -2
  9. package/examples/breakoutTutorial/README.md +32 -32
  10. package/examples/breakoutTutorial/game.js +1 -1
  11. package/examples/electron/game.js +3 -3
  12. package/examples/electron/index.html +2 -2
  13. package/examples/electron/package.json +1 -8
  14. package/examples/index.html +59 -51
  15. package/examples/module/game.js +3 -3
  16. package/examples/platformer/gameCharacter.js +6 -2
  17. package/examples/platformer/gameEffects.js +4 -4
  18. package/examples/platformer/gameLevel.js +1 -1
  19. package/examples/platformer/gameObjects.js +11 -9
  20. package/examples/puzzle/game.js +1 -1
  21. package/examples/shorts/animation.js +1 -1
  22. package/examples/shorts/base.html +1 -1
  23. package/examples/shorts/blending.js +8 -14
  24. package/examples/shorts/box2d.js +7 -3
  25. package/examples/shorts/box2dCar.js +2 -1
  26. package/examples/shorts/empty.js +30 -0
  27. package/examples/shorts/helloWorld.js +1 -1
  28. package/examples/shorts/hillGlideGame.js +10 -4
  29. package/examples/shorts/landerGame.js +30 -3
  30. package/examples/shorts/medals.js +4 -4
  31. package/examples/shorts/music.js +29 -55
  32. package/examples/shorts/musicPlayer.js +134 -0
  33. package/examples/shorts/nineSlice.js +34 -15
  34. package/examples/shorts/parallax.js +4 -3
  35. package/examples/shorts/particles.js +16 -16
  36. package/examples/shorts/piano.js +15 -21
  37. package/examples/shorts/pongGame.js +6 -4
  38. package/examples/shorts/raycasting.js +13 -5
  39. package/examples/shorts/sequencer.js +122 -0
  40. package/examples/shorts/shapes.js +7 -4
  41. package/examples/shorts/slidingPuzzle.js +16 -6
  42. package/examples/shorts/sound.js +18 -9
  43. package/examples/shorts/spaceGame.js +12 -8
  44. package/examples/shorts/spriteAtlas.js +7 -7
  45. package/examples/shorts/starfield.js +1 -1
  46. package/examples/shorts/systemFont.js +2 -2
  47. package/examples/shorts/texture.js +2 -2
  48. package/examples/shorts/tileLayer.js +10 -10
  49. package/examples/shorts/tiltedView.js +14 -3
  50. package/examples/shorts/timers.js +10 -0
  51. package/examples/shorts/topDown.js +4 -1
  52. package/examples/shorts/uiSystem.js +8 -5
  53. package/examples/starter/game.js +3 -3
  54. package/examples/starter/index.html +2 -2
  55. package/examples/typescript/game.js +3 -3
  56. package/examples/typescript/game.ts +3 -3
  57. package/examples/uiSystem/game.js +6 -6
  58. package/package.json +4 -2
  59. package/plugins/box2d.js +18 -3
  60. package/plugins/newgrounds.js +11 -9
  61. package/plugins/postProcess.js +5 -2
  62. package/plugins/uiSystem.js +120 -34
  63. package/plugins/zzfxm.js +5 -1
  64. package/reference.md +1 -3
  65. package/src/engine.js +52 -21
  66. package/src/engineAudio.js +31 -14
  67. package/src/engineDebug.js +80 -55
  68. package/src/engineDraw.js +60 -31
  69. package/src/engineExport.js +3 -1
  70. package/src/engineMedals.js +10 -2
  71. package/src/engineObject.js +14 -10
  72. package/src/engineParticles.js +59 -46
  73. package/src/engineRelease.js +1 -0
  74. package/src/engineSettings.js +7 -7
  75. package/src/engineTileLayer.js +49 -18
  76. package/src/engineUtilities.js +79 -34
@@ -24,6 +24,7 @@
24
24
  * - Collision for objects can be set to be solid to block other objects
25
25
  * - Objects may get pushed into overlapping other solid objects, if so they will push away
26
26
  * - Solid objects are more performance intensive and should be used sparingly
27
+ * @memberof Engine
27
28
  * @example
28
29
  * // create an engine object, normally you would first extend the class with your own
29
30
  * const pos = vec2(2,3);
@@ -32,18 +33,18 @@
32
33
  class EngineObject
33
34
  {
34
35
  /** Create an engine object and adds it to the list of objects
35
- * @param {Vector2} [pos=(0,0)] - World space position of the object
36
- * @param {Vector2} [size=(1,1)] - World space size of the object
37
- * @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
38
- * @param {number} [angle] - Angle the object is rotated by
39
- * @param {Color} [color=(1,1,1,1)] - Color to apply to tile when rendered
40
- * @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
36
+ * @param {Vector2} [pos=(0,0)] - World space position of the object
37
+ * @param {Vector2} [size=(1,1)] - World space size of the object
38
+ * @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
39
+ * @param {number} [angle] - Angle the object is rotated by
40
+ * @param {Color} [color=WHITE] - Color to apply to tile when rendered
41
+ * @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
41
42
  */
42
- constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=new Color, renderOrder=0)
43
+ constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=WHITE, renderOrder=0)
43
44
  {
44
45
  // check passed in params
45
- ASSERT(isVector2(pos), 'object pos should be a vec2');
46
- ASSERT(isVector2(size), 'object size should be a vec2');
46
+ ASSERT(isVector2(pos), 'object pos must be a vec2');
47
+ ASSERT(isVector2(size), 'object size must be a vec2');
47
48
  ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
48
49
  ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
49
50
  ASSERT(isColor(color), 'object color should be a valid rgba color');
@@ -328,7 +329,7 @@ class EngineObject
328
329
  drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
329
330
  }
330
331
 
331
- /** Destroy this object, destroy its children, detach it's parent, and mark it for removal */
332
+ /** Destroy this object, destroy its children, detach its parent, and mark it for removal */
332
333
  destroy()
333
334
  {
334
335
  if (this.destroyed)
@@ -402,6 +403,8 @@ class EngineObject
402
403
  addChild(child, localPos=vec2(), localAngle=0)
403
404
  {
404
405
  ASSERT(!child.parent && !this.children.includes(child));
406
+ ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
407
+ ASSERT(child !== this, 'cannot add self as child');
405
408
  this.children.push(child);
406
409
  child.parent = this;
407
410
  child.localPos = localPos.copy();
@@ -413,6 +416,7 @@ class EngineObject
413
416
  removeChild(child)
414
417
  {
415
418
  ASSERT(child.parent === this && this.children.includes(child));
419
+ ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
416
420
  this.children.splice(this.children.indexOf(child), 1);
417
421
  child.parent = 0;
418
422
  }
@@ -4,9 +4,16 @@
4
4
 
5
5
  'use strict';
6
6
 
7
+ /**
8
+ * @callback ParticleCallbackFunction - Function that processes a particle
9
+ * @param {Particle} particle
10
+ * @memberof Engine
11
+ */
12
+
7
13
  /**
8
14
  * Particle Emitter - Spawns particles with the given settings
9
15
  * @extends EngineObject
16
+ * @memberof Engine
10
17
  * @example
11
18
  * // create a particle emitter
12
19
  * let pos = vec2(2,3);
@@ -31,10 +38,10 @@ class ParticleEmitter extends EngineObject
31
38
  * @param {number} [emitRate] - How many particles per second to spawn, does not emit if 0
32
39
  * @param {number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
33
40
  * @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
34
- * @param {Color} [colorStartA=(1,1,1,1)] - Color at start of life 1, randomized between start colors
35
- * @param {Color} [colorStartB=(1,1,1,1)] - Color at start of life 2, randomized between start colors
36
- * @param {Color} [colorEndA=(1,1,1,0)] - Color at end of life 1, randomized between end colors
37
- * @param {Color} [colorEndB=(1,1,1,0)] - Color at end of life 2, randomized between end colors
41
+ * @param {Color} [colorStartA=WHITE] - Color at start of life 1, randomized between start colors
42
+ * @param {Color} [colorStartB=WHITE] - Color at start of life 2, randomized between start colors
43
+ * @param {Color} [colorEndA=CLEAR_WHITE] - Color at end of life 1, randomized between end colors
44
+ * @param {Color} [colorEndB=CLEAR_WHITE] - Color at end of life 2, randomized between end colors
38
45
  * @param {number} [particleTime] - How long particles live
39
46
  * @param {number} [sizeStart] - How big are particles at start
40
47
  * @param {number} [sizeEnd] - How big are particles at end
@@ -61,10 +68,10 @@ class ParticleEmitter extends EngineObject
61
68
  emitRate = 100,
62
69
  emitConeAngle = PI,
63
70
  tileInfo,
64
- colorStartA = new Color,
65
- colorStartB = new Color,
66
- colorEndA = new Color(1,1,1,0),
67
- colorEndB = new Color(1,1,1,0),
71
+ colorStartA = WHITE,
72
+ colorStartB = WHITE,
73
+ colorEndA = CLEAR_WHITE,
74
+ colorEndB = CLEAR_WHITE,
68
75
  particleTime = .5,
69
76
  sizeStart = .1,
70
77
  sizeEnd = 1,
@@ -87,7 +94,8 @@ class ParticleEmitter extends EngineObject
87
94
 
88
95
  // emitter settings
89
96
  /** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
90
- this.emitSize = emitSize
97
+ this.emitSize = emitSize instanceof Vector2 ?
98
+ emitSize.copy() : emitSize;
91
99
  /** @property {number} - How long to stay alive (0 is forever) */
92
100
  this.emitTime = emitTime;
93
101
  /** @property {number} - How many particles per second to spawn, does not emit if 0 */
@@ -97,13 +105,13 @@ class ParticleEmitter extends EngineObject
97
105
 
98
106
  // color settings
99
107
  /** @property {Color} - Color at start of life 1, randomized between start colors */
100
- this.colorStartA = colorStartA;
108
+ this.colorStartA = colorStartA.copy();
101
109
  /** @property {Color} - Color at start of life 2, randomized between start colors */
102
- this.colorStartB = colorStartB;
110
+ this.colorStartB = colorStartB.copy();
103
111
  /** @property {Color} - Color at end of life 1, randomized between end colors */
104
- this.colorEndA = colorEndA;
112
+ this.colorEndA = colorEndA.copy();
105
113
  /** @property {Color} - Color at end of life 2, randomized between end colors */
106
- this.colorEndB = colorEndB;
114
+ this.colorEndB = colorEndB.copy();
107
115
  /** @property {boolean} - Should color be randomized linearly or across each component */
108
116
  this.randomColorLinear = randomColorLinear;
109
117
 
@@ -138,9 +146,9 @@ class ParticleEmitter extends EngineObject
138
146
  this.localSpace = localSpace;
139
147
  /** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
140
148
  this.trailScale = 0;
141
- /** @property {Function} - Callback when particle is destroyed */
149
+ /** @property {ParticleCallbackFunction} - Callback when particle is destroyed */
142
150
  this.particleDestroyCallback = undefined;
143
- /** @property {Function} - Callback when particle is created */
151
+ /** @property {ParticleCallbackFunction} - Callback when particle is created */
144
152
  this.particleCreateCallback = undefined;
145
153
  /** @property {number} - Track particle emit time */
146
154
  this.emitTimeBuffer = 0;
@@ -241,6 +249,7 @@ class ParticleEmitter extends EngineObject
241
249
  /**
242
250
  * Particle Object - Created automatically by Particle Emitters
243
251
  * @extends EngineObject
252
+ * @memberof Engine
244
253
  */
245
254
  class Particle extends EngineObject
246
255
  {
@@ -259,7 +268,7 @@ class Particle extends EngineObject
259
268
  * @param {boolean} additive - Does it use additive blend mode
260
269
  * @param {number} trailScale - If a trail, how long to make it
261
270
  * @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
262
- * @param {Function} [destroyCallback] - Callback when particle dies
271
+ * @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
263
272
  */
264
273
  constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
265
274
  )
@@ -268,14 +277,14 @@ class Particle extends EngineObject
268
277
 
269
278
  /** @property {Color} - Color at start of life */
270
279
  this.colorStart = colorStart;
271
- /** @property {Color} - Calculated change in color */
272
- this.colorEndDelta = colorEnd.subtract(colorStart);
280
+ /** @property {Color} - Color at end of life */
281
+ this.colorEnd = colorEnd;
273
282
  /** @property {number} - How long to live for */
274
283
  this.lifeTime = lifeTime;
275
284
  /** @property {number} - Size at start of life */
276
285
  this.sizeStart = sizeStart;
277
- /** @property {number} - Calculated change in size */
278
- this.sizeEndDelta = sizeEnd - sizeStart;
286
+ /** @property {number} - Size at end of life */
287
+ this.sizeEnd = sizeEnd;
279
288
  /** @property {number} - How quick to fade in/out */
280
289
  this.fadeRate = fadeRate;
281
290
  /** @property {boolean} - Is it additive */
@@ -284,7 +293,7 @@ class Particle extends EngineObject
284
293
  this.trailScale = trailScale;
285
294
  /** @property {ParticleEmitter} - Parent emitter if local space */
286
295
  this.localSpaceEmitter = localSpaceEmitter;
287
- /** @property {Function} - Called when particle dies */
296
+ /** @property {ParticleCallbackFunction} - Called when particle dies */
288
297
  this.destroyCallback = destroyCallback;
289
298
  // particles do not clamp speed by default
290
299
  this.clampSpeed = false;
@@ -311,54 +320,58 @@ class Particle extends EngineObject
311
320
  /** Render the particle, automatically called each frame, sorted by renderOrder */
312
321
  render()
313
322
  {
314
- // modulate size and color
315
- const p = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1;
316
- const radius = this.sizeStart + p * this.sizeEndDelta;
317
- const size = vec2(radius);
323
+ // lerp color and size
324
+ const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
325
+ const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
326
+ this.size.x = this.size.y = radius;
327
+ this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
328
+ this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
329
+ this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
330
+ this.color.a = p2 * this.colorStart.a + p1 * this.colorEnd.a;
331
+
332
+ // fade alpha
318
333
  const fadeRate = this.fadeRate/2;
319
- const color = new Color(
320
- this.colorStart.r + p * this.colorEndDelta.r,
321
- this.colorStart.g + p * this.colorEndDelta.g,
322
- this.colorStart.b + p * this.colorEndDelta.b,
323
- (this.colorStart.a + p * this.colorEndDelta.a) *
324
- (p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
334
+ this.color.a *= p1 < fadeRate ? p1/fadeRate :
335
+ p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
325
336
 
326
337
  // draw the particle
327
338
  this.additive && setBlendMode(true);
328
339
 
340
+ // update the position and angle for drawing
329
341
  let pos = this.pos, angle = this.angle;
330
342
  if (this.localSpaceEmitter)
331
343
  {
332
344
  // in local space of emitter
333
- pos = this.localSpaceEmitter.pos.add(pos.rotate(-this.localSpaceEmitter.angle));
345
+ const a = this.localSpaceEmitter.angle;
346
+ const c = Math.cos(a), s = Math.sin(a);
347
+ pos = this.localSpaceEmitter.pos.add(
348
+ new Vector2(pos.x*c - pos.y*s, pos.x*s + pos.y*c));
334
349
  angle += this.localSpaceEmitter.angle;
335
350
  }
336
351
  if (this.trailScale)
337
352
  {
338
353
  // trail style particles
339
- let velocity = this.velocity;
340
- if (this.localSpaceEmitter)
341
- velocity = velocity.rotate(-this.localSpaceEmitter.angle);
342
- const speed = velocity.length();
354
+ const direction = this.localSpaceEmitter ?
355
+ this.velocity.rotate(-this.localSpaceEmitter.angle) :
356
+ this.velocity;
357
+ const speed = direction.length();
343
358
  if (speed)
344
359
  {
345
- const direction = velocity.scale(1/speed);
360
+ // stretch in direction of motion
346
361
  const trailLength = speed * this.trailScale;
347
- size.y = max(size.x, trailLength);
348
- angle = direction.angle();
349
- drawTile(pos.add(direction.multiply(vec2(0,-trailLength/2))), size, this.tileInfo, color, angle, this.mirror);
362
+ this.size.y = max(this.size.x, trailLength);
363
+ angle = Math.atan2(direction.x, direction.y);
364
+ drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
350
365
  }
351
366
  }
352
367
  else
353
- drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
368
+ drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
354
369
  this.additive && setBlendMode();
355
- debugParticles && debugRect(pos, size, '#f005', 0, angle);
370
+ debugParticles && debugRect(pos, this.size, '#f005', 0, angle);
356
371
 
357
- if (p === 1)
372
+ if (p1 === 1)
358
373
  {
359
- // destroy particle when it's time runs out
360
- this.color = color;
361
- this.size = size;
374
+ // destroy particle when its time runs out
362
375
  this.destroyCallback && this.destroyCallback(this);
363
376
  this.destroyed = 1;
364
377
  }
@@ -18,6 +18,7 @@ const debugMedals = 0;
18
18
 
19
19
  // debug commands are automatically removed from the final build
20
20
  function ASSERT (){}
21
+ function LOG (){}
21
22
  function debugInit (){}
22
23
  function debugUpdate (){}
23
24
  function debugRender (){}
@@ -306,7 +306,7 @@ let medalsPreventUnlock = false;
306
306
  /** Set position of camera in world space
307
307
  * @param {Vector2} pos
308
308
  * @memberof Settings */
309
- function setCameraPos(pos) { cameraPos = pos; }
309
+ function setCameraPos(pos) { cameraPos = pos.copy(); }
310
310
 
311
311
  /** Set angle of camera in world space
312
312
  * @param {number} angle
@@ -329,17 +329,17 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
329
329
  /** Set color to clear the canvas to before render
330
330
  * @param {Color} color
331
331
  * @memberof Settings */
332
- function setCanvasClearColor(color) { canvasClearColor = color; }
332
+ function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
333
333
 
334
334
  /** Set max size of the canvas
335
335
  * @param {Vector2} size
336
336
  * @memberof Settings */
337
- function setCanvasMaxSize(size) { canvasMaxSize = size; }
337
+ function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
338
338
 
339
339
  /** Set fixed size of the canvas
340
340
  * @param {Vector2} size
341
341
  * @memberof Settings */
342
- function setCanvasFixedSize(size) { canvasFixedSize = size; }
342
+ function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
343
343
 
344
344
  /** Use nearest scaling algorithm for canvas for more pixelated look
345
345
  * - If enabled sets css image-rendering:pixelated
@@ -404,7 +404,7 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
404
404
  /** Set default size of tiles in pixels
405
405
  * @param {Vector2} size
406
406
  * @memberof Settings */
407
- function setTileSizeDefault(size) { tileSizeDefault = size; }
407
+ function setTileSizeDefault(size) { tileSizeDefault = size.copy(); }
408
408
 
409
409
  /** Set to prevent tile bleeding from neighbors in pixels
410
410
  * @param {number} scale
@@ -449,7 +449,7 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
449
449
  /** Set how much gravity to apply to objects
450
450
  * @param {Vector2} newGravity
451
451
  * @memberof Settings */
452
- function setGravity(newGravity) { gravity = newGravity; }
452
+ function setGravity(newGravity) { gravity = newGravity.copy(); }
453
453
 
454
454
  /** Set to scales emit rate of particles
455
455
  * @param {number} scale
@@ -539,7 +539,7 @@ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
539
539
  /** Set size of medal display
540
540
  * @param {Vector2} size
541
541
  * @memberof Settings */
542
- function setMedalDisplaySize(size) { medalDisplaySize = size; }
542
+ function setMedalDisplaySize(size) { medalDisplaySize = size.copy(); }
543
543
 
544
544
  /** Set to stop medals from being unlockable
545
545
  * @param {boolean} preventUnlock
@@ -4,7 +4,7 @@
4
4
  * - Unlimited numbers of layers, allocates canvases as needed
5
5
  * - Tile layers can be drawn to using their context with canvas2d
6
6
  * - Tile layers can also have collision with EngineObjects
7
- * @namespace TileCollision
7
+ * @namespace TileLayers
8
8
  */
9
9
 
10
10
  'use strict';
@@ -14,13 +14,13 @@
14
14
 
15
15
  /** Keep track of all tile layers with collision
16
16
  * @type {Array<TileCollisionLayer>}
17
- * @memberof TileCollision */
17
+ * @memberof TileLayers */
18
18
  const tileCollisionLayers = [];
19
19
 
20
20
  /** Get tile collision data for a given cell in the grid
21
21
  * @param {Vector2} pos
22
22
  * @return {number}
23
- * @memberof TileCollision */
23
+ * @memberof TileLayers */
24
24
  function tileCollisionGetData(pos)
25
25
  {
26
26
  // check all tile collision layers
@@ -36,7 +36,7 @@ function tileCollisionGetData(pos)
36
36
  * @param {EngineObject} [object] - An object or undefined for generic test
37
37
  * @param {boolean} [solidOnly] - Only check solid layers if true
38
38
  * @return {TileCollisionLayer}
39
- * @memberof TileCollision */
39
+ * @memberof TileLayers */
40
40
  function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
41
41
  {
42
42
  for (const layer of tileCollisionLayers)
@@ -54,7 +54,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
54
54
  * @param {EngineObject} [object] - An object or undefined for generic test
55
55
  * @param {boolean} [solidOnly=true] - Only check solid layers if true
56
56
  * @return {Vector2}
57
- * @memberof TileCollision */
57
+ * @memberof TileLayers */
58
58
  function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
59
59
  {
60
60
  for (const layer of tileCollisionLayers)
@@ -77,8 +77,8 @@ function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
77
77
  * @param {number} [collisionLayer] - Layer to use for collision if any
78
78
  * @param {boolean} [draw] - Should the layer be drawn automatically
79
79
  * @return {Array<TileCollisionLayer>}
80
- * @memberof TileCollision */
81
- function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
80
+ * @memberof TileLayers */
81
+ function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
82
82
  {
83
83
  if (!tileMapData)
84
84
  {
@@ -108,6 +108,12 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
108
108
  const tileLayer = new TileCollisionLayer(vec2(), levelSize, tileInfo, layerRenderOrder);
109
109
  tileLayers[layerIndex] = tileLayer;
110
110
 
111
+ // apply layer color
112
+ const layerColor = dataLayer.tintcolor ?
113
+ new Color().setHex(dataLayer.tintcolor) :
114
+ dataLayer.color || WHITE;
115
+ ASSERT(isColor(layerColor), 'layer color is not a color');
116
+
111
117
  for (let x=levelSize.x; x--;)
112
118
  for (let y=levelSize.y; y--;)
113
119
  {
@@ -115,7 +121,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
115
121
  const data = dataLayer.data[x + y*levelSize.x];
116
122
  if (data)
117
123
  {
118
- const layerData = new TileLayerData(data-1);
124
+ const layerData = new TileLayerData(data-1, 0, false, layerColor);
119
125
  tileLayer.setData(pos, layerData);
120
126
 
121
127
  // set collision for top layer
@@ -132,6 +138,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
132
138
  ///////////////////////////////////////////////////////////////////////////////
133
139
  /**
134
140
  * Tile layer data object stores info about how to draw a tile
141
+ * @memberof TileLayers
135
142
  * @example
136
143
  * // create tile layer data with tile index 0 and random orientation and color
137
144
  * const tileIndex = 0;
@@ -169,6 +176,7 @@ class TileLayerData
169
176
  * - Contains an offscreen canvas that can be rendered to
170
177
  * - WebGL rendering is optional, call useWebGL to enable
171
178
  * @extends EngineObject
179
+ * @memberof TileLayers
172
180
  * @example
173
181
  * const canvasLayer = new CanvasLayer(vec2(), vec2(200,100));
174
182
  */
@@ -183,6 +191,7 @@ class CanvasLayer extends EngineObject
183
191
  */
184
192
  constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
185
193
  {
194
+ ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
186
195
  super(position, size, undefined, angle, WHITE, renderOrder);
187
196
 
188
197
  /** @property {HTMLCanvasElement} - The canvas used by this layer */
@@ -230,12 +239,18 @@ class CanvasLayer extends EngineObject
230
239
  drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
231
240
  }
232
241
 
242
+ /**
243
+ * @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
244
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
245
+ * @memberof TileLayers
246
+ */
247
+
233
248
  /** Draw onto the layer canvas in world space (bypass WebGL)
234
249
  * @param {Vector2} pos
235
250
  * @param {Vector2} size
236
251
  * @param {number} angle
237
252
  * @param {boolean} mirror
238
- * @param {Function} drawFunction */
253
+ * @param {Canvas2DDrawCallback} drawFunction */
239
254
  drawCanvas2D(pos, size, angle, mirror, drawFunction)
240
255
  {
241
256
  const context = this.context;
@@ -272,7 +287,7 @@ class CanvasLayer extends EngineObject
272
287
  else
273
288
  {
274
289
  // untextured
275
- context.fillStyle = color;
290
+ context.fillStyle = color.toString();
276
291
  context.fillRect(-.5, -.5, 1, 1);
277
292
  }
278
293
  });
@@ -309,7 +324,9 @@ class CanvasLayer extends EngineObject
309
324
  * - To allow dynamic modifications, layers are rendered using canvas 2d
310
325
  * - Some devices like mobile phones are limited to 4k texture resolution
311
326
  * - For with 16x16 tiles this limits layers to 256x256 on mobile devices
327
+ * - Tile layers are centered on their corner, so normal levels are at (0,0)
312
328
  * @extends CanvasLayer
329
+ * @memberof TileLayers
313
330
  * @example
314
331
  * const tileLayer = new TileLayer(vec2(), vec2(200,100));
315
332
  */
@@ -319,15 +336,14 @@ class TileLayer extends CanvasLayer
319
336
  * @param {Vector2} position - World space position
320
337
  * @param {Vector2} size - World space size
321
338
  * @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
322
- * @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
323
339
  * @param {number} [renderOrder] - Objects are sorted by renderOrder
324
340
  * @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
325
341
  */
326
- constructor(position, size, tileInfo=tile(), scale=vec2(1), renderOrder=0, useWebGL=glEnable)
342
+ constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
327
343
  {
328
344
  super(position, size, 0, renderOrder, size);
345
+
329
346
  this.tileInfo = tileInfo;
330
-
331
347
  const canvasSize = size.multiply(tileInfo.size);
332
348
  /** @property {HTMLCanvasElement} - The canvas used by this tile layer */
333
349
  this.canvas = new OffscreenCanvas(canvasSize.x, canvasSize.y);
@@ -364,6 +380,8 @@ class TileLayer extends CanvasLayer
364
380
  * @param {boolean} [redraw] - Force the tile to redraw if true */
365
381
  setData(layerPos, data, redraw=false)
366
382
  {
383
+ ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
384
+ ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
367
385
  if (layerPos.arrayCheck(this.size))
368
386
  {
369
387
  this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
@@ -375,7 +393,10 @@ class TileLayer extends CanvasLayer
375
393
  * @param {Vector2} layerPos - Local position in array
376
394
  * @return {TileLayerData} */
377
395
  getData(layerPos)
378
- { return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0]; }
396
+ {
397
+ ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
398
+ return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
399
+ }
379
400
 
380
401
  // Render the tile layer, called automatically by the engine
381
402
  render()
@@ -384,9 +405,10 @@ class TileLayer extends CanvasLayer
384
405
 
385
406
  // draw the tile layer as a single tile
386
407
  const tileInfo = new TileInfo().setFullImage(this.canvas, this.glTexture);
387
- const pos = this.pos.add(this.size.scale(.5));
408
+ const size = this.drawSize || this.size;
409
+ const pos = this.pos.add(size.scale(.5));
388
410
  const useWebGL = glEnable && this.glTexture !== undefined;
389
- drawTile(pos, this.size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
411
+ drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
390
412
  }
391
413
 
392
414
  /** Draw all the tile data to an offscreen canvas
@@ -478,6 +500,7 @@ class TileLayer extends CanvasLayer
478
500
  * - there can be multiple tile collision layers
479
501
  * - tile collision layers should not overlap each other
480
502
  * @extends TileLayer
503
+ * @memberof TileLayers
481
504
  */
482
505
  class TileCollisionLayer extends TileLayer
483
506
  {
@@ -490,8 +513,7 @@ class TileCollisionLayer extends TileLayer
490
513
  */
491
514
  constructor(position, size, tileInfo=tile(), renderOrder=0, useWebGL=glEnable)
492
515
  {
493
- const scale = vec2(1); // collision layers are not scaled
494
- super(position, size.floor(), tileInfo, scale, renderOrder, useWebGL);
516
+ super(position, size.floor(), tileInfo, renderOrder, useWebGL);
495
517
 
496
518
  /** @property {Array<number>} - The tile collision grid */
497
519
  this.collisionData = [];
@@ -521,6 +543,7 @@ class TileCollisionLayer extends TileLayer
521
543
  * @param {Vector2} size - width and height of tile collision 2d grid */
522
544
  initCollision(size)
523
545
  {
546
+ ASSERT(isVector2(size), 'size must be a Vector2');
524
547
  this.size = size.floor();
525
548
  this.collisionData = [];
526
549
  this.collisionData.length = size.area();
@@ -532,6 +555,7 @@ class TileCollisionLayer extends TileLayer
532
555
  * @param {number} [data] */
533
556
  setCollisionData(gridPos, data=1)
534
557
  {
558
+ ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
535
559
  const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
536
560
  gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
537
561
  }
@@ -541,6 +565,7 @@ class TileCollisionLayer extends TileLayer
541
565
  * @return {number} */
542
566
  getCollisionData(gridPos)
543
567
  {
568
+ ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
544
569
  const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
545
570
  return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
546
571
  }
@@ -552,6 +577,9 @@ class TileCollisionLayer extends TileLayer
552
577
  * @return {boolean} */
553
578
  collisionTest(pos, size=new Vector2, object)
554
579
  {
580
+ ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
581
+ ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
582
+
555
583
  // transform to local layer space
556
584
  const posX = pos.x - this.pos.x;
557
585
  const posY = pos.y - this.pos.y;
@@ -582,6 +610,9 @@ class TileCollisionLayer extends TileLayer
582
610
  * @return {Vector2} */
583
611
  collisionRaycast(posStart, posEnd, object)
584
612
  {
613
+ ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
614
+ ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
615
+
585
616
  // transform to local layer space
586
617
  const posStartX = posStart.x - this.pos.x;
587
618
  const posStartY = posStart.y - this.pos.y;