littlejsengine 1.9.11 → 1.10.2

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 (53) hide show
  1. package/README.md +4 -0
  2. package/dist/littlejs.d.ts +80 -9
  3. package/dist/littlejs.esm.js +193 -71
  4. package/dist/littlejs.esm.min.js +1 -1
  5. package/dist/littlejs.js +175 -69
  6. package/dist/littlejs.min.js +1 -1
  7. package/dist/littlejs.release.js +174 -68
  8. package/examples/box2d/game.js +1 -1
  9. package/examples/box2d/index.html +7 -6
  10. package/examples/breakout/game.js +1 -1
  11. package/examples/breakout/index.html +5 -4
  12. package/examples/breakoutTutorial/index.html +4 -3
  13. package/examples/electron/game.js +1 -1
  14. package/examples/electron/index.html +4 -3
  15. package/examples/empty/game.js +1 -1
  16. package/examples/empty/index.html +2 -1
  17. package/examples/htmlMenu/game.js +67 -0
  18. package/examples/htmlMenu/index.html +56 -0
  19. package/examples/htmlMenu/tiles.png +0 -0
  20. package/examples/index.html +33 -0
  21. package/examples/js13k/game.js +1 -1
  22. package/examples/js13k/index.html +17 -14
  23. package/examples/module/game.js +1 -1
  24. package/examples/module/index.html +3 -2
  25. package/examples/particles/index.html +4 -3
  26. package/examples/platformer/game.js +1 -1
  27. package/examples/platformer/gameLevel.js +2 -2
  28. package/examples/platformer/gameObjects.js +0 -1
  29. package/examples/platformer/index.html +10 -9
  30. package/examples/puzzle/game.js +1 -1
  31. package/examples/puzzle/index.html +4 -3
  32. package/examples/starter/game.js +4 -4
  33. package/examples/starter/index.html +15 -14
  34. package/examples/stress/index.html +3 -2
  35. package/examples/typescript/game.js +1 -1
  36. package/examples/typescript/index.html +3 -2
  37. package/examples/uiSystem/game.js +134 -0
  38. package/examples/uiSystem/index.html +25 -0
  39. package/examples/uiSystem/tiles.png +0 -0
  40. package/jsconfig.json +11 -0
  41. package/package.json +1 -1
  42. package/plugins/box2d.js +2 -1
  43. package/plugins/postProcess.js +6 -7
  44. package/plugins/uiSystem.js +243 -0
  45. package/src/engine.js +48 -20
  46. package/src/engineAudio.js +10 -13
  47. package/src/engineDebug.js +1 -1
  48. package/src/engineDraw.js +101 -27
  49. package/src/engineExport.js +18 -2
  50. package/src/engineInput.js +2 -2
  51. package/src/engineSettings.js +1 -1
  52. package/src/engineUtilities.js +1 -1
  53. package/src/engineWebGL.js +11 -4
@@ -773,7 +773,7 @@ class Color
773
773
  * @return {String} */
774
774
  toString(useAlpha = true)
775
775
  {
776
- const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
776
+ const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
777
777
  return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
778
778
  }
779
779
 
@@ -1003,7 +1003,7 @@ let tileSizeDefault = vec2(16);
1003
1003
  * @type {Number}
1004
1004
  * @default
1005
1005
  * @memberof Settings */
1006
- let tileFixBleedScale = .5;
1006
+ let tileFixBleedScale = 0;
1007
1007
 
1008
1008
  ///////////////////////////////////////////////////////////////////////////////
1009
1009
  // Object settings
@@ -1905,12 +1905,13 @@ let drawCount;
1905
1905
  ///////////////////////////////////////////////////////////////////////////////
1906
1906
 
1907
1907
  /**
1908
- * Create a tile info object
1908
+ * Create a tile info object using a grid based system
1909
1909
  * - This can take vecs or floats for easier use and conversion
1910
1910
  * - If an index is passed in, the tile size and index will determine the position
1911
- * @param {(Number|Vector2)} [pos=(0,0)] - Top left corner of tile in pixels or index
1911
+ * @param {(Number|Vector2)} [pos=0] - Index of tile in sheet
1912
1912
  * @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
1913
1913
  * @param {Number} [textureIndex] - Texture index to use
1914
+ * @param {Number} [padding] - How many pixels padding around tiles
1914
1915
  * @return {TileInfo}
1915
1916
  * @example
1916
1917
  * tile(2) // a tile at index 2 using the default tile size of 16
@@ -1919,7 +1920,7 @@ let drawCount;
1919
1920
  * tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
1920
1921
  * @memberof Draw
1921
1922
  */
1922
- function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
1923
+ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
1923
1924
  {
1924
1925
  if (headlessMode)
1925
1926
  return new TileInfo;
@@ -1931,17 +1932,17 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
1931
1932
  size = vec2(size);
1932
1933
  }
1933
1934
 
1934
- // if pos is a number, use it as a tile index
1935
+ // use pos as a tile index
1936
+ const textureInfo = textureInfos[textureIndex];
1937
+ ASSERT(textureInfo, 'Texture not loaded');
1938
+ const sizePadded = size.add(vec2(padding*2));
1939
+ const cols = textureInfo.size.x / sizePadded.x |0;
1935
1940
  if (typeof pos === 'number')
1936
- {
1937
- const textureInfo = textureInfos[textureIndex];
1938
- ASSERT(textureInfo, 'Texture not loaded');
1939
- const cols = textureInfo.size.x / size.x |0;
1940
- pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
1941
- }
1941
+ pos = vec2(pos%cols, pos/cols|0);
1942
+ pos = vec2(pos.x*sizePadded.x+padding, pos.y*sizePadded.y+padding);
1942
1943
 
1943
1944
  // return a tile info object
1944
- return new TileInfo(pos, size, textureIndex);
1945
+ return new TileInfo(pos, size, textureIndex, padding);
1945
1946
  }
1946
1947
 
1947
1948
  /**
@@ -1953,8 +1954,9 @@ class TileInfo
1953
1954
  * @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
1954
1955
  * @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
1955
1956
  * @param {Number} [textureIndex] - Texture index to use
1957
+ * @param {Number} [padding] - How many pixels padding around tiles
1956
1958
  */
1957
- constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
1959
+ constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
1958
1960
  {
1959
1961
  /** @property {Vector2} - Top left corner of tile in pixels */
1960
1962
  this.pos = pos.copy();
@@ -1962,6 +1964,8 @@ class TileInfo
1962
1964
  this.size = size.copy();
1963
1965
  /** @property {Number} - Texture index to use */
1964
1966
  this.textureIndex = textureIndex;
1967
+ /** @property {Number} - How many pixels padding around tiles */
1968
+ this.padding = padding;
1965
1969
  }
1966
1970
 
1967
1971
  /** Returns a copy of this tile offset by a vector
@@ -1978,7 +1982,7 @@ class TileInfo
1978
1982
  frame(frame)
1979
1983
  {
1980
1984
  ASSERT(typeof frame == 'number');
1981
- return this.offset(vec2(frame*this.size.x, 0));
1985
+ return this.offset(vec2(frame*(this.size.x+this.padding*2), 0));
1982
1986
  }
1983
1987
 
1984
1988
  /** Returns the texture info for this tile
@@ -2003,8 +2007,6 @@ class TextureInfo
2003
2007
  this.size = vec2(image.width, image.height);
2004
2008
  /** @property {WebGLTexture} - webgl texture */
2005
2009
  this.glTexture = glEnable && glCreateTexture(image);
2006
- /** @property {Vector2} - size to adjust tile to fix bleeding */
2007
- this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
2008
2010
  }
2009
2011
  }
2010
2012
 
@@ -2073,11 +2075,12 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2073
2075
  if (textureInfo)
2074
2076
  {
2075
2077
  // calculate uvs and render
2076
- const x = tileInfo.pos.x / textureInfo.size.x;
2077
- const y = tileInfo.pos.y / textureInfo.size.y;
2078
- const w = tileInfo.size.x / textureInfo.size.x;
2079
- const h = tileInfo.size.y / textureInfo.size.y;
2080
- const tileImageFixBleed = textureInfo.fixBleedSize;
2078
+ const sizeInverse = vec2(1).divide(textureInfo.size);
2079
+ const x = tileInfo.pos.x * sizeInverse.x;
2080
+ const y = tileInfo.pos.y * sizeInverse.y;
2081
+ const w = tileInfo.size.x * sizeInverse.x;
2082
+ const h = tileInfo.size.y * sizeInverse.y;
2083
+ const tileImageFixBleed = sizeInverse.scale(tileFixBleedScale);
2081
2084
  glSetTexture(textureInfo.glTexture);
2082
2085
  glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
2083
2086
  x + tileImageFixBleed.x, y + tileImageFixBleed.y,
@@ -2094,6 +2097,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
2094
2097
  {
2095
2098
  // normal canvas 2D rendering method (slower)
2096
2099
  showWatermark && ++drawCount;
2100
+ size = vec2(size.x, -size.y); // fix upside down sprites
2097
2101
  drawCanvas2D(pos, size, angle, mirror, (context)=>
2098
2102
  {
2099
2103
  if (textureInfo)
@@ -2131,6 +2135,74 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
2131
2135
  drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
2132
2136
  }
2133
2137
 
2138
+ /** Draw colored polygon using passed in points
2139
+ * @param {Array} points - Array of Vector2 points
2140
+ * @param {Color} [color=(1,1,1,1)]
2141
+ * @param {Number} [lineWidth=0]
2142
+ * @param {Color} [lineColor=(0,0,0,1)]
2143
+ * @param {Boolean} [screenSpace=false]
2144
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2145
+ * @memberof Draw */
2146
+ function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2147
+ {
2148
+ context.fillStyle = color.toString();
2149
+ context.beginPath();
2150
+ for (const point of screenSpace ? points : points.map(worldToScreen))
2151
+ context.lineTo(point.x, point.y);
2152
+ context.closePath();
2153
+ context.fill();
2154
+ if (lineWidth)
2155
+ {
2156
+ context.strokeStyle = lineColor.toString();
2157
+ context.lineWidth = screenSpace ? lineWidth : lineWidth*cameraScale;
2158
+ context.stroke();
2159
+ }
2160
+ }
2161
+
2162
+ /** Draw colored ellipse using passed in point
2163
+ * @param {Vector2} pos
2164
+ * @param {Number} [width=1]
2165
+ * @param {Number} [height=1]
2166
+ * @param {Number} [angle=0]
2167
+ * @param {Color} [color=(1,1,1,1)]
2168
+ * @param {Number} [lineWidth=0]
2169
+ * @param {Color} [lineColor=(0,0,0,1)]
2170
+ * @param {Boolean} [screenSpace=false]
2171
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2172
+ * @memberof Draw */
2173
+ function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2174
+ {
2175
+ if (!screenSpace)
2176
+ {
2177
+ pos = worldToScreen(pos);
2178
+ width *= cameraScale;
2179
+ height *= cameraScale;
2180
+ lineWidth *= cameraScale;
2181
+ }
2182
+ context.fillStyle = color.toString();
2183
+ context.beginPath();
2184
+ context.ellipse(pos.x, pos.y, width, height, angle, 0, 9);
2185
+ context.fill();
2186
+ if (lineWidth)
2187
+ {
2188
+ context.strokeStyle = lineColor.toString();
2189
+ context.lineWidth = lineWidth;
2190
+ context.stroke();
2191
+ }
2192
+ }
2193
+
2194
+ /** Draw colored circle using passed in point
2195
+ * @param {Vector2} pos
2196
+ * @param {Number} [radius=1]
2197
+ * @param {Color} [color=(1,1,1,1)]
2198
+ * @param {Number} [lineWidth=0]
2199
+ * @param {Color} [lineColor=(0,0,0,1)]
2200
+ * @param {Boolean} [screenSpace=false]
2201
+ * @param {CanvasRenderingContext2D} [context=mainContext]
2202
+ * @memberof Draw */
2203
+ function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
2204
+ { drawEllipse(pos, radius, radius, 0, color, lineWidth, lineColor, screenSpace, context); }
2205
+
2134
2206
  /** Draw colored line between two points
2135
2207
  * @param {Vector2} posA
2136
2208
  * @param {Vector2} posB
@@ -2201,10 +2273,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
2201
2273
  * @param {CanvasTextAlign} [textAlign='center']
2202
2274
  * @param {String} [font=fontDefault]
2203
2275
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2276
+ * @param {Number} [maxWidth]
2204
2277
  * @memberof Draw */
2205
- function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
2278
+ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context, maxWidth)
2206
2279
  {
2207
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
2280
+ drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context, maxWidth);
2208
2281
  }
2209
2282
 
2210
2283
  /** Draw text on overlay canvas in screen space
@@ -2218,8 +2291,9 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
2218
2291
  * @param {CanvasTextAlign} [textAlign]
2219
2292
  * @param {String} [font=fontDefault]
2220
2293
  * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
2294
+ * @param {Number} [maxWidth]
2221
2295
  * @memberof Draw */
2222
- function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
2296
+ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext, maxWidth=undefined)
2223
2297
  {
2224
2298
  context.fillStyle = color.toString();
2225
2299
  context.lineWidth = lineWidth;
@@ -2232,8 +2306,8 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
2232
2306
  pos = pos.copy();
2233
2307
  (text+'').split('\n').forEach(line=>
2234
2308
  {
2235
- lineWidth && context.strokeText(line, pos.x, pos.y);
2236
- context.fillText(line, pos.x, pos.y);
2309
+ lineWidth && context.strokeText(line, pos.x, pos.y, maxWidth);
2310
+ context.fillText(line, pos.x, pos.y, maxWidth);
2237
2311
  pos.y += size;
2238
2312
  });
2239
2313
  }
@@ -2341,8 +2415,8 @@ function toggleFullscreen()
2341
2415
  if (document.exitFullscreen)
2342
2416
  document.exitFullscreen();
2343
2417
  }
2344
- else if (document.body.requestFullscreen)
2345
- document.body.requestFullscreen();
2418
+ else if (engineRoot.requestFullscreen)
2419
+ engineRoot.requestFullscreen();
2346
2420
  }
2347
2421
  /**
2348
2422
  * LittleJS Input System
@@ -2513,7 +2587,7 @@ function inputInit()
2513
2587
 
2514
2588
  onkeydown = (e)=>
2515
2589
  {
2516
- if (debug && e.target != document.body) return;
2590
+ if (debug && e.target != engineRoot) return;
2517
2591
  if (!e.repeat)
2518
2592
  {
2519
2593
  isUsingGamepad = false;
@@ -2526,7 +2600,7 @@ function inputInit()
2526
2600
 
2527
2601
  onkeyup = (e)=>
2528
2602
  {
2529
- if (debug && e.target != document.body) return;
2603
+ if (debug && e.target != engineRoot) return;
2530
2604
  inputData[0][e.code] = 4;
2531
2605
  if (inputWASDEmulateDirection)
2532
2606
  inputData[0][remapKey(e.code)] = 4;
@@ -3178,17 +3252,6 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3178
3252
  {
3179
3253
  if (!soundEnable || headlessMode) return;
3180
3254
 
3181
- // prevent sounds from building up if they can't be played
3182
- if (audioContext.state != 'running')
3183
- {
3184
- // fix stalled audio
3185
- audioContext.resume().then(()=>
3186
- playSamples(sampleChannels, volume, rate, pan, loop, sampleRate, gainNode));
3187
-
3188
- // prevent suspended sounds from building up
3189
- return;
3190
- }
3191
-
3192
3255
  // create buffer and source
3193
3256
  const channelCount = sampleChannels.length;
3194
3257
  const sampleLength = sampleChannels[0].length;
@@ -3210,8 +3273,16 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
3210
3273
  const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
3211
3274
  source.connect(pannerNode).connect(gainNode);
3212
3275
 
3213
- // play and return sound
3214
- source.start();
3276
+ // play the sound
3277
+ if (audioContext.state != 'running')
3278
+ {
3279
+ // fix stalled audio and play
3280
+ audioContext.resume().then(()=>source.start());
3281
+ }
3282
+ else
3283
+ source.start();
3284
+
3285
+ // return sound
3215
3286
  return source;
3216
3287
  }
3217
3288
 
@@ -4371,10 +4442,10 @@ function glInit()
4371
4442
 
4372
4443
  // create the canvas and textures
4373
4444
  glCanvas = document.createElement('canvas');
4374
- glContext = glCanvas.getContext('webgl2');
4445
+ glContext = glCanvas.getContext('webgl2', {antialias:!canvasPixelated});
4375
4446
 
4376
4447
  // some browsers are much faster without copying the gl buffer so we just overlay it instead
4377
- glOverlay && document.body.appendChild(glCanvas);
4448
+ glOverlay && engineRoot.appendChild(glCanvas);
4378
4449
 
4379
4450
  // setup vertex and fragment shaders
4380
4451
  glShader = glCreateProgram(
@@ -4429,7 +4500,8 @@ function glPreRender()
4429
4500
  // set up the shader
4430
4501
  glContext.useProgram(glShader);
4431
4502
  glContext.activeTexture(gl_TEXTURE0);
4432
- glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
4503
+ if (textureInfos[0])
4504
+ glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
4433
4505
 
4434
4506
  // set vertex attributes
4435
4507
  let offset = glAdditive = glBatchAdditive = 0;
@@ -4527,8 +4599,14 @@ function glCreateTexture(image)
4527
4599
  // build the texture
4528
4600
  const texture = glContext.createTexture();
4529
4601
  glContext.bindTexture(gl_TEXTURE_2D, texture);
4530
- if (image)
4602
+ if (image && image.width)
4531
4603
  glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
4604
+ else
4605
+ {
4606
+ // create a white texture
4607
+ const whitePixel = new Uint8Array([255, 255, 255, 255]);
4608
+ glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, 1, 1, 0, gl_RGBA, gl_UNSIGNED_BYTE, whitePixel);
4609
+ }
4532
4610
 
4533
4611
  // use point filtering for pixelated rendering
4534
4612
  const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
@@ -4671,7 +4749,7 @@ const engineName = 'LittleJS';
4671
4749
  * @type {String}
4672
4750
  * @default
4673
4751
  * @memberof Engine */
4674
- const engineVersion = '1.9.11';
4752
+ const engineVersion = '1.10.2';
4675
4753
 
4676
4754
  /** Frames per second to update
4677
4755
  * @type {Number}
@@ -4716,6 +4794,12 @@ let timeReal = 0;
4716
4794
  * @memberof Engine */
4717
4795
  let paused = false;
4718
4796
 
4797
+ /** The root element that engine is attached to
4798
+ * @type {HTMLElement}
4799
+ * @default document.body
4800
+ * @memberof Engine */
4801
+ let engineRoot;
4802
+
4719
4803
  /** Set if game is paused
4720
4804
  * @param {Boolean} isPaused
4721
4805
  * @memberof Engine */
@@ -4749,8 +4833,9 @@ function engineAddPlugin(updateFunction, renderFunction)
4749
4833
  * @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
4750
4834
  * @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
4751
4835
  * @param {Array} [imageSources=['tiles.png']] - Image to load
4836
+ * @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
4752
4837
  * @memberof Engine */
4753
- function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
4838
+ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
4754
4839
  {
4755
4840
  ASSERT(Array.isArray(imageSources), 'pass in images as array');
4756
4841
 
@@ -4792,6 +4877,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4792
4877
  for (const o of engineObjects)
4793
4878
  o.parent || o.updateTransforms();
4794
4879
  inputUpdate();
4880
+ pluginUpdateList.forEach(f=>f());
4795
4881
  debugUpdate();
4796
4882
  gameUpdatePost();
4797
4883
  inputUpdatePost();
@@ -4907,16 +4993,22 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4907
4993
  }
4908
4994
 
4909
4995
  // setup html
4910
- const styleBody =
4996
+ const styleRoot =
4911
4997
  'margin:0;overflow:hidden;' + // fill the window
4998
+ 'width:100vw;height:100vh;' + // fill the window
4999
+ 'display:flex;' + // use flexbox
5000
+ 'align-items:center;' + // horizontal center
5001
+ (canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
5002
+ 'justify-content:center;' + // vertical center
4912
5003
  'background:#000;' + // set background color
4913
5004
  'user-select:none;' + // prevent hold to select
4914
5005
  '-webkit-user-select:none;' + // compatibility for ios
4915
5006
  (!touchInputEnable ? '' : // no touch css setttings
4916
5007
  'touch-action:none;' + // prevent mobile pinch to resize
4917
5008
  '-webkit-touch-callout:none');// compatibility for ios
4918
- document.body.style.cssText = styleBody;
4919
- document.body.appendChild(mainCanvas = document.createElement('canvas'));
5009
+ engineRoot = rootElement;
5010
+ engineRoot.style.cssText = styleRoot;
5011
+ engineRoot.appendChild(mainCanvas = document.createElement('canvas'));
4920
5012
  mainContext = mainCanvas.getContext('2d');
4921
5013
 
4922
5014
  // init stuff and start engine
@@ -4926,13 +5018,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4926
5018
  glInit();
4927
5019
 
4928
5020
  // create overlay canvas for hud to appear above gl canvas
4929
- document.body.appendChild(overlayCanvas = document.createElement('canvas'));
5021
+ engineRoot.appendChild(overlayCanvas = document.createElement('canvas'));
4930
5022
  overlayContext = overlayCanvas.getContext('2d');
4931
5023
 
4932
5024
  // set canvas style
4933
- const styleCanvas = 'position:absolute;' + // position
4934
- 'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
4935
- (glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5025
+ const styleCanvas = 'position:absolute'; // allow canvases to overlap
5026
+ mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
5027
+ if (glCanvas)
5028
+ glCanvas.style.cssText = styleCanvas;
4936
5029
  updateCanvas();
4937
5030
 
4938
5031
  // create promises for loading images
@@ -4949,19 +5042,32 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
4949
5042
  })
4950
5043
  );
4951
5044
 
4952
- // draw splash screen
4953
- showSplashScreen && promises.push(new Promise(resolve =>
5045
+ if (!imageSources.length)
4954
5046
  {
4955
- let t = 0;
4956
- console.log(`${engineName} Engine v${engineVersion}`);
4957
- updateSplash();
4958
- function updateSplash()
5047
+ // no images to load
5048
+ promises.push(new Promise(resolve =>
4959
5049
  {
4960
- clearInput();
4961
- drawEngineSplashScreen(t+=.01);
4962
- t>1 ? resolve() : setTimeout(updateSplash, 16);
4963
- }
4964
- }));
5050
+ textureInfos[0] = new TextureInfo(new Image);
5051
+ resolve();
5052
+ }));
5053
+ }
5054
+
5055
+ if (showSplashScreen)
5056
+ {
5057
+ // draw splash screen
5058
+ promises.push(new Promise(resolve =>
5059
+ {
5060
+ let t = 0;
5061
+ console.log(`${engineName} Engine v${engineVersion}`);
5062
+ updateSplash();
5063
+ function updateSplash()
5064
+ {
5065
+ clearInput();
5066
+ drawEngineSplashScreen(t+=.01);
5067
+ t>1 ? resolve() : setTimeout(updateSplash, 16);
5068
+ }
5069
+ }));
5070
+ }
4965
5071
 
4966
5072
  // load all of the images
4967
5073
  Promise.all(promises).then(startEngine);
@@ -153,4 +153,4 @@ function gameRenderPost()
153
153
  ///////////////////////////////////////////////////////////////////////////////
154
154
  // Startup LittleJS Engine with Box2D
155
155
 
156
- box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
156
+ box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
@@ -1,13 +1,14 @@
1
1
  <!DOCTYPE html><head>
2
- <title>Little JS Starter Project</title>
2
+ <title>LittleJS Box2D Example</title>
3
+ <meta charset=utf-8>
3
4
  <meta name=apple-mobile-web-app-capable content=yes>
4
5
  <meta name=mobile-web-app-capable content=yes>
5
6
  <link rel=icon type=image/png href=../favicon.png>
6
7
  </head><body>
7
8
 
8
9
  <script src=../../dist/littlejs.js></script>
9
- <script src=../../plugins/Box2D_v2.3.1_min.wasm.js?198></script>
10
- <script src=../../plugins/box2d.js?198></script>
11
- <script src=scenes.js?198></script>
12
- <script src=gameObjects.js?198></script>
13
- <script src=game.js?198></script>
10
+ <script src=../../plugins/Box2D_v2.3.1_min.wasm.js?1100></script>
11
+ <script src=../../plugins/box2d.js?1100></script>
12
+ <script src=scenes.js?1100></script>
13
+ <script src=gameObjects.js?1100></script>
14
+ <script src=game.js?1100></script>
@@ -135,4 +135,4 @@ function setupPostProcess()
135
135
 
136
136
  ///////////////////////////////////////////////////////////////////////////////
137
137
  // Startup LittleJS Engine
138
- engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
138
+ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
@@ -1,11 +1,12 @@
1
1
  <!DOCTYPE html><head>
2
- <title>Little JS Breakout Game</title>
2
+ <title>LittleJS Breakout Game</title>
3
+ <meta charset=utf-8>
3
4
  <meta name=apple-mobile-web-app-capable content=yes>
4
5
  <meta name=mobile-web-app-capable content=yes>
5
6
  <link rel=icon type=image/png href=../favicon.png>
6
7
  </head><body>
7
8
 
8
- <script src=../../dist/littlejs.js?198></script>
9
+ <script src=../../dist/littlejs.js?1100></script>
9
10
  <script src=../../plugins/postProcess.js></script>
10
- <script src=gameObjects.js?198></script>
11
- <script src=game.js?198></script>
11
+ <script src=gameObjects.js?1100></script>
12
+ <script src=game.js?1100></script>
@@ -1,9 +1,10 @@
1
1
  <!DOCTYPE html><head>
2
- <title>Little JS Breakout Tutorial</title>
2
+ <title>LittleJS Breakout Tutorial</title>
3
+ <meta charset=utf-8>
3
4
  <meta name=apple-mobile-web-app-capable content=yes>
4
5
  <meta name=mobile-web-app-capable content=yes>
5
6
  <link rel=icon type=image/png href=../favicon.png>
6
7
  </head><body>
7
8
 
8
- <script src=../../dist/littlejs.js?198></script>
9
- <script src=game.js?198></script>
9
+ <script src=../../dist/littlejs.js?1100></script>
10
+ <script src=game.js?1100></script>
@@ -125,4 +125,4 @@ function gameRenderPost()
125
125
 
126
126
  ///////////////////////////////////////////////////////////////////////////////
127
127
  // Startup LittleJS Engine
128
- engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
128
+ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
@@ -1,12 +1,13 @@
1
1
  <!DOCTYPE html><head>
2
- <title>Little JS Electron Demo</title>
2
+ <title>LittleJS Electron Demo</title>
3
+ <meta charset=utf-8>
3
4
  <meta name=apple-mobile-web-app-capable content=yes>
4
5
  <meta name=mobile-web-app-capable content=yes>
5
6
  <link rel=icon type=image/png href=../favicon.png>
6
7
  </head><body>
7
8
 
8
9
  <!-- LittleJS Engine -->
9
- <script src=../../dist/littlejs.js?198></script>
10
+ <script src=../../dist/littlejs.js?1100></script>
10
11
 
11
12
  <!-- Add your game scripts here -->
12
- <script src=game.js?198></script>
13
+ <script src=game.js?1100></script>
@@ -44,4 +44,4 @@ function gameRenderPost()
44
44
 
45
45
  ///////////////////////////////////////////////////////////////////////////////
46
46
  // Startup LittleJS Engine
47
- engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
47
+ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
@@ -1,5 +1,6 @@
1
1
  <!DOCTYPE html><head>
2
- <title>Little JS Hello World Demo</title>
2
+ <title>LittleJS Hello World Demo</title>
3
+ <meta charset=utf-8>
3
4
  </head><body>
4
5
 
5
6
  <script src=../../dist/littlejs.js></script>
@@ -0,0 +1,67 @@
1
+ /*
2
+ Little JS HTML Menu Example Project
3
+ - Setup a simple html menu system
4
+ - Menu can be toggled
5
+ - Pauses game when menu is visible
6
+ - Shows several input types
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ // show the LittleJS splash screen
12
+ setShowSplashScreen(true);
13
+
14
+ // sound effects
15
+ const sound_click = new Sound([1,.5]);
16
+
17
+ ///////////////////////////////////////////////////////////////////////////////
18
+ function gameInit()
19
+ {
20
+ // show menu for demo
21
+ setMenuVisible(true);
22
+ }
23
+
24
+ ///////////////////////////////////////////////////////////////////////////////
25
+ function gameUpdate()
26
+ {
27
+ // play sound when mouse is pressed
28
+ if (mouseWasPressed(0))
29
+ sound_click.play(mousePos);
30
+ }
31
+
32
+ ///////////////////////////////////////////////////////////////////////////////
33
+ function gameUpdatePost()
34
+ {
35
+ // pause game when menu is visible
36
+ const menuVisible = getMenuVisible();
37
+ paused = menuVisible;
38
+
39
+ // toggle menu visibility
40
+ if (keyWasPressed('KeyM'))
41
+ setMenuVisible(!menuVisible);
42
+ }
43
+
44
+ ///////////////////////////////////////////////////////////////////////////////
45
+ function gameRender()
46
+ {
47
+ // test game rendering
48
+ drawRect(vec2(), vec2(1e3), hsl(0,0,.2));
49
+ for(let i=0; i<1e3; ++i)
50
+ {
51
+ const pos = vec2(30*Math.sin(i+time/9),20*Math.sin(i*i+time/9));
52
+ drawTile(pos, vec2(2), tile(3,128), hsl(i/9,1,.4), time+i, !(i%2), hsl(i/9,1,.1,0));
53
+ }
54
+ }
55
+
56
+ ///////////////////////////////////////////////////////////////////////////////
57
+ function gameRenderPost()
58
+ {
59
+ // draw to overlay canvas for hud rendering
60
+ drawTextScreen('LittleJS HTML Menu Example\nM = Toggle menu',
61
+ vec2(mainCanvasSize.x/2, 70), 60, // position, size
62
+ hsl(0,0,1), 6, hsl(0,0,0)); // color, outline size and color
63
+ }
64
+
65
+ ///////////////////////////////////////////////////////////////////////////////
66
+ // Startup LittleJS Engine
67
+ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);