littlejsengine 1.9.2 → 1.9.4

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 (37) hide show
  1. package/README.md +4 -2
  2. package/dist/littlejs.d.ts +31 -26
  3. package/dist/littlejs.esm.js +145 -122
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +145 -122
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +140 -121
  8. package/examples/logo.png +0 -0
  9. package/examples/module/game.js +1 -1
  10. package/examples/platformer/data/{gameTileData.tmx → gameLevelData.tmx} +3 -3
  11. package/examples/platformer/game.js +31 -10
  12. package/examples/platformer/gameCharacter.js +7 -6
  13. package/examples/platformer/gameEffects.js +77 -68
  14. package/examples/platformer/gameLevel.js +33 -17
  15. package/examples/platformer/{gameTileData.js → gameLevelData.js} +3 -3
  16. package/examples/platformer/gameObjects.js +14 -14
  17. package/examples/platformer/index.html +1 -1
  18. package/examples/platformer/tiles.png +0 -0
  19. package/examples/screenshot.jpg +0 -0
  20. package/examples/stress/index.html +1 -1
  21. package/examples/typescript/game.js +1 -1
  22. package/examples/typescript/game.ts +1 -1
  23. package/package.json +1 -1
  24. package/reference.md +409 -0
  25. package/src/engine.js +47 -42
  26. package/src/engineAudio.js +19 -10
  27. package/src/engineDebug.js +5 -1
  28. package/src/engineDraw.js +19 -23
  29. package/src/engineInput.js +31 -17
  30. package/src/engineMedals.js +1 -1
  31. package/src/engineObject.js +2 -2
  32. package/src/engineParticles.js +11 -10
  33. package/src/engineSettings.js +3 -3
  34. package/src/engineTileLayer.js +2 -2
  35. package/src/engineUtilities.js +4 -4
  36. package/src/engineWebGL.js +1 -7
  37. /package/examples/platformer/data/{gameTileData.tsx → gameLevelData.tsx} +0 -0
package/src/engineDraw.js CHANGED
@@ -85,13 +85,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
85
85
  if (typeof pos === 'number')
86
86
  {
87
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();
88
+ ASSERT(textureInfo, 'Texture not loaded');
89
+ const cols = textureInfo.size.x / size.x |0;
90
+ pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
95
91
  }
96
92
 
97
93
  // return a tile info object
@@ -118,13 +114,23 @@ class TileInfo
118
114
  this.textureIndex = textureIndex;
119
115
  }
120
116
 
121
- /** Returns an offset copy of this tile, useful for animation
117
+ /** Returns a copy of this tile offset by a vector
122
118
  * @param {Vector2} offset - Offset to apply in pixels
123
119
  * @return {TileInfo}
124
120
  */
125
121
  offset(offset)
126
122
  { return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
127
123
 
124
+ /** Returns a copy of this tile offset by a number of animation frames
125
+ * @param {Number} frame - Offset to apply in animation frames
126
+ * @return {TileInfo}
127
+ */
128
+ frame(frame)
129
+ {
130
+ ASSERT(typeof frame == 'number');
131
+ return this.offset(vec2(frame*this.size.x, 0));
132
+ }
133
+
128
134
  /** Returns the texture info for this tile
129
135
  * @return {TextureInfo}
130
136
  */
@@ -180,6 +186,11 @@ function worldToScreen(worldPos)
180
186
  );
181
187
  }
182
188
 
189
+ /** Get the camera's visible area in world space
190
+ * @return {Vector2}
191
+ * @memberof Draw */
192
+ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
193
+
183
194
  /** Draw textured tile centered in world space, with color applied if using WebGL
184
195
  * @param {Vector2} pos - Center of the tile in world space
185
196
  * @param {Vector2} [size=(1,1)] - Size of the tile in world space
@@ -270,21 +281,6 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
270
281
  drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
271
282
  }
272
283
 
273
- /** Draw colored polygon using passed in points
274
- * @param {Array} points - Array of Vector2 points
275
- * @param {Color} [color=(1,1,1,1)]
276
- * @param {Boolean} [screenSpace=false]
277
- * @param {CanvasRenderingContext2D} [context=mainContext]
278
- * @memberof Draw */
279
- function drawPoly(points, color=new Color, screenSpace, context=mainContext)
280
- {
281
- context.fillStyle = color.toString();
282
- context.beginPath();
283
- for (const point of screenSpace ? points : points.map(worldToScreen))
284
- context.lineTo(point.x, point.y);
285
- context.fill();
286
- }
287
-
288
284
  /** Draw colored line between two points
289
285
  * @param {Vector2} posA
290
286
  * @param {Vector2} posB
@@ -211,9 +211,21 @@ function mouseToScreen(mousePos)
211
211
  ///////////////////////////////////////////////////////////////////////////////
212
212
  // Gamepad input
213
213
 
214
+ // gamepad internal variables
214
215
  const stickData = [];
216
+
217
+ // gamepads are updated by engine every frame automatically
215
218
  function gamepadsUpdate()
216
219
  {
220
+ const applyDeadZones = (v)=>
221
+ {
222
+ const min=.3, max=.8;
223
+ const deadZone = (v)=>
224
+ v > min ? percent( v, min, max) :
225
+ v < -min ? -percent(-v, min, max) : 0;
226
+ return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
227
+ }
228
+
217
229
  // update touch gamepad if enabled
218
230
  if (touchGamepadEnable && isTouchDevice)
219
231
  {
@@ -225,7 +237,16 @@ function gamepadsUpdate()
225
237
  {
226
238
  // read virtual analog stick
227
239
  const sticks = stickData[0] || (stickData[0] = []);
228
- sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
240
+ sticks[0] = vec2();
241
+ if (touchGamepadAnalog)
242
+ sticks[0] = applyDeadZones(touchGamepadStick);
243
+ else if (touchGamepadStick.lengthSquared() > .3)
244
+ {
245
+ // convert to 8 way dpad
246
+ sticks[0].x = Math.round(touchGamepadStick.x);
247
+ sticks[0].y = -Math.round(touchGamepadStick.y);
248
+ sticks[0] = sticks[0].clampLength();
249
+ }
229
250
 
230
251
  // read virtual gamepad buttons
231
252
  const data = inputData[1] || (inputData[1] = []);
@@ -237,7 +258,12 @@ function gamepadsUpdate()
237
258
  }
238
259
  }
239
260
 
240
- if (!gamepadsEnable || !navigator || !navigator.getGamepads || !document.hasFocus() && !debug)
261
+ // return if gamepads are disabled or not supported
262
+ if (!gamepadsEnable || !navigator || !navigator.getGamepads)
263
+ return;
264
+
265
+ // only poll gamepads when focused or in debug mode
266
+ if (!debug && !document.hasFocus())
241
267
  return;
242
268
 
243
269
  // poll gamepads
@@ -251,14 +277,9 @@ function gamepadsUpdate()
251
277
 
252
278
  if (gamepad)
253
279
  {
254
- // read clamp dead zone of analog sticks
255
- const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
256
- v > deadZone ? percent( v, deadZone, deadZoneMax) :
257
- v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
258
-
259
280
  // read analog sticks
260
281
  for (let j = 0; j < gamepad.axes.length-1; j+=2)
261
- sticks[j>>1] = vec2(applyDeadZone(gamepad.axes[j]), applyDeadZone(-gamepad.axes[j+1])).clampLength();
282
+ sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
262
283
 
263
284
  // read buttons
264
285
  for (let j = gamepad.buttons.length; j--;)
@@ -357,7 +378,7 @@ function createTouchGamepad()
357
378
  touchGamepadStick = vec2();
358
379
 
359
380
  const touchHandler = ontouchstart;
360
- ontouchstart = ontouchmove = ontouchend = (e)=>
381
+ ontouchstart = ontouchmove = ontouchend = (e)=>
361
382
  {
362
383
  // clear touch gamepad input
363
384
  touchGamepadStick = vec2();
@@ -387,14 +408,7 @@ function createTouchGamepad()
387
408
  if (touchPos.distance(stickCenter) < touchGamepadSize)
388
409
  {
389
410
  // virtual analog stick
390
- if (touchGamepadAnalog)
391
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
392
- else
393
- {
394
- // 8 way dpad
395
- const angle = touchPos.subtract(stickCenter).angle();
396
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
397
- }
411
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
398
412
  }
399
413
  else if (touchPos.distance(buttonCenter) < touchGamepadSize)
400
414
  {
@@ -270,7 +270,7 @@ class Newgrounds
270
270
  }
271
271
 
272
272
  // build the input object
273
- const input =
273
+ const input =
274
274
  {
275
275
  'app_id': this.app_id,
276
276
  'session_id': this.session_id,
@@ -10,7 +10,7 @@
10
10
  * - Automatically adds self to object list
11
11
  * - Will be updated and rendered each frame
12
12
  * - Renders as a sprite from a tilesheet by default
13
- * - Can have color and addtive color applied
13
+ * - Can have color and additive color applied
14
14
  * - 2D Physics and collision system
15
15
  * - Sorted by renderOrder
16
16
  * - Objects can have children attached
@@ -289,7 +289,7 @@ class EngineObject
289
289
  }
290
290
 
291
291
  /** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
292
- destroy()
292
+ destroy()
293
293
  {
294
294
  if (this.destroyed)
295
295
  return;
@@ -202,16 +202,17 @@ class ParticleEmitter extends EngineObject
202
202
 
203
203
  // build particle
204
204
  const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
205
- particle.velocity = vec2().setAngle(velocityAngle, speed);
206
- particle.fadeRate = this.fadeRate;
207
- particle.damping = this.damping;
208
- particle.angleDamping = this.angleDamping;
209
- particle.elasticity = this.elasticity;
210
- particle.friction = this.friction;
211
- particle.gravityScale = this.gravityScale;
212
- particle.collideTiles = this.collideTiles;
213
- particle.renderOrder = this.renderOrder;
214
- particle.mirror = !!randInt(2);
205
+ particle.velocity = vec2().setAngle(velocityAngle, speed);
206
+ particle.angleVelocity = angleSpeed;
207
+ particle.fadeRate = this.fadeRate;
208
+ particle.damping = this.damping;
209
+ particle.angleDamping = this.angleDamping;
210
+ particle.elasticity = this.elasticity;
211
+ particle.friction = this.friction;
212
+ particle.gravityScale = this.gravityScale;
213
+ particle.collideTiles = this.collideTiles;
214
+ particle.renderOrder = this.renderOrder;
215
+ particle.mirror = !!randInt(2);
215
216
 
216
217
  // call particle create callaback
217
218
  this.particleCreateCallback && this.particleCreateCallback(particle);
@@ -83,7 +83,7 @@ let tileSizeDefault = vec2(16);
83
83
  * @type {Number}
84
84
  * @default
85
85
  * @memberof Settings */
86
- let tileFixBleedScale = .3;
86
+ let tileFixBleedScale = .1;
87
87
 
88
88
  ///////////////////////////////////////////////////////////////////////////////
89
89
  // Object settings
@@ -94,7 +94,7 @@ let tileFixBleedScale = .3;
94
94
  * @memberof Settings */
95
95
  let enablePhysicsSolver = true;
96
96
 
97
- /** Default object mass for collison calcuations (how heavy objects are)
97
+ /** Default object mass for collision calcuations (how heavy objects are)
98
98
  * @type {Number}
99
99
  * @default
100
100
  * @memberof Settings */
@@ -177,7 +177,7 @@ let touchGamepadEnable = false;
177
177
  * @memberof Settings */
178
178
  let touchGamepadAnalog = true;
179
179
 
180
- /** Size of virutal gamepad for touch devices in pixels
180
+ /** Size of virtual gamepad for touch devices in pixels
181
181
  * @type {Number}
182
182
  * @default
183
183
  * @memberof Settings */
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * LittleJS Tile Layer System
3
3
  * - Caches arrays of tiles to off screen canvas for fast rendering
4
- * - Unlimted numbers of layers, allocates canvases as needed
4
+ * - Unlimited numbers of layers, allocates canvases as needed
5
5
  * - Interfaces with EngineObject for collision
6
6
  * - Collision layer is separate from visible layers
7
7
  * - It is recommended to have a visible layer that matches the collision
@@ -53,7 +53,7 @@ function getTileCollisionData(pos)
53
53
 
54
54
  /** Check if collision with another object should occur
55
55
  * @param {Vector2} pos
56
- * @param {Vector2} [size=(1,1)]
56
+ * @param {Vector2} [size=(0,0)]
57
57
  * @param {EngineObject} [object]
58
58
  * @return {Boolean}
59
59
  * @memberof TileCollision */
@@ -36,7 +36,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
36
36
  * @memberof Utilities */
37
37
  function max(valueA, valueB) { return Math.max(valueA, valueB); }
38
38
 
39
- /** Returns the sign of value passed in (also returns 1 if 0)
39
+ /** Returns the sign of value passed in
40
40
  * @param {Number} value
41
41
  * @return {Number}
42
42
  * @memberof Utilities */
@@ -83,7 +83,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
83
83
  function distanceWrap(valueA, valueB, wrapSize=1)
84
84
  { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
85
85
 
86
- /** Linearly interpolates between values passed in with wrappping
86
+ /** Linearly interpolates between values passed in with wrapping
87
87
  * @param {Number} percent
88
88
  * @param {Number} valueA
89
89
  * @param {Number} valueB
@@ -100,7 +100,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
100
100
  * @memberof Utilities */
101
101
  function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
102
102
 
103
- /** Linearly interpolates between the angles passed in with wrappping
103
+ /** Linearly interpolates between the angles passed in with wrapping
104
104
  * @param {Number} percent
105
105
  * @param {Number} angleA
106
106
  * @param {Number} angleB
@@ -630,7 +630,7 @@ class Color
630
630
 
631
631
  /** Returns this color expressed in hsla format
632
632
  * @return {Array} */
633
- getHSLA()
633
+ HSLA()
634
634
  {
635
635
  const r = clamp(this.r);
636
636
  const g = clamp(this.g);
@@ -193,8 +193,6 @@ function glCreateTexture(image)
193
193
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
194
194
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
195
195
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
196
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
197
- glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
198
196
 
199
197
  return texture;
200
198
  }
@@ -272,7 +270,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
272
270
  ///////////////////////////////////////////////////////////////////////////////
273
271
  // post processing - can be enabled to pass other canvases through a final shader
274
272
 
275
- let glPostShader, glPostArrayBuffer, glPostTexture, glPostIncludeOverlay;
273
+ let glPostShader, glPostTexture, glPostIncludeOverlay;
276
274
 
277
275
  /** Set up a post processing shader
278
276
  * @param {String} shaderCode
@@ -308,7 +306,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
308
306
  );
309
307
 
310
308
  // create buffer and texture
311
- glPostArrayBuffer = glContext.createBuffer();
312
309
  glPostTexture = glCreateTexture(undefined);
313
310
  glPostIncludeOverlay = includeOverlay;
314
311
 
@@ -380,10 +377,7 @@ gl_NEAREST = 9728,
380
377
  gl_LINEAR = 9729,
381
378
  gl_TEXTURE_MAG_FILTER = 10240,
382
379
  gl_TEXTURE_MIN_FILTER = 10241,
383
- gl_TEXTURE_WRAP_S = 10242,
384
- gl_TEXTURE_WRAP_T = 10243,
385
380
  gl_COLOR_BUFFER_BIT = 16384,
386
- gl_CLAMP_TO_EDGE = 33071,
387
381
  gl_TEXTURE0 = 33984,
388
382
  gl_ARRAY_BUFFER = 34962,
389
383
  gl_STATIC_DRAW = 35044,