littlejsengine 1.8.6 → 1.8.8

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/README.md CHANGED
@@ -1,4 +1,8 @@
1
- # ![LittleJS Logo](examples/favicon.png) LittleJS - The Tiny JavaScript Game Engine That Can
1
+ # ![LittleJS Logo](examples/favicon.png) LittleJS - The Tiny Fast JavaScript Game Engine
2
+
3
+ <div align="center">
4
+
5
+ ![LittleJS Screenshot](examples/logo.png)
2
6
 
3
7
  [![NPM Package][npm]][npm-url]
4
8
  [![Build Size][build-size]][build-size-url]
@@ -6,9 +10,11 @@
6
10
  [![DeepScan][deepscan]][deepscan-url]
7
11
  [![Discord][discord]][discord-url]
8
12
 
13
+ </div>
14
+
9
15
  ## 🚂 All aboard!
10
16
 
11
- LittleJS is a lightweight open source HTML5 game engine for modern web development.
17
+ LittleJS is a fast lightweight open source HTML5 game engine for modern web development.
12
18
  It's small footprint is packed with a comprehensive feature set including hybrid rendering, physics, particles, sound effects, music, and input handling.
13
19
  The code is very clean and well documented with some fun examples to get you started. Choo-Choo! 🚂
14
20
 
@@ -31,7 +37,7 @@ LittleJS is a small but powerful game engine with many features and no dependenc
31
37
  ### ✨ Graphics
32
38
 
33
39
  - Super fast sprite and tile map rendering engine with WebGL
34
- - Update and render over 10,000 objects at a solid 60fps
40
+ - Update and render well over 10,000 sprites at a solid 60fps
35
41
  - Apply [Shadertoy](https://www.shadertoy.com) compatible shaders for post-processing effects
36
42
  - Robust particle effect system and [effect design tool](https://killedbyapixel.github.io/LittleJS/examples/particles/)
37
43
 
@@ -204,6 +204,11 @@ declare module "littlejs.esm" {
204
204
  * @default
205
205
  * @memberof Settings */
206
206
  export let fontDefault: string;
207
+ /** Enable to show the LittleJS splash screen be shown on startup
208
+ * @type {Boolean}
209
+ * @default
210
+ * @memberof Settings */
211
+ export let showSplashScreen: boolean;
207
212
  /** Default size of tiles in pixels
208
213
  * @type {Vector2}
209
214
  * @default Vector2(16,16)
@@ -375,6 +380,10 @@ declare module "littlejs.esm" {
375
380
  * @param {String} font
376
381
  * @memberof Settings */
377
382
  export function setFontDefault(font: string): void;
383
+ /** Set if the LittleJS splash screen be shown on startup
384
+ * @param {Boolean} show
385
+ * @memberof Settings */
386
+ export function setShowSplashScreen(show: boolean): void;
378
387
  /** Set if webgl rendering is enabled
379
388
  * @param {Boolean} enable
380
389
  * @memberof Settings */
@@ -1008,11 +1008,10 @@ class Color
1008
1008
  * @return {Number} */
1009
1009
  rgbaInt()
1010
1010
  {
1011
- const toByte = (c)=> clamp(c)*255|0;
1012
- const r = toByte(this.r);
1013
- const g = toByte(this.g)<<8;
1014
- const b = toByte(this.b)<<16;
1015
- const a = toByte(this.a)<<24;
1011
+ const r = clamp(this.r)*255|0;
1012
+ const g = clamp(this.g)*255<<8;
1013
+ const b = clamp(this.b)*255<<16;
1014
+ const a = clamp(this.a)*255<<24;
1016
1015
  return r + g + b + a;
1017
1016
  }
1018
1017
  }
@@ -1120,6 +1119,12 @@ let canvasPixelated = 1;
1120
1119
  * @memberof Settings */
1121
1120
  let fontDefault = 'arial';
1122
1121
 
1122
+ /** Enable to show the LittleJS splash screen be shown on startup
1123
+ * @type {Boolean}
1124
+ * @default
1125
+ * @memberof Settings */
1126
+ let showSplashScreen = 0;
1127
+
1123
1128
  ///////////////////////////////////////////////////////////////////////////////
1124
1129
  // WebGL settings
1125
1130
 
@@ -1353,6 +1358,11 @@ function setCanvasPixelated(pixelated) { canvasPixelated = pixelated; }
1353
1358
  * @memberof Settings */
1354
1359
  function setFontDefault(font) { fontDefault = font; }
1355
1360
 
1361
+ /** Set if the LittleJS splash screen be shown on startup
1362
+ * @param {Boolean} show
1363
+ * @memberof Settings */
1364
+ function setShowSplashScreen(show) { showSplashScreen = show; }
1365
+
1356
1366
  /** Set if webgl rendering is enabled
1357
1367
  * @param {Boolean} enable
1358
1368
  * @memberof Settings */
@@ -1944,7 +1954,7 @@ let mainCanvasSize = vec2();
1944
1954
  * @memberof Draw */
1945
1955
  let textureInfos = [];
1946
1956
 
1947
- // Engine internal variables not exposed to documentation
1957
+ // Keep track of how many draw calls there were each frame for debugging
1948
1958
  let drawCount;
1949
1959
 
1950
1960
  ///////////////////////////////////////////////////////////////////////////////
@@ -4392,7 +4402,7 @@ let glCanvas;
4392
4402
  let glContext;
4393
4403
 
4394
4404
  // WebGL internal variables not exposed to documentation
4395
- let glActiveTexture, glShader, glArrayBuffer, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
4405
+ let glActiveTexture, glShader, glArrayBuffer, glVertexData, glPositionData, glColorData, glBatchCount, glBatchAdditive, glAdditive;
4396
4406
 
4397
4407
  ///////////////////////////////////////////////////////////////////////////////
4398
4408
 
@@ -4429,10 +4439,10 @@ function glInit()
4429
4439
  );
4430
4440
 
4431
4441
  // init buffers
4432
- const vertexData = new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);
4442
+ glVertexData = new ArrayBuffer(gl_VERTEX_BUFFER_SIZE);
4443
+ glPositionData = new Float32Array(glVertexData);
4444
+ glColorData = new Uint32Array(glVertexData);
4433
4445
  glArrayBuffer = glContext.createBuffer();
4434
- glPositionData = new Float32Array(vertexData);
4435
- glColorData = new Uint32Array(vertexData);
4436
4446
  glBatchCount = 0;
4437
4447
  }
4438
4448
 
@@ -4460,7 +4470,7 @@ function glPreRender()
4460
4470
  glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
4461
4471
  offset += size*typeSize;
4462
4472
  }
4463
- initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & texture coords
4473
+ initVertexAttribArray('p', gl_FLOAT, 4, 4); // position & texture
4464
4474
  initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
4465
4475
  initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
4466
4476
 
@@ -4548,6 +4558,7 @@ function glCreateTexture(image)
4548
4558
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
4549
4559
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
4550
4560
  glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
4561
+
4551
4562
  return texture;
4552
4563
  }
4553
4564
 
@@ -4562,8 +4573,7 @@ function glFlush()
4562
4573
  glContext.enable(gl_BLEND);
4563
4574
 
4564
4575
  // draw all the sprites in the batch and reset the buffer
4565
- glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
4566
- glPositionData.subarray(0, glBatchCount * gl_INDICIES_PER_VERT));
4576
+ glContext.bufferSubData(gl_ARRAY_BUFFER, 0, glVertexData);
4567
4577
  glContext.drawArrays(gl_TRIANGLE_STRIP, 0, glBatchCount);
4568
4578
  glBatchCount = 0;
4569
4579
  glBatchAdditive = glAdditive;
@@ -4618,11 +4628,11 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
4618
4628
  // setup 2 triangle strip quad
4619
4629
  for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
4620
4630
  {
4621
- let j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
4622
- glPositionData[offset++] = positionData[j++];
4623
- glPositionData[offset++] = positionData[j++];
4624
- glPositionData[offset++] = positionData[j++];
4625
- glPositionData[offset++] = positionData[j++];
4631
+ const j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
4632
+ glPositionData[offset++] = positionData[j+0];
4633
+ glPositionData[offset++] = positionData[j+1];
4634
+ glPositionData[offset++] = positionData[j+2];
4635
+ glPositionData[offset++] = positionData[j+3];
4626
4636
  glColorData[offset++] = rgba;
4627
4637
  glColorData[offset++] = rgbaAdditive;
4628
4638
  }
@@ -4823,7 +4833,7 @@ const engineName = 'LittleJS';
4823
4833
  * @type {String}
4824
4834
  * @default
4825
4835
  * @memberof Engine */
4826
- const engineVersion = '1.8.6';
4836
+ const engineVersion = '1.8.8';
4827
4837
 
4828
4838
  /** Frames per second to update objects
4829
4839
  * @type {Number}
@@ -5006,7 +5016,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5006
5016
  }
5007
5017
 
5008
5018
  // setup html
5009
- const styleBody =
5019
+ const styleBody =
5010
5020
  'margin:0;overflow:hidden;' + // fill the window
5011
5021
  'background:#000;' + // set background color
5012
5022
  'touch-action:none;' + // prevent mobile pinch to resize
@@ -5027,14 +5037,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5027
5037
 
5028
5038
  // set canvas style
5029
5039
  const styleCanvas =
5030
- 'position:absolute;' + // position
5031
- 'top:50%;left:50%;transform:translate(-50%,-50%);' + // center
5032
- (canvasPixelated?'image-rendering:pixelated':''); // pixelated rendering
5040
+ 'position:absolute;' + // position
5041
+ 'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
5033
5042
  (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
5034
5043
 
5035
- // load all of the images
5036
- Promise.all(imageSources.map((src, textureIndex)=>
5037
- new Promise((resolve, reject)=>
5044
+ // create promises for loading images
5045
+ const promises = imageSources.map((src, textureIndex)=>
5046
+ new Promise(resolve =>
5038
5047
  {
5039
5048
  const image = new Image;
5040
5049
  image.onerror = image.onload = ()=>
@@ -5044,7 +5053,24 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
5044
5053
  }
5045
5054
  image.src = src;
5046
5055
  })
5047
- )).then(()=>
5056
+ )
5057
+
5058
+ // draw splash screen
5059
+ showSplashScreen && promises.push(new Promise(resolve =>
5060
+ {
5061
+ let t = 0;
5062
+ console.log(`LittleJS Engine v${engineVersion}`);
5063
+ updateSplash();
5064
+ function updateSplash()
5065
+ {
5066
+ clearInput();
5067
+ drawEngineSplashScreen(t+=.01);
5068
+ t>1 ? resolve() : setTimeout(updateSplash,16);
5069
+ }
5070
+ }));
5071
+
5072
+ // load all of the images
5073
+ Promise.all(promises).then(()=>
5048
5074
  {
5049
5075
  // start the engine
5050
5076
  gameInit();
@@ -5122,6 +5148,167 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
5122
5148
  for (const o of objects)
5123
5149
  pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
5124
5150
  }
5151
+ }
5152
+
5153
+ ///////////////////////////////////////////////////////////////////////////////
5154
+ // LittleJS splash screen and logo
5155
+
5156
+ function drawEngineSplashScreen(t)
5157
+ {
5158
+ // background
5159
+ const grayscale = 0;
5160
+ const x = mainContext;
5161
+ const w = mainCanvas.width = innerWidth;
5162
+ const h = mainCanvas.height = innerHeight;
5163
+ const p3 = percent(t, 1, .8);
5164
+ const p4 = percent(t, 0, .5);
5165
+ const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,Math.hypot(w,h)*.7);
5166
+ g.addColorStop(0,hsl(0,0,lerp(p4,0,p3/2),p3));
5167
+ g.addColorStop(1,hsl(0,0,0,p3));
5168
+ x.save();
5169
+ x.fillStyle = g;
5170
+ x.fillRect(0,0,w,h);
5171
+
5172
+ // logo - fade in and out
5173
+ const rect = (X, Y, W, H, C)=>
5174
+ {
5175
+ x.beginPath();
5176
+ x.rect(X,Y,W,C?H*p:H);
5177
+ x.fillStyle = C;
5178
+ C ? x.fill() : x.stroke();
5179
+ };
5180
+ const line = (X, Y, Z, W)=>
5181
+ {
5182
+ x.beginPath();
5183
+ x.lineTo(X,Y);
5184
+ x.lineTo(Z,W);
5185
+ x.stroke();
5186
+ };
5187
+ const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
5188
+ {
5189
+ const D = (A+B)/2, E = p*(B-A)/2;
5190
+ x.beginPath();
5191
+ F && x.lineTo(X,Y);
5192
+ x.arc(X,Y,R,D-E,D+E);
5193
+ x.fillStyle = C;
5194
+ C ? x.fill() : x.stroke();
5195
+ };
5196
+ const color = (c=0, l=0) =>
5197
+ hsl([.98,.3,.57,.14][c%4]-10,grayscale?0:.8,[0,.3,.5,.8,.9][l]);
5198
+ const alpha = wave(1,1,t);
5199
+ const p = percent(alpha, .1, .5);
5200
+
5201
+ // setup
5202
+ x.translate(w/2,h/2);
5203
+ const size = min(6, min(w,h)/99); // fit to screen
5204
+ x.scale(size,size);
5205
+ x.translate(-40,-35);
5206
+ x.lineJoin = x.lineCap = 'round';
5207
+ x.lineWidth = 1+p;
5208
+
5209
+ // drawing effect
5210
+ const p2 = percent(alpha,.1,1);
5211
+ x.setLineDash([99*p2,99]);
5212
+
5213
+ // cab top
5214
+ rect(7,17,18,-8,color(2,2));
5215
+ rect(7,9,18,4,color(2,3));
5216
+ rect(25,9,8,8,color(2,1));
5217
+ rect(25,9,-18,8);
5218
+ rect(25,9,8,8);
5219
+
5220
+ // cab
5221
+ rect(25,17,7,22,color());
5222
+ rect(11,40,14,-23,color(1,1));
5223
+ rect(11,17,14,17,color(1,2));
5224
+ rect(11,17,14,9,color(1,3));
5225
+ rect(15,31,6,-9,color(2,2));
5226
+ circle(15,23,5,0,PI/2,color(2,4),1);
5227
+ rect(25,17,-14,23);
5228
+ rect(21,22,-6,9);
5229
+
5230
+ // little stack
5231
+ rect(37,14,9,6,color(3,2));
5232
+ rect(37,14,4,6,color(3,3));
5233
+ rect(37,14,9,6);
5234
+
5235
+ // big stack
5236
+ rect(50,20,10,-10,color(0,1));
5237
+ rect(50,20,6,-10,color(0,2));
5238
+ rect(50,20,3,-10,color(0,3));
5239
+ rect(50,10,10,10);
5240
+ circle(55,2,11,.5,PI-.5,color(3,3));
5241
+ circle(55,2,11,.5,PI/2,color(3,2),1);
5242
+ circle(55,2,11,.5,PI-.5);
5243
+ rect(45,7,20,-7,color(0,2));
5244
+ rect(45,0,20,3,color(0,3));
5245
+ rect(45,0,20,7);
5246
+
5247
+ // engine
5248
+ for (let i=5; i--;)
5249
+ {
5250
+ // stagger radius to fix slight seam
5251
+ circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
5252
+ circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
5253
+ circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
5254
+ }
5255
+
5256
+ // engine outline
5257
+ circle(36,30,10,PI/2,PI*3/2);
5258
+ circle(47,30,10,PI/2,PI*3/2);
5259
+ circle(60,30,10);
5260
+ line(36,20,60,20);
5261
+
5262
+ // engine front light
5263
+ circle(60,30,4,PI,3*PI,color(3,2));
5264
+ circle(60,30,4,PI,2*PI,color(3,3));
5265
+ circle(60,30,4,PI,3*PI);
5266
+
5267
+ // front brush
5268
+ for (let i=6; i--;)
5269
+ {
5270
+ x.beginPath();
5271
+ x.lineTo(53,54);
5272
+ x.lineTo(53,40);
5273
+ x.lineTo(53+(1+i*2.9)*p,40);
5274
+ x.lineTo(53+(4+i*3.5)*p,54);
5275
+ x.fillStyle = color(0,i%2+2);
5276
+ x.fill() || i%2 && x.stroke();
5277
+ }
5278
+
5279
+ // wheels
5280
+ rect(5,40,9,6,color());
5281
+ rect(15,54,38,-14,color())
5282
+ for (let i=3; i--;)
5283
+ for (let j=2; j--;)
5284
+ {
5285
+ circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
5286
+ x.stroke();
5287
+ circle(15*i+15,47,j?7:1,0,PI,color(i,2));
5288
+ x.stroke();
5289
+ }
5290
+ line(6,40,68,40) // center
5291
+ line(77,54,4,54) // bottom
5292
+
5293
+ // text
5294
+ const s = engineName;
5295
+ x.font = '900 16px arial';
5296
+ x.textAlign = 'center';
5297
+ x.textBaseline = 'top';
5298
+ x.lineWidth = 1+p*3
5299
+ let w2 = 0;
5300
+ for (let i=0; i<s.length; ++i)
5301
+ w2 += x.measureText(s[i]).width;
5302
+ for (let j=2; j--;)
5303
+ for (let i=0, X=41-w2/2; i<s.length; ++i)
5304
+ {
5305
+ x.fillStyle = color(i,grayscale?3:2);
5306
+ const w = x.measureText(s[i]).width;
5307
+ x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
5308
+ X += w;
5309
+ }
5310
+
5311
+ x.restore();
5125
5312
  }
5126
5313
 
5127
5314
  /**
@@ -5169,6 +5356,7 @@ export {
5169
5356
  canvasFixedSize,
5170
5357
  canvasPixelated,
5171
5358
  fontDefault,
5359
+ showSplashScreen,
5172
5360
  tileSizeDefault,
5173
5361
  tileFixBleedScale,
5174
5362
  enablePhysicsSolver,
@@ -5206,6 +5394,7 @@ export {
5206
5394
  setCanvasFixedSize,
5207
5395
  setCanvasPixelated,
5208
5396
  setFontDefault,
5397
+ setShowSplashScreen,
5209
5398
  setGlEnable,
5210
5399
  setGlOverlay,
5211
5400
  setTileSizeDefault,