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.
- package/README.md +4 -0
- package/dist/littlejs.d.ts +80 -9
- package/dist/littlejs.esm.js +193 -71
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +175 -69
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +174 -68
- package/examples/box2d/game.js +1 -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/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/game.js +1 -1
- 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 +1 -1
- package/examples/platformer/gameLevel.js +2 -2
- package/examples/platformer/gameObjects.js +0 -1
- package/examples/platformer/index.html +10 -9
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +4 -3
- package/examples/starter/game.js +4 -4
- package/examples/starter/index.html +15 -14
- package/examples/stress/index.html +3 -2
- package/examples/typescript/game.js +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 +6 -7
- package/plugins/uiSystem.js +243 -0
- package/src/engine.js +48 -20
- package/src/engineAudio.js +10 -13
- package/src/engineDebug.js +1 -1
- package/src/engineDraw.js +101 -27
- package/src/engineExport.js +18 -2
- package/src/engineInput.js +2 -2
- package/src/engineSettings.js +1 -1
- package/src/engineUtilities.js +1 -1
- package/src/engineWebGL.js +11 -4
package/dist/littlejs.release.js
CHANGED
|
@@ -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 =
|
|
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=
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|
2077
|
-
const
|
|
2078
|
-
const
|
|
2079
|
-
const
|
|
2080
|
-
const
|
|
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 (
|
|
2345
|
-
|
|
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 !=
|
|
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 !=
|
|
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
|
|
3214
|
-
|
|
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 &&
|
|
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
|
-
|
|
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.
|
|
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=[
|
|
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
|
|
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
|
-
|
|
4919
|
-
|
|
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
|
-
|
|
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;
|
|
4934
|
-
|
|
4935
|
-
(glCanvas
|
|
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
|
-
|
|
4953
|
-
showSplashScreen && promises.push(new Promise(resolve =>
|
|
5045
|
+
if (!imageSources.length)
|
|
4954
5046
|
{
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
updateSplash();
|
|
4958
|
-
function updateSplash()
|
|
5047
|
+
// no images to load
|
|
5048
|
+
promises.push(new Promise(resolve =>
|
|
4959
5049
|
{
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
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);
|
package/examples/box2d/game.js
CHANGED
|
@@ -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>
|
|
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>
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
<!DOCTYPE html><head>
|
|
2
|
-
<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?
|
|
9
|
-
<script src=game.js?
|
|
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>
|
|
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?
|
|
10
|
+
<script src=../../dist/littlejs.js?1100></script>
|
|
10
11
|
|
|
11
12
|
<!-- Add your game scripts here -->
|
|
12
|
-
<script src=game.js?
|
|
13
|
+
<script src=game.js?1100></script>
|
package/examples/empty/game.js
CHANGED
|
@@ -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']);
|
|
@@ -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']);
|