littlejsengine 1.9.11 → 1.10.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.
- package/README.md +9 -13
- package/dist/littlejs.d.ts +129 -10
- package/dist/littlejs.esm.js +220 -75
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +188 -73
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +187 -72
- package/examples/box2d/game.js +4 -1
- package/examples/box2d/index.html +7 -6
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/index.html +5 -4
- package/examples/breakoutTutorial/README.md +9 -7
- package/examples/breakoutTutorial/game.js +6 -6
- package/examples/breakoutTutorial/index.html +4 -3
- package/examples/electron/game.js +1 -1
- package/examples/electron/index.html +4 -3
- package/examples/empty/game.js +1 -1
- package/examples/empty/index.html +2 -1
- package/examples/htmlMenu/game.js +67 -0
- package/examples/htmlMenu/index.html +56 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +33 -0
- package/examples/js13k/build/index.html +1 -0
- package/examples/js13k/build/index.js +1 -0
- package/examples/js13k/build/tiles.png +0 -0
- package/examples/js13k/game - Copy.zip +0 -0
- package/examples/js13k/game.js +1 -1
- package/examples/js13k/game.zip +0 -0
- package/examples/js13k/index.html +17 -14
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +3 -2
- package/examples/particles/index.html +4 -3
- package/examples/platformer/game.js +7 -5
- package/examples/platformer/gameCharacter.js +12 -11
- package/examples/platformer/gameEffects.js +3 -4
- package/examples/platformer/gameLevel.js +18 -34
- package/examples/platformer/gameObjects.js +9 -15
- package/examples/platformer/index.html +10 -9
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +4 -3
- package/examples/starter/build/index.html +2 -0
- package/examples/starter/build/index.js +1 -0
- package/examples/starter/build/tiles.png +0 -0
- package/examples/starter/game.js +4 -4
- package/examples/starter/game.zip +0 -0
- package/examples/starter/index.html +15 -14
- package/examples/stress/index.html +3 -2
- package/examples/typescript/build/build/littlejs.esm.js +4327 -0
- package/examples/typescript/build/dist/littlejs.esm.js +4425 -0
- package/examples/typescript/build/examples/typescript/build.js +24 -0
- package/examples/typescript/build/examples/typescript/game.js +100 -0
- package/examples/typescript/build/examples/typescript/test/build/littlejs.esm.js +3934 -0
- package/examples/typescript/build/examples/typescript/test/examples/typescript/build.js +86 -0
- package/examples/typescript/build/examples/typescript/test/examples/typescript/game.js +92 -0
- package/examples/typescript/game.js +1 -1
- package/examples/typescript/game.ts +1 -1
- package/examples/typescript/index.html +3 -2
- package/examples/uiSystem/game.js +134 -0
- package/examples/uiSystem/index.html +25 -0
- package/examples/uiSystem/tiles.png +0 -0
- package/jsconfig.json +11 -0
- package/package.json +1 -1
- package/plugins/box2d.js +2 -1
- package/plugins/postProcess.js +14 -8
- package/plugins/uiSystem.js +243 -0
- package/src/engine.js +42 -22
- package/src/engineAudio.js +10 -13
- package/src/engineDebug.js +1 -1
- package/src/engineDraw.js +102 -27
- package/src/engineExport.js +32 -2
- package/src/engineInput.js +1 -3
- package/src/engineObject.js +1 -0
- package/src/engineSettings.js +1 -1
- package/src/engineTileLayer.js +2 -1
- package/src/engineUtilities.js +2 -1
- package/src/engineWebGL.js +26 -4
package/dist/littlejs.release.js
CHANGED
|
@@ -521,6 +521,7 @@ class Vector2
|
|
|
521
521
|
* @param {Number} [length] */
|
|
522
522
|
setDirection(direction, length=1)
|
|
523
523
|
{
|
|
524
|
+
direction = mod(direction, 4);
|
|
524
525
|
ASSERT(direction==0 || direction==1 || direction==2 || direction==3);
|
|
525
526
|
return vec2(direction%2 ? direction-1 ? -length : length : 0,
|
|
526
527
|
direction%2 ? 0 : direction ? -length : length);
|
|
@@ -773,7 +774,7 @@ class Color
|
|
|
773
774
|
* @return {String} */
|
|
774
775
|
toString(useAlpha = true)
|
|
775
776
|
{
|
|
776
|
-
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
777
|
+
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
777
778
|
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b) + (useAlpha ? toHex(this.a) : '');
|
|
778
779
|
}
|
|
779
780
|
|
|
@@ -1003,7 +1004,7 @@ let tileSizeDefault = vec2(16);
|
|
|
1003
1004
|
* @type {Number}
|
|
1004
1005
|
* @default
|
|
1005
1006
|
* @memberof Settings */
|
|
1006
|
-
let tileFixBleedScale =
|
|
1007
|
+
let tileFixBleedScale = 0;
|
|
1007
1008
|
|
|
1008
1009
|
///////////////////////////////////////////////////////////////////////////////
|
|
1009
1010
|
// Object settings
|
|
@@ -1703,6 +1704,7 @@ class EngineObject
|
|
|
1703
1704
|
this.pos.x = oldPos.x;
|
|
1704
1705
|
this.velocity.x *= -this.elasticity;
|
|
1705
1706
|
}
|
|
1707
|
+
debugOverlay && debugPhysics && debugRect(this.pos, this.size, '#f00');
|
|
1706
1708
|
}
|
|
1707
1709
|
}
|
|
1708
1710
|
}
|
|
@@ -1905,12 +1907,13 @@ let drawCount;
|
|
|
1905
1907
|
///////////////////////////////////////////////////////////////////////////////
|
|
1906
1908
|
|
|
1907
1909
|
/**
|
|
1908
|
-
* Create a tile info object
|
|
1910
|
+
* Create a tile info object using a grid based system
|
|
1909
1911
|
* - This can take vecs or floats for easier use and conversion
|
|
1910
1912
|
* - If an index is passed in, the tile size and index will determine the position
|
|
1911
|
-
* @param {(Number|Vector2)} [pos=
|
|
1913
|
+
* @param {(Number|Vector2)} [pos=0] - Index of tile in sheet
|
|
1912
1914
|
* @param {(Number|Vector2)} [size=tileSizeDefault] - Size of tile in pixels
|
|
1913
1915
|
* @param {Number} [textureIndex] - Texture index to use
|
|
1916
|
+
* @param {Number} [padding] - How many pixels padding around tiles
|
|
1914
1917
|
* @return {TileInfo}
|
|
1915
1918
|
* @example
|
|
1916
1919
|
* tile(2) // a tile at index 2 using the default tile size of 16
|
|
@@ -1919,7 +1922,7 @@ let drawCount;
|
|
|
1919
1922
|
* tile(vec2(4,8), vec2(30,10)) // a tile at pixel location (4,8) with a size of (30,10)
|
|
1920
1923
|
* @memberof Draw
|
|
1921
1924
|
*/
|
|
1922
|
-
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1925
|
+
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
|
1923
1926
|
{
|
|
1924
1927
|
if (headlessMode)
|
|
1925
1928
|
return new TileInfo;
|
|
@@ -1931,17 +1934,17 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
|
1931
1934
|
size = vec2(size);
|
|
1932
1935
|
}
|
|
1933
1936
|
|
|
1934
|
-
//
|
|
1937
|
+
// use pos as a tile index
|
|
1938
|
+
const textureInfo = textureInfos[textureIndex];
|
|
1939
|
+
ASSERT(textureInfo, 'Texture not loaded');
|
|
1940
|
+
const sizePadded = size.add(vec2(padding*2));
|
|
1941
|
+
const cols = textureInfo.size.x / sizePadded.x |0;
|
|
1935
1942
|
if (typeof pos === 'number')
|
|
1936
|
-
|
|
1937
|
-
|
|
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
|
-
}
|
|
1943
|
+
pos = vec2(pos%cols, pos/cols|0);
|
|
1944
|
+
pos = vec2(pos.x*sizePadded.x+padding, pos.y*sizePadded.y+padding);
|
|
1942
1945
|
|
|
1943
1946
|
// return a tile info object
|
|
1944
|
-
return new TileInfo(pos, size, textureIndex);
|
|
1947
|
+
return new TileInfo(pos, size, textureIndex, padding);
|
|
1945
1948
|
}
|
|
1946
1949
|
|
|
1947
1950
|
/**
|
|
@@ -1953,8 +1956,9 @@ class TileInfo
|
|
|
1953
1956
|
* @param {Vector2} [pos=(0,0)] - Top left corner of tile in pixels
|
|
1954
1957
|
* @param {Vector2} [size=tileSizeDefault] - Size of tile in pixels
|
|
1955
1958
|
* @param {Number} [textureIndex] - Texture index to use
|
|
1959
|
+
* @param {Number} [padding] - How many pixels padding around tiles
|
|
1956
1960
|
*/
|
|
1957
|
-
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
1961
|
+
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0, padding=0)
|
|
1958
1962
|
{
|
|
1959
1963
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
1960
1964
|
this.pos = pos.copy();
|
|
@@ -1962,6 +1966,8 @@ class TileInfo
|
|
|
1962
1966
|
this.size = size.copy();
|
|
1963
1967
|
/** @property {Number} - Texture index to use */
|
|
1964
1968
|
this.textureIndex = textureIndex;
|
|
1969
|
+
/** @property {Number} - How many pixels padding around tiles */
|
|
1970
|
+
this.padding = padding;
|
|
1965
1971
|
}
|
|
1966
1972
|
|
|
1967
1973
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -1978,7 +1984,7 @@ class TileInfo
|
|
|
1978
1984
|
frame(frame)
|
|
1979
1985
|
{
|
|
1980
1986
|
ASSERT(typeof frame == 'number');
|
|
1981
|
-
return this.offset(vec2(frame*this.size.x, 0));
|
|
1987
|
+
return this.offset(vec2(frame*(this.size.x+this.padding*2), 0));
|
|
1982
1988
|
}
|
|
1983
1989
|
|
|
1984
1990
|
/** Returns the texture info for this tile
|
|
@@ -2003,8 +2009,6 @@ class TextureInfo
|
|
|
2003
2009
|
this.size = vec2(image.width, image.height);
|
|
2004
2010
|
/** @property {WebGLTexture} - webgl texture */
|
|
2005
2011
|
this.glTexture = glEnable && glCreateTexture(image);
|
|
2006
|
-
/** @property {Vector2} - size to adjust tile to fix bleeding */
|
|
2007
|
-
this.fixBleedSize = vec2(tileFixBleedScale).divide(this.size);
|
|
2008
2012
|
}
|
|
2009
2013
|
}
|
|
2010
2014
|
|
|
@@ -2073,11 +2077,12 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
2073
2077
|
if (textureInfo)
|
|
2074
2078
|
{
|
|
2075
2079
|
// calculate uvs and render
|
|
2076
|
-
const
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2080
|
-
const
|
|
2080
|
+
const sizeInverse = vec2(1).divide(textureInfo.size);
|
|
2081
|
+
const x = tileInfo.pos.x * sizeInverse.x;
|
|
2082
|
+
const y = tileInfo.pos.y * sizeInverse.y;
|
|
2083
|
+
const w = tileInfo.size.x * sizeInverse.x;
|
|
2084
|
+
const h = tileInfo.size.y * sizeInverse.y;
|
|
2085
|
+
const tileImageFixBleed = sizeInverse.scale(tileFixBleedScale);
|
|
2081
2086
|
glSetTexture(textureInfo.glTexture);
|
|
2082
2087
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
2083
2088
|
x + tileImageFixBleed.x, y + tileImageFixBleed.y,
|
|
@@ -2094,6 +2099,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
2094
2099
|
{
|
|
2095
2100
|
// normal canvas 2D rendering method (slower)
|
|
2096
2101
|
showWatermark && ++drawCount;
|
|
2102
|
+
size = vec2(size.x, -size.y); // fix upside down sprites
|
|
2097
2103
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
2098
2104
|
{
|
|
2099
2105
|
if (textureInfo)
|
|
@@ -2147,6 +2153,74 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
|
|
|
2147
2153
|
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace, context);
|
|
2148
2154
|
}
|
|
2149
2155
|
|
|
2156
|
+
/** Draw colored polygon using passed in points
|
|
2157
|
+
* @param {Array} points - Array of Vector2 points
|
|
2158
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2159
|
+
* @param {Number} [lineWidth=0]
|
|
2160
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2161
|
+
* @param {Boolean} [screenSpace=false]
|
|
2162
|
+
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2163
|
+
* @memberof Draw */
|
|
2164
|
+
function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
|
|
2165
|
+
{
|
|
2166
|
+
context.fillStyle = color.toString();
|
|
2167
|
+
context.beginPath();
|
|
2168
|
+
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
2169
|
+
context.lineTo(point.x, point.y);
|
|
2170
|
+
context.closePath();
|
|
2171
|
+
context.fill();
|
|
2172
|
+
if (lineWidth)
|
|
2173
|
+
{
|
|
2174
|
+
context.strokeStyle = lineColor.toString();
|
|
2175
|
+
context.lineWidth = screenSpace ? lineWidth : lineWidth*cameraScale;
|
|
2176
|
+
context.stroke();
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
/** Draw colored ellipse using passed in point
|
|
2181
|
+
* @param {Vector2} pos
|
|
2182
|
+
* @param {Number} [width=1]
|
|
2183
|
+
* @param {Number} [height=1]
|
|
2184
|
+
* @param {Number} [angle=0]
|
|
2185
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2186
|
+
* @param {Number} [lineWidth=0]
|
|
2187
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2188
|
+
* @param {Boolean} [screenSpace=false]
|
|
2189
|
+
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2190
|
+
* @memberof Draw */
|
|
2191
|
+
function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
|
|
2192
|
+
{
|
|
2193
|
+
if (!screenSpace)
|
|
2194
|
+
{
|
|
2195
|
+
pos = worldToScreen(pos);
|
|
2196
|
+
width *= cameraScale;
|
|
2197
|
+
height *= cameraScale;
|
|
2198
|
+
lineWidth *= cameraScale;
|
|
2199
|
+
}
|
|
2200
|
+
context.fillStyle = color.toString();
|
|
2201
|
+
context.beginPath();
|
|
2202
|
+
context.ellipse(pos.x, pos.y, width, height, angle, 0, 9);
|
|
2203
|
+
context.fill();
|
|
2204
|
+
if (lineWidth)
|
|
2205
|
+
{
|
|
2206
|
+
context.strokeStyle = lineColor.toString();
|
|
2207
|
+
context.lineWidth = lineWidth;
|
|
2208
|
+
context.stroke();
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
|
|
2212
|
+
/** Draw colored circle using passed in point
|
|
2213
|
+
* @param {Vector2} pos
|
|
2214
|
+
* @param {Number} [radius=1]
|
|
2215
|
+
* @param {Color} [color=(1,1,1,1)]
|
|
2216
|
+
* @param {Number} [lineWidth=0]
|
|
2217
|
+
* @param {Color} [lineColor=(0,0,0,1)]
|
|
2218
|
+
* @param {Boolean} [screenSpace=false]
|
|
2219
|
+
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2220
|
+
* @memberof Draw */
|
|
2221
|
+
function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=mainContext)
|
|
2222
|
+
{ drawEllipse(pos, radius, radius, 0, color, lineWidth, lineColor, screenSpace, context); }
|
|
2223
|
+
|
|
2150
2224
|
/** Draw directly to a 2d canvas context in world space
|
|
2151
2225
|
* @param {Vector2} pos
|
|
2152
2226
|
* @param {Vector2} size
|
|
@@ -2201,10 +2275,11 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
2201
2275
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
2202
2276
|
* @param {String} [font=fontDefault]
|
|
2203
2277
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
2278
|
+
* @param {Number} [maxWidth]
|
|
2204
2279
|
* @memberof Draw */
|
|
2205
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
|
|
2280
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context, maxWidth)
|
|
2206
2281
|
{
|
|
2207
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context);
|
|
2282
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, context, maxWidth);
|
|
2208
2283
|
}
|
|
2209
2284
|
|
|
2210
2285
|
/** Draw text on overlay canvas in screen space
|
|
@@ -2218,8 +2293,9 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
2218
2293
|
* @param {CanvasTextAlign} [textAlign]
|
|
2219
2294
|
* @param {String} [font=fontDefault]
|
|
2220
2295
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
2296
|
+
* @param {Number} [maxWidth]
|
|
2221
2297
|
* @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)
|
|
2298
|
+
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
2299
|
{
|
|
2224
2300
|
context.fillStyle = color.toString();
|
|
2225
2301
|
context.lineWidth = lineWidth;
|
|
@@ -2232,8 +2308,8 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
2232
2308
|
pos = pos.copy();
|
|
2233
2309
|
(text+'').split('\n').forEach(line=>
|
|
2234
2310
|
{
|
|
2235
|
-
lineWidth && context.strokeText(line, pos.x, pos.y);
|
|
2236
|
-
context.fillText(line, pos.x, pos.y);
|
|
2311
|
+
lineWidth && context.strokeText(line, pos.x, pos.y, maxWidth);
|
|
2312
|
+
context.fillText(line, pos.x, pos.y, maxWidth);
|
|
2237
2313
|
pos.y += size;
|
|
2238
2314
|
});
|
|
2239
2315
|
}
|
|
@@ -2336,13 +2412,14 @@ function isFullscreen() { return !!document.fullscreenElement; }
|
|
|
2336
2412
|
* @memberof Draw */
|
|
2337
2413
|
function toggleFullscreen()
|
|
2338
2414
|
{
|
|
2415
|
+
const rootElement = mainCanvas.parentElement;
|
|
2339
2416
|
if (isFullscreen())
|
|
2340
2417
|
{
|
|
2341
2418
|
if (document.exitFullscreen)
|
|
2342
2419
|
document.exitFullscreen();
|
|
2343
2420
|
}
|
|
2344
|
-
else if (
|
|
2345
|
-
|
|
2421
|
+
else if (rootElement.requestFullscreen)
|
|
2422
|
+
rootElement.requestFullscreen();
|
|
2346
2423
|
}
|
|
2347
2424
|
/**
|
|
2348
2425
|
* LittleJS Input System
|
|
@@ -2513,7 +2590,6 @@ function inputInit()
|
|
|
2513
2590
|
|
|
2514
2591
|
onkeydown = (e)=>
|
|
2515
2592
|
{
|
|
2516
|
-
if (debug && e.target != document.body) return;
|
|
2517
2593
|
if (!e.repeat)
|
|
2518
2594
|
{
|
|
2519
2595
|
isUsingGamepad = false;
|
|
@@ -2526,7 +2602,6 @@ function inputInit()
|
|
|
2526
2602
|
|
|
2527
2603
|
onkeyup = (e)=>
|
|
2528
2604
|
{
|
|
2529
|
-
if (debug && e.target != document.body) return;
|
|
2530
2605
|
inputData[0][e.code] = 4;
|
|
2531
2606
|
if (inputWASDEmulateDirection)
|
|
2532
2607
|
inputData[0][remapKey(e.code)] = 4;
|
|
@@ -2728,7 +2803,7 @@ function touchInputInit()
|
|
|
2728
2803
|
// set event pos and pass it along
|
|
2729
2804
|
const p = vec2(e.touches[0].clientX, e.touches[0].clientY);
|
|
2730
2805
|
mousePosScreen = mouseToScreen(p);
|
|
2731
|
-
wasTouching ? isUsingGamepad =
|
|
2806
|
+
wasTouching ? isUsingGamepad = touchGamepadEnable : inputData[0][button] = 3;
|
|
2732
2807
|
}
|
|
2733
2808
|
else if (wasTouching)
|
|
2734
2809
|
inputData[0][button] = inputData[0][button] & 2 | 4;
|
|
@@ -3178,17 +3253,6 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
3178
3253
|
{
|
|
3179
3254
|
if (!soundEnable || headlessMode) return;
|
|
3180
3255
|
|
|
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
3256
|
// create buffer and source
|
|
3193
3257
|
const channelCount = sampleChannels.length;
|
|
3194
3258
|
const sampleLength = sampleChannels[0].length;
|
|
@@ -3210,8 +3274,16 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
3210
3274
|
const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
|
|
3211
3275
|
source.connect(pannerNode).connect(gainNode);
|
|
3212
3276
|
|
|
3213
|
-
// play
|
|
3214
|
-
|
|
3277
|
+
// play the sound
|
|
3278
|
+
if (audioContext.state != 'running')
|
|
3279
|
+
{
|
|
3280
|
+
// fix stalled audio and play
|
|
3281
|
+
audioContext.resume().then(()=>source.start());
|
|
3282
|
+
}
|
|
3283
|
+
else
|
|
3284
|
+
source.start();
|
|
3285
|
+
|
|
3286
|
+
// return sound
|
|
3215
3287
|
return source;
|
|
3216
3288
|
}
|
|
3217
3289
|
|
|
@@ -3527,6 +3599,7 @@ function tileCollisionTest(pos, size=vec2(), object)
|
|
|
3527
3599
|
if (tileData && (!object || object.collideWithTile(tileData, vec2(x, y))))
|
|
3528
3600
|
return true;
|
|
3529
3601
|
}
|
|
3602
|
+
return false;
|
|
3530
3603
|
}
|
|
3531
3604
|
|
|
3532
3605
|
/** Return the center of first tile hit (does not return the exact intersection)
|
|
@@ -3550,7 +3623,7 @@ function tileCollisionRaycast(posStart, posEnd, object)
|
|
|
3550
3623
|
let xi = unit.x * (delta.x < 0 ? posStart.x - pos.x : pos.x - posStart.x + 1);
|
|
3551
3624
|
let yi = unit.y * (delta.y < 0 ? posStart.y - pos.y : pos.y - posStart.y + 1);
|
|
3552
3625
|
|
|
3553
|
-
while (
|
|
3626
|
+
while (true)
|
|
3554
3627
|
{
|
|
3555
3628
|
// check for tile collision
|
|
3556
3629
|
const tileData = getTileCollisionData(pos);
|
|
@@ -4359,6 +4432,11 @@ let glCanvas;
|
|
|
4359
4432
|
* @memberof WebGL */
|
|
4360
4433
|
let glContext;
|
|
4361
4434
|
|
|
4435
|
+
/** Shoule webgl be setup with antialiasing, must be set before calling engineInit
|
|
4436
|
+
* @type {Boolean}
|
|
4437
|
+
* @memberof WebGL */
|
|
4438
|
+
let glAntialias = true;
|
|
4439
|
+
|
|
4362
4440
|
// WebGL internal variables not exposed to documentation
|
|
4363
4441
|
let glShader, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glInstanceCount, glAdditive, glBatchAdditive;
|
|
4364
4442
|
|
|
@@ -4371,10 +4449,11 @@ function glInit()
|
|
|
4371
4449
|
|
|
4372
4450
|
// create the canvas and textures
|
|
4373
4451
|
glCanvas = document.createElement('canvas');
|
|
4374
|
-
glContext = glCanvas.getContext('webgl2');
|
|
4452
|
+
glContext = glCanvas.getContext('webgl2', {antialias:glAntialias});
|
|
4375
4453
|
|
|
4376
4454
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
4377
|
-
|
|
4455
|
+
const rootElement = mainCanvas.parentElement;
|
|
4456
|
+
glOverlay && rootElement.appendChild(glCanvas);
|
|
4378
4457
|
|
|
4379
4458
|
// setup vertex and fragment shaders
|
|
4380
4459
|
glShader = glCreateProgram(
|
|
@@ -4429,7 +4508,8 @@ function glPreRender()
|
|
|
4429
4508
|
// set up the shader
|
|
4430
4509
|
glContext.useProgram(glShader);
|
|
4431
4510
|
glContext.activeTexture(gl_TEXTURE0);
|
|
4432
|
-
|
|
4511
|
+
if (textureInfos[0])
|
|
4512
|
+
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = textureInfos[0].glTexture);
|
|
4433
4513
|
|
|
4434
4514
|
// set vertex attributes
|
|
4435
4515
|
let offset = glAdditive = glBatchAdditive = 0;
|
|
@@ -4527,8 +4607,14 @@ function glCreateTexture(image)
|
|
|
4527
4607
|
// build the texture
|
|
4528
4608
|
const texture = glContext.createTexture();
|
|
4529
4609
|
glContext.bindTexture(gl_TEXTURE_2D, texture);
|
|
4530
|
-
if (image)
|
|
4610
|
+
if (image && image.width)
|
|
4531
4611
|
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, gl_RGBA, gl_UNSIGNED_BYTE, image);
|
|
4612
|
+
else
|
|
4613
|
+
{
|
|
4614
|
+
// create a white texture
|
|
4615
|
+
const whitePixel = new Uint8Array([255, 255, 255, 255]);
|
|
4616
|
+
glContext.texImage2D(gl_TEXTURE_2D, 0, gl_RGBA, 1, 1, 0, gl_RGBA, gl_UNSIGNED_BYTE, whitePixel);
|
|
4617
|
+
}
|
|
4532
4618
|
|
|
4533
4619
|
// use point filtering for pixelated rendering
|
|
4534
4620
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
@@ -4571,6 +4657,15 @@ function glCopyToContext(context, forceDraw=false)
|
|
|
4571
4657
|
context.drawImage(glCanvas, 0, 0);
|
|
4572
4658
|
}
|
|
4573
4659
|
|
|
4660
|
+
/** Set antialiasing for webgl canvas
|
|
4661
|
+
* @param {Boolean} [antialias]
|
|
4662
|
+
* @memberof WebGL */
|
|
4663
|
+
function glSetAntialias(antialias=true)
|
|
4664
|
+
{
|
|
4665
|
+
ASSERT(!glCanvas, 'must be called before engineInit');
|
|
4666
|
+
glAntialias = antialias;
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4574
4669
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
4575
4670
|
* @param {Number} x
|
|
4576
4671
|
* @param {Number} y
|
|
@@ -4671,7 +4766,7 @@ const engineName = 'LittleJS';
|
|
|
4671
4766
|
* @type {String}
|
|
4672
4767
|
* @default
|
|
4673
4768
|
* @memberof Engine */
|
|
4674
|
-
const engineVersion = '1.
|
|
4769
|
+
const engineVersion = '1.10.4';
|
|
4675
4770
|
|
|
4676
4771
|
/** Frames per second to update
|
|
4677
4772
|
* @type {Number}
|
|
@@ -4715,7 +4810,6 @@ let timeReal = 0;
|
|
|
4715
4810
|
* @default false
|
|
4716
4811
|
* @memberof Engine */
|
|
4717
4812
|
let paused = false;
|
|
4718
|
-
|
|
4719
4813
|
/** Set if game is paused
|
|
4720
4814
|
* @param {Boolean} isPaused
|
|
4721
4815
|
* @memberof Engine */
|
|
@@ -4748,9 +4842,10 @@ function engineAddPlugin(updateFunction, renderFunction)
|
|
|
4748
4842
|
* @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
|
|
4749
4843
|
* @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
|
|
4750
4844
|
* @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
|
|
4751
|
-
* @param {Array} [imageSources=[
|
|
4845
|
+
* @param {Array} [imageSources=[]] - Image to load
|
|
4846
|
+
* @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default
|
|
4752
4847
|
* @memberof Engine */
|
|
4753
|
-
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[
|
|
4848
|
+
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
4754
4849
|
{
|
|
4755
4850
|
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
4756
4851
|
|
|
@@ -4792,6 +4887,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4792
4887
|
for (const o of engineObjects)
|
|
4793
4888
|
o.parent || o.updateTransforms();
|
|
4794
4889
|
inputUpdate();
|
|
4890
|
+
pluginUpdateList.forEach(f=>f());
|
|
4795
4891
|
debugUpdate();
|
|
4796
4892
|
gameUpdatePost();
|
|
4797
4893
|
inputUpdatePost();
|
|
@@ -4907,16 +5003,21 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4907
5003
|
}
|
|
4908
5004
|
|
|
4909
5005
|
// setup html
|
|
4910
|
-
const
|
|
5006
|
+
const styleRoot =
|
|
4911
5007
|
'margin:0;overflow:hidden;' + // fill the window
|
|
5008
|
+
'width:100vw;height:100vh;' + // fill the window
|
|
5009
|
+
'display:flex;' + // use flexbox
|
|
5010
|
+
'align-items:center;' + // horizontal center
|
|
5011
|
+
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
5012
|
+
'justify-content:center;' + // vertical center
|
|
4912
5013
|
'background:#000;' + // set background color
|
|
4913
5014
|
'user-select:none;' + // prevent hold to select
|
|
4914
5015
|
'-webkit-user-select:none;' + // compatibility for ios
|
|
4915
5016
|
(!touchInputEnable ? '' : // no touch css setttings
|
|
4916
5017
|
'touch-action:none;' + // prevent mobile pinch to resize
|
|
4917
5018
|
'-webkit-touch-callout:none');// compatibility for ios
|
|
4918
|
-
|
|
4919
|
-
|
|
5019
|
+
rootElement.style.cssText = styleRoot;
|
|
5020
|
+
rootElement.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4920
5021
|
mainContext = mainCanvas.getContext('2d');
|
|
4921
5022
|
|
|
4922
5023
|
// init stuff and start engine
|
|
@@ -4926,13 +5027,14 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4926
5027
|
glInit();
|
|
4927
5028
|
|
|
4928
5029
|
// create overlay canvas for hud to appear above gl canvas
|
|
4929
|
-
|
|
5030
|
+
rootElement.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4930
5031
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4931
5032
|
|
|
4932
5033
|
// set canvas style
|
|
4933
|
-
const styleCanvas = 'position:absolute;
|
|
4934
|
-
|
|
4935
|
-
(glCanvas
|
|
5034
|
+
const styleCanvas = 'position:absolute'; // allow canvases to overlap
|
|
5035
|
+
mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
5036
|
+
if (glCanvas)
|
|
5037
|
+
glCanvas.style.cssText = styleCanvas;
|
|
4936
5038
|
updateCanvas();
|
|
4937
5039
|
|
|
4938
5040
|
// create promises for loading images
|
|
@@ -4949,19 +5051,32 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4949
5051
|
})
|
|
4950
5052
|
);
|
|
4951
5053
|
|
|
4952
|
-
|
|
4953
|
-
showSplashScreen && promises.push(new Promise(resolve =>
|
|
5054
|
+
if (!imageSources.length)
|
|
4954
5055
|
{
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
updateSplash();
|
|
4958
|
-
function updateSplash()
|
|
5056
|
+
// no images to load
|
|
5057
|
+
promises.push(new Promise(resolve =>
|
|
4959
5058
|
{
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
5059
|
+
textureInfos[0] = new TextureInfo(new Image);
|
|
5060
|
+
resolve();
|
|
5061
|
+
}));
|
|
5062
|
+
}
|
|
5063
|
+
|
|
5064
|
+
if (showSplashScreen)
|
|
5065
|
+
{
|
|
5066
|
+
// draw splash screen
|
|
5067
|
+
promises.push(new Promise(resolve =>
|
|
5068
|
+
{
|
|
5069
|
+
let t = 0;
|
|
5070
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
5071
|
+
updateSplash();
|
|
5072
|
+
function updateSplash()
|
|
5073
|
+
{
|
|
5074
|
+
clearInput();
|
|
5075
|
+
drawEngineSplashScreen(t+=.01);
|
|
5076
|
+
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
5077
|
+
}
|
|
5078
|
+
}));
|
|
5079
|
+
}
|
|
4965
5080
|
|
|
4966
5081
|
// load all of the images
|
|
4967
5082
|
Promise.all(promises).then(startEngine);
|
package/examples/box2d/game.js
CHANGED
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
setShowSplashScreen(true);
|
|
16
16
|
//box2dDebug = 1; // enable box2d debug draw
|
|
17
17
|
|
|
18
|
+
// fix texture bleeding by shrinking tile slightly
|
|
19
|
+
tileFixBleedScale = .2;
|
|
20
|
+
|
|
18
21
|
// game variables
|
|
19
22
|
const maxScenes = 11;
|
|
20
23
|
let scene = 0;
|
|
@@ -153,4 +156,4 @@ function gameRenderPost()
|
|
|
153
156
|
///////////////////////////////////////////////////////////////////////////////
|
|
154
157
|
// Startup LittleJS Engine with Box2D
|
|
155
158
|
|
|
156
|
-
box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost);
|
|
159
|
+
box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, ['tiles.png']);
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<!DOCTYPE html><head>
|
|
2
|
-
<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?
|
|
10
|
-
<script src=../../plugins/box2d.js?
|
|
11
|
-
<script src=scenes.js?
|
|
12
|
-
<script src=gameObjects.js?
|
|
13
|
-
<script src=game.js?
|
|
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>
|
|
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?
|
|
9
|
+
<script src=../../dist/littlejs.js?1100></script>
|
|
9
10
|
<script src=../../plugins/postProcess.js></script>
|
|
10
|
-
<script src=gameObjects.js?
|
|
11
|
-
<script src=game.js?
|
|
11
|
+
<script src=gameObjects.js?1100></script>
|
|
12
|
+
<script src=game.js?1100></script>
|
|
@@ -53,6 +53,8 @@ for(let y=0; y<=levelSize.y; y+=1)
|
|
|
53
53
|
## Move the Camera
|
|
54
54
|
|
|
55
55
|
Let’s move the camera to the center of our level by setting the cameraPos variable to half the level size.
|
|
56
|
+
You can also change the camera zoom by using camaraScale which controls the number of pixels for each world space unit.
|
|
57
|
+
For this example the camera will just remain stationary, but in other types of games it is very useful!
|
|
56
58
|
|
|
57
59
|
```javascript
|
|
58
60
|
setCameraPos(levelSize.scale(.5)); // center camera in level
|
|
@@ -307,7 +309,7 @@ collideWithObject(o)
|
|
|
307
309
|
But wait! This will cause the ball to careen through the bricks without bouncing. Maybe fun for a special powerup, but not our goal here. This function should return a Boolean value to indicate if the collision needs to be resolved, if it returns false then the ball will not bounce. So the answer is to just add another line of code to the collideWithObject function that returns a truthy value indicating that the collision should occur.
|
|
308
310
|
|
|
309
311
|
```javascript
|
|
310
|
-
return
|
|
312
|
+
return true; // allow object to collide
|
|
311
313
|
```
|
|
312
314
|
|
|
313
315
|

|
|
@@ -356,7 +358,7 @@ To play the sound we will override collideWithObject for the Ball class. Also re
|
|
|
356
358
|
collideWithObject(o)
|
|
357
359
|
{
|
|
358
360
|
sound_bounce.play(); // play bounce sound
|
|
359
|
-
return
|
|
361
|
+
return true; // allow object to collide
|
|
360
362
|
}
|
|
361
363
|
```
|
|
362
364
|
Let’s also make a brick break sound. You can use the ZzFX sound designer again to create your own sound, for this one try the Hit preset.
|
|
@@ -422,12 +424,12 @@ const color = this.color;
|
|
|
422
424
|
new ParticleEmitter(
|
|
423
425
|
this.pos, 0, // pos, angle
|
|
424
426
|
this.size, .1, 200, PI, // emitSize, emitTime, emitRate, emiteCone
|
|
425
|
-
|
|
427
|
+
undefined, // tileInfo
|
|
426
428
|
color, color, // colorStartA, colorStartB
|
|
427
429
|
color.scale(1,0), color.scale(1,0), // colorEndA, colorEndB
|
|
428
430
|
.2, .5, 1, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
|
|
429
431
|
.99, .95, .4, PI, // damping, angleDamping, gravityScale, cone
|
|
430
|
-
.1, .5,
|
|
432
|
+
.1, .5, false, true // fadeRate, randomness, collide, additive
|
|
431
433
|
);
|
|
432
434
|
```
|
|
433
435
|
|
|
@@ -446,7 +448,7 @@ collideWithObject(o)
|
|
|
446
448
|
{
|
|
447
449
|
// prevent colliding with paddle if moving upwards
|
|
448
450
|
if (o == paddle && this.velocity.y > 0)
|
|
449
|
-
return
|
|
451
|
+
return false;
|
|
450
452
|
|
|
451
453
|
sound_bounce.play(); // play bounce sound
|
|
452
454
|
|
|
@@ -460,10 +462,10 @@ collideWithObject(o)
|
|
|
460
462
|
this.velocity.y = max(-this.velocity.y, .2);
|
|
461
463
|
|
|
462
464
|
// prevent default collision code
|
|
463
|
-
return
|
|
465
|
+
return false;
|
|
464
466
|
}
|
|
465
467
|
|
|
466
|
-
return
|
|
468
|
+
return true; // allow object to collide
|
|
467
469
|
}
|
|
468
470
|
```
|
|
469
471
|
|