littlejsengine 1.9.4 → 1.9.5

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.
package/LittleJS.zip ADDED
Binary file
@@ -1,4 +1,4 @@
1
- declare module "littlejs.esm" {
1
+ declare module "littlejsengine" {
2
2
  /**
3
3
  * LittleJS - The Tiny Fast JavaScript Game Engine
4
4
  * MIT License - Copyright 2021 Frank Force
@@ -79,7 +79,7 @@ declare module "littlejs.esm" {
79
79
  * @memberof Engine */
80
80
  export function engineObjectsDestroy(): void;
81
81
  /** Triggers a callback for each object within a given area
82
- * @param {Vector2} [pos] - Center of test area
82
+ * @param {Vector2} [pos] - Center of test area, or undefined for all objects
83
83
  * @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
84
84
  * @param {Function} [callbackFunction] - Calls this function on every object that passes the test
85
85
  * @param {Array} [objects=engineObjects] - List of objects to check
@@ -619,13 +619,13 @@ declare module "littlejs.esm" {
619
619
  * @memberof Utilities */
620
620
  export function nearestPowerOfTwo(value: number): number;
621
621
  /** Returns true if two axis aligned bounding boxes are overlapping
622
- * @param {Vector2} pointA - Center of box A
623
- * @param {Vector2} sizeA - Size of box A
624
- * @param {Vector2} pointB - Center of box B
625
- * @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
626
- * @return {Boolean} - True if overlapping
622
+ * @param {Vector2} posA - Center of box A
623
+ * @param {Vector2} sizeA - Size of box A
624
+ * @param {Vector2} posB - Center of box B
625
+ * @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
626
+ * @return {Boolean} - True if overlapping
627
627
  * @memberof Utilities */
628
- export function isOverlapping(pointA: Vector2, sizeA: Vector2, pointB: Vector2, sizeB?: Vector2): boolean;
628
+ export function isOverlapping(posA: Vector2, sizeA: Vector2, posB: Vector2, sizeB?: Vector2): boolean;
629
629
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
630
630
  * @param {Number} [frequency] - Frequency of the wave in Hz
631
631
  * @param {Number} [amplitude] - Amplitude (max height) of the wave
@@ -1637,6 +1637,8 @@ declare module "littlejs.esm" {
1637
1637
  collideSolidObjects: boolean;
1638
1638
  /** @property {Boolean} - Object collides with and blocks other objects */
1639
1639
  isSolid: boolean;
1640
+ /** @property {Boolean} - Object collides with raycasts */
1641
+ collideRaycast: boolean;
1640
1642
  /** Update the object transform and physics, called automatically by engine once each frame */
1641
1643
  update(): void;
1642
1644
  groundObject: any;
@@ -1650,11 +1652,6 @@ declare module "littlejs.esm" {
1650
1652
  * @param {Vector2} pos - tile where the collision occured
1651
1653
  * @return {Boolean} - true if the collision should be resolved */
1652
1654
  collideWithTile(tileData: number, pos: Vector2): boolean;
1653
- /** Called to check if a tile raycast hit
1654
- * @param {Number} tileData - the value of the tile at the position
1655
- * @param {Vector2} pos - tile where the raycast is
1656
- * @return {Boolean} - true if the raycast should hit */
1657
- collideWithTileRaycast(tileData: number, pos: Vector2): boolean;
1658
1655
  /** Called to check if a object collision should be resolved
1659
1656
  * @param {EngineObject} object - the object to test against
1660
1657
  * @return {Boolean} - true if the collision should be resolved
@@ -1681,10 +1678,11 @@ declare module "littlejs.esm" {
1681
1678
  * @param {EngineObject} child */
1682
1679
  removeChild(child: EngineObject): void;
1683
1680
  /** Set how this object collides
1684
- * @param {Boolean} [collideSolidObjects] - Does it collide with solid objects
1685
- * @param {Boolean} [isSolid] - Does it collide with and block other objects (expensive in large numbers)
1686
- * @param {Boolean} [collideTiles] - Does it collide with the tile collision */
1687
- setCollision(collideSolidObjects?: boolean, isSolid?: boolean, collideTiles?: boolean): void;
1681
+ * @param {Boolean} [collideSolidObjects] - Does it collide with solid objects?
1682
+ * @param {Boolean} [isSolid] - Does it collide with and block other objects? (expensive in large numbers)
1683
+ * @param {Boolean} [collideTiles] - Does it collide with the tile collision?
1684
+ * @param {Boolean} [collideRaycast] - Does it collide with raycasts? */
1685
+ setCollision(collideSolidObjects?: boolean, isSolid?: boolean, collideTiles?: boolean, collideRaycast?: boolean): void;
1688
1686
  /** Returns string containg info about this object for debugging
1689
1687
  * @return {String} */
1690
1688
  toString(): string;
@@ -1729,7 +1727,7 @@ declare module "littlejs.esm" {
1729
1727
  * @return {Boolean}
1730
1728
  * @memberof TileCollision */
1731
1729
  export function tileCollisionTest(pos: Vector2, size?: Vector2, object?: EngineObject): boolean;
1732
- /** Return the center of tile if any that is hit (does not return the exact intersection)
1730
+ /** Return the center of first tile hit (does not return the exact intersection)
1733
1731
  * @param {Vector2} posStart
1734
1732
  * @param {Vector2} posEnd
1735
1733
  * @param {EngineObject} [object]
@@ -357,7 +357,7 @@ function debugRender()
357
357
  overlayContext.restore();
358
358
  });
359
359
 
360
- // remove expired pritives
360
+ // remove expired primitives
361
361
  debugPrimitives = debugPrimitives.filter(r=>r.time<0);
362
362
  }
363
363
 
@@ -542,16 +542,57 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
542
542
  function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
543
543
 
544
544
  /** Returns true if two axis aligned bounding boxes are overlapping
545
- * @param {Vector2} pointA - Center of box A
546
- * @param {Vector2} sizeA - Size of box A
547
- * @param {Vector2} pointB - Center of box B
548
- * @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
549
- * @return {Boolean} - True if overlapping
545
+ * @param {Vector2} posA - Center of box A
546
+ * @param {Vector2} sizeA - Size of box A
547
+ * @param {Vector2} posB - Center of box B
548
+ * @param {Vector2} [sizeB=(0,0)] - Size of box B, a point if undefined
549
+ * @return {Boolean} - True if overlapping
550
550
  * @memberof Utilities */
551
- function isOverlapping(pointA, sizeA, pointB, sizeB=vec2())
551
+ function isOverlapping(posA, sizeA, posB, sizeB=vec2())
552
552
  {
553
- return abs(pointA.x - pointB.x)*2 < sizeA.x + sizeB.x
554
- && abs(pointA.y - pointB.y)*2 < sizeA.y + sizeB.y;
553
+ return abs(posA.x - posB.x)*2 < sizeA.x + sizeB.x
554
+ && abs(posA.y - posB.y)*2 < sizeA.y + sizeB.y;
555
+ }
556
+
557
+ /** Returns true if a line segment is intersecting an axis aligned box
558
+ * @param {Vector2} start - Start of raycast
559
+ * @param {Vector2} end - End of raycast
560
+ * @param {Vector2} pos - Center of box
561
+ * @param {Vector2} size - Size of box
562
+ * @return {Boolean} - True if intersecting
563
+ * @memberof Utilities */
564
+ function isIntersecting(start, end, pos, size)
565
+ {
566
+ // Liang-Barsky algorithm
567
+ const boxMin = pos.subtract(size.scale(.5));
568
+ const boxMax = boxMin.add(size);
569
+ const delta = end.subtract(start);
570
+ const a = start.subtract(boxMin);
571
+ const b = start.subtract(boxMax);
572
+ const p = [-delta.x, delta.x, -delta.y, delta.y];
573
+ const q = [a.x, -b.x, a.y, -b.y];
574
+ let tMin = 0, tMax = 1;
575
+ for (let i = 4; i--;)
576
+ {
577
+ if (p[i])
578
+ {
579
+ const t = q[i] / p[i];
580
+ if (p[i] < 0)
581
+ {
582
+ if (t > tMax) return false;
583
+ tMin = max(t, tMin);
584
+ }
585
+ else
586
+ {
587
+ if (t < tMin) return false;
588
+ tMax = min(t, tMax);
589
+ }
590
+ }
591
+ else if (q[i] < 0)
592
+ return false;
593
+ }
594
+
595
+ return true;
555
596
  }
556
597
 
557
598
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
@@ -1735,6 +1776,8 @@ class EngineObject
1735
1776
  this.collideSolidObjects = false;
1736
1777
  /** @property {Boolean} - Object collides with and blocks other objects */
1737
1778
  this.isSolid = false;
1779
+ /** @property {Boolean} - Object collides with raycasts */
1780
+ this.collideRaycast = false;
1738
1781
 
1739
1782
  // add to list of objects
1740
1783
  engineObjects.push(this);
@@ -1939,13 +1982,7 @@ class EngineObject
1939
1982
  * @param {Number} tileData - the value of the tile at the position
1940
1983
  * @param {Vector2} pos - tile where the collision occured
1941
1984
  * @return {Boolean} - true if the collision should be resolved */
1942
- collideWithTile(tileData, pos) { return tileData > 0; }
1943
-
1944
- /** Called to check if a tile raycast hit
1945
- * @param {Number} tileData - the value of the tile at the position
1946
- * @param {Vector2} pos - tile where the raycast is
1947
- * @return {Boolean} - true if the raycast should hit */
1948
- collideWithTileRaycast(tileData, pos) { return tileData > 0; }
1985
+ collideWithTile(tileData, pos) { return tileData > 0; }
1949
1986
 
1950
1987
  /** Called to check if a object collision should be resolved
1951
1988
  * @param {EngineObject} object - the object to test against
@@ -1992,16 +2029,18 @@ class EngineObject
1992
2029
  }
1993
2030
 
1994
2031
  /** Set how this object collides
1995
- * @param {Boolean} [collideSolidObjects] - Does it collide with solid objects
1996
- * @param {Boolean} [isSolid] - Does it collide with and block other objects (expensive in large numbers)
1997
- * @param {Boolean} [collideTiles] - Does it collide with the tile collision */
1998
- setCollision(collideSolidObjects=true, isSolid=true, collideTiles=true)
2032
+ * @param {Boolean} [collideSolidObjects] - Does it collide with solid objects?
2033
+ * @param {Boolean} [isSolid] - Does it collide with and block other objects? (expensive in large numbers)
2034
+ * @param {Boolean} [collideTiles] - Does it collide with the tile collision?
2035
+ * @param {Boolean} [collideRaycast] - Does it collide with raycasts? */
2036
+ setCollision(collideSolidObjects=true, isSolid=true, collideTiles=true, collideRaycast=true)
1999
2037
  {
2000
2038
  ASSERT(collideSolidObjects || !isSolid, 'solid objects must be set to collide');
2001
2039
 
2002
2040
  this.collideSolidObjects = collideSolidObjects;
2003
2041
  this.isSolid = isSolid;
2004
2042
  this.collideTiles = collideTiles;
2043
+ this.collideRaycast = collideRaycast;
2005
2044
  }
2006
2045
 
2007
2046
  /** Returns string containg info about this object for debugging
@@ -3654,7 +3693,7 @@ function tileCollisionTest(pos, size=vec2(), object)
3654
3693
  }
3655
3694
  }
3656
3695
 
3657
- /** Return the center of tile if any that is hit (does not return the exact intersection)
3696
+ /** Return the center of first tile hit (does not return the exact intersection)
3658
3697
  * @param {Vector2} posStart
3659
3698
  * @param {Vector2} posEnd
3660
3699
  * @param {EngineObject} [object]
@@ -3850,8 +3889,11 @@ class TileLayer extends EngineObject
3850
3889
  mainCanvas.height = mainCanvasSize.y;
3851
3890
  }
3852
3891
 
3853
- // begin a new render for the tile canvas
3854
- enginePreRender();
3892
+ // disable smoothing for pixel art
3893
+ this.context.imageSmoothingEnabled = !canvasPixelated;
3894
+
3895
+ // setup gl rendering if enabled
3896
+ glEnable && glPreRender();
3855
3897
  }
3856
3898
 
3857
3899
  /** Call to end the redraw process */
@@ -5000,7 +5042,7 @@ const engineName = 'LittleJS';
5000
5042
  * @type {String}
5001
5043
  * @default
5002
5044
  * @memberof Engine */
5003
- const engineVersion = '1.9.4';
5045
+ const engineVersion = '1.9.5';
5004
5046
 
5005
5047
  /** Frames per second to update
5006
5048
  * @type {Number}
@@ -5067,6 +5109,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5067
5109
  {
5068
5110
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
5069
5111
 
5112
+ // Called automatically by engine to setup render system
5113
+ function enginePreRender()
5114
+ {
5115
+ // save canvas size
5116
+ mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5117
+
5118
+ // disable smoothing for pixel art
5119
+ mainContext.imageSmoothingEnabled = !canvasPixelated;
5120
+
5121
+ // setup gl rendering if enabled
5122
+ glEnable && glPreRender();
5123
+ }
5124
+
5070
5125
  // internal update loop for engine
5071
5126
  function engineUpdate(frameTimeMS=0)
5072
5127
  {
@@ -5224,7 +5279,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5224
5279
  }
5225
5280
  image.src = src;
5226
5281
  })
5227
- )
5282
+ );
5228
5283
 
5229
5284
  // draw splash screen
5230
5285
  showSplashScreen && promises.push(new Promise(resolve =>
@@ -5249,19 +5304,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5249
5304
  });
5250
5305
  }
5251
5306
 
5252
- // Called automatically by engine to setup render system
5253
- function enginePreRender()
5254
- {
5255
- // save canvas size
5256
- mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
5257
-
5258
- // disable smoothing for pixel art
5259
- mainContext.imageSmoothingEnabled = !canvasPixelated;
5260
-
5261
- // setup gl rendering if enabled
5262
- glEnable && glPreRender();
5263
- }
5264
-
5265
5307
  /** Update each engine object, remove destroyed objects, and update time
5266
5308
  * @memberof Engine */
5267
5309
  function engineObjectsUpdate()
@@ -5295,30 +5337,63 @@ function engineObjectsDestroy()
5295
5337
  engineObjects = engineObjects.filter(o=>!o.destroyed);
5296
5338
  }
5297
5339
 
5298
- /** Triggers a callback for each object within a given area
5299
- * @param {Vector2} [pos] - Center of test area
5340
+ /** Collects all object within a given area
5341
+ * @param {Vector2} [pos] - Center of test area, or undefined for all objects
5300
5342
  * @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
5301
- * @param {Function} [callbackFunction] - Calls this function on every object that passes the test
5302
5343
  * @param {Array} [objects=engineObjects] - List of objects to check
5344
+ * @return {Array} - List of collected objects
5303
5345
  * @memberof Engine */
5304
- function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
5346
+ function engineObjectsCollect(pos, size, objects=engineObjects)
5305
5347
  {
5348
+ const collectedObjects = [];
5306
5349
  if (!pos) // all objects
5307
5350
  {
5308
5351
  for (const o of objects)
5309
- callbackFunction(o);
5352
+ collectedObjects.push(o);
5310
5353
  }
5311
- else if (typeof size === 'object') // bounding box test
5354
+ else if (size instanceof Vector2) // bounding box test
5312
5355
  {
5313
5356
  for (const o of objects)
5314
- isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
5357
+ isOverlapping(pos, size, o.pos, o.size) && collectedObjects.push(o);
5315
5358
  }
5316
5359
  else // circle test
5317
5360
  {
5318
5361
  const sizeSquared = size*size;
5319
5362
  for (const o of objects)
5320
- pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
5363
+ pos.distanceSquared(o.pos) < sizeSquared && collectedObjects.push(o);
5364
+ }
5365
+ return collectedObjects;
5366
+ }
5367
+
5368
+ /** Triggers a callback for each object within a given area
5369
+ * @param {Vector2} [pos] - Center of test area, or undefined for all objects
5370
+ * @param {Number|Vector2} [size] - Radius of circle if float, rectangle size if Vector2
5371
+ * @param {Function} [callbackFunction] - Calls this function on every object that passes the test
5372
+ * @param {Array} [objects=engineObjects] - List of objects to check
5373
+ * @memberof Engine */
5374
+ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
5375
+ { engineObjectsCollect(pos, size, objects).forEach(o => callbackFunction(o)); }
5376
+
5377
+ /** Return a list of objects intersecting a ray
5378
+ * @param {Vector2} start
5379
+ * @param {Vector2} end
5380
+ * @param {Array} [objects=engineObjects] - List of objects to check
5381
+ * @return {Array} - List of objects hit
5382
+ * @memberof Engine */
5383
+ function engineObjectsRaycast(start, end, objects=engineObjects)
5384
+ {
5385
+ const hitObjects = [];
5386
+ for (const o of objects)
5387
+ {
5388
+ if (o.collideRaycast && isIntersecting(start, end, o.pos, o.size))
5389
+ {
5390
+ debugRaycast && debugRect(o.pos, o.size, '#f00');
5391
+ hitObjects.push(o);
5392
+ }
5321
5393
  }
5394
+
5395
+ debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
5396
+ return hitObjects;
5322
5397
  }
5323
5398
 
5324
5399
  ///////////////////////////////////////////////////////////////////////////////
@@ -5406,16 +5481,16 @@ function drawEngineSplashScreen(t)
5406
5481
  rect(37,14,9,6);
5407
5482
 
5408
5483
  // big stack
5409
- rect(50,20,10,-10,color(0,1));
5410
- rect(50,20,6.5,-10,color(0,2));
5411
- rect(50,20,3.5,-10,color(0,3));
5412
- rect(50,20,10,-10);
5413
- circle(55,2,11.4,.5,PI-.5,color(3,3));
5414
- circle(55,2,11.4,.5,PI/2,color(3,2),1);
5415
- circle(55,2,11.4,.5,PI-.5);
5416
- rect(45,7,20,-7,color(0,2));
5417
- rect(45,0,20,3,color(0,3));
5418
- rect(45,0,20,7);
5484
+ rect(50,20,10,-8,color(0,1))
5485
+ rect(50,20,6.5,-8,color(0,2))
5486
+ rect(50,20,3.5,-8,color(0,3))
5487
+ rect(50,20,10,-8)
5488
+ circle(55,2,11.4,.5,PI-.5,color(3,3))
5489
+ circle(55,2,11.4,.5,PI/2,color(3,2),1)
5490
+ circle(55,2,11.4,.5,PI-.5)
5491
+ rect(45,7,20,-7,color(0,2))
5492
+ rect(45,-1,20,4,color(0,3))
5493
+ rect(45,-1,20,8)
5419
5494
 
5420
5495
  // engine
5421
5496
  for (let i=5; i--;)