littlejsengine 1.9.2 → 1.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/dist/littlejs.d.ts +31 -26
- package/dist/littlejs.esm.js +145 -122
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +145 -122
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +140 -121
- package/examples/logo.png +0 -0
- package/examples/module/game.js +1 -1
- package/examples/platformer/data/{gameTileData.tmx → gameLevelData.tmx} +3 -3
- package/examples/platformer/game.js +31 -10
- package/examples/platformer/gameCharacter.js +7 -6
- package/examples/platformer/gameEffects.js +77 -68
- package/examples/platformer/gameLevel.js +33 -17
- package/examples/platformer/{gameTileData.js → gameLevelData.js} +3 -3
- package/examples/platformer/gameObjects.js +14 -14
- package/examples/platformer/index.html +1 -1
- package/examples/platformer/tiles.png +0 -0
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +1 -1
- package/examples/typescript/game.js +1 -1
- package/examples/typescript/game.ts +1 -1
- package/package.json +1 -1
- package/reference.md +409 -0
- package/src/engine.js +47 -42
- package/src/engineAudio.js +19 -10
- package/src/engineDebug.js +5 -1
- package/src/engineDraw.js +19 -23
- package/src/engineInput.js +31 -17
- package/src/engineMedals.js +1 -1
- package/src/engineObject.js +2 -2
- package/src/engineParticles.js +11 -10
- package/src/engineSettings.js +3 -3
- package/src/engineTileLayer.js +2 -2
- package/src/engineUtilities.js +4 -4
- package/src/engineWebGL.js +1 -7
- /package/examples/platformer/data/{gameTileData.tsx → gameLevelData.tsx} +0 -0
package/dist/littlejs.esm.js
CHANGED
|
@@ -56,7 +56,7 @@ let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParti
|
|
|
56
56
|
///////////////////////////////////////////////////////////////////////////////
|
|
57
57
|
// Debug helper functions
|
|
58
58
|
|
|
59
|
-
/** Asserts if the
|
|
59
|
+
/** Asserts if the expression is false, does not do anything in release builds
|
|
60
60
|
* @param {Boolean} assert
|
|
61
61
|
* @param {Object} [output]
|
|
62
62
|
* @memberof Debug */
|
|
@@ -264,6 +264,10 @@ function debugRender()
|
|
|
264
264
|
{
|
|
265
265
|
const saveContext = mainContext;
|
|
266
266
|
mainContext = overlayContext;
|
|
267
|
+
|
|
268
|
+
// draw red rectangle around screen
|
|
269
|
+
const cameraSize = getCameraSize();
|
|
270
|
+
debugRect(cameraPos, cameraSize.subtract(vec2(.1)), '#f008');
|
|
267
271
|
|
|
268
272
|
// mouse pick
|
|
269
273
|
let bestDistance = Infinity, bestObject;
|
|
@@ -453,7 +457,7 @@ function min(valueA, valueB) { return Math.min(valueA, valueB); }
|
|
|
453
457
|
* @memberof Utilities */
|
|
454
458
|
function max(valueA, valueB) { return Math.max(valueA, valueB); }
|
|
455
459
|
|
|
456
|
-
/** Returns the sign of value passed in
|
|
460
|
+
/** Returns the sign of value passed in
|
|
457
461
|
* @param {Number} value
|
|
458
462
|
* @return {Number}
|
|
459
463
|
* @memberof Utilities */
|
|
@@ -500,7 +504,7 @@ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (value
|
|
|
500
504
|
function distanceWrap(valueA, valueB, wrapSize=1)
|
|
501
505
|
{ const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
|
|
502
506
|
|
|
503
|
-
/** Linearly interpolates between values passed in with
|
|
507
|
+
/** Linearly interpolates between values passed in with wrapping
|
|
504
508
|
* @param {Number} percent
|
|
505
509
|
* @param {Number} valueA
|
|
506
510
|
* @param {Number} valueB
|
|
@@ -517,7 +521,7 @@ function lerpWrap(percent, valueA, valueB, wrapSize=1)
|
|
|
517
521
|
* @memberof Utilities */
|
|
518
522
|
function distanceAngle(angleA, angleB) { return distanceWrap(angleA, angleB, 2*PI); }
|
|
519
523
|
|
|
520
|
-
/** Linearly interpolates between the angles passed in with
|
|
524
|
+
/** Linearly interpolates between the angles passed in with wrapping
|
|
521
525
|
* @param {Number} percent
|
|
522
526
|
* @param {Number} angleA
|
|
523
527
|
* @param {Number} angleB
|
|
@@ -1047,7 +1051,7 @@ class Color
|
|
|
1047
1051
|
|
|
1048
1052
|
/** Returns this color expressed in hsla format
|
|
1049
1053
|
* @return {Array} */
|
|
1050
|
-
|
|
1054
|
+
HSLA()
|
|
1051
1055
|
{
|
|
1052
1056
|
const r = clamp(this.r);
|
|
1053
1057
|
const g = clamp(this.g);
|
|
@@ -1259,7 +1263,7 @@ let tileSizeDefault = vec2(16);
|
|
|
1259
1263
|
* @type {Number}
|
|
1260
1264
|
* @default
|
|
1261
1265
|
* @memberof Settings */
|
|
1262
|
-
let tileFixBleedScale = .
|
|
1266
|
+
let tileFixBleedScale = .1;
|
|
1263
1267
|
|
|
1264
1268
|
///////////////////////////////////////////////////////////////////////////////
|
|
1265
1269
|
// Object settings
|
|
@@ -1270,7 +1274,7 @@ let tileFixBleedScale = .3;
|
|
|
1270
1274
|
* @memberof Settings */
|
|
1271
1275
|
let enablePhysicsSolver = true;
|
|
1272
1276
|
|
|
1273
|
-
/** Default object mass for
|
|
1277
|
+
/** Default object mass for collision calcuations (how heavy objects are)
|
|
1274
1278
|
* @type {Number}
|
|
1275
1279
|
* @default
|
|
1276
1280
|
* @memberof Settings */
|
|
@@ -1353,7 +1357,7 @@ let touchGamepadEnable = false;
|
|
|
1353
1357
|
* @memberof Settings */
|
|
1354
1358
|
let touchGamepadAnalog = true;
|
|
1355
1359
|
|
|
1356
|
-
/** Size of
|
|
1360
|
+
/** Size of virtual gamepad for touch devices in pixels
|
|
1357
1361
|
* @type {Number}
|
|
1358
1362
|
* @default
|
|
1359
1363
|
* @memberof Settings */
|
|
@@ -1640,7 +1644,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
1640
1644
|
* - Automatically adds self to object list
|
|
1641
1645
|
* - Will be updated and rendered each frame
|
|
1642
1646
|
* - Renders as a sprite from a tilesheet by default
|
|
1643
|
-
* - Can have color and
|
|
1647
|
+
* - Can have color and additive color applied
|
|
1644
1648
|
* - 2D Physics and collision system
|
|
1645
1649
|
* - Sorted by renderOrder
|
|
1646
1650
|
* - Objects can have children attached
|
|
@@ -1919,7 +1923,7 @@ class EngineObject
|
|
|
1919
1923
|
}
|
|
1920
1924
|
|
|
1921
1925
|
/** Destroy this object, destroy it's children, detach it's parent, and mark it for removal */
|
|
1922
|
-
destroy()
|
|
1926
|
+
destroy()
|
|
1923
1927
|
{
|
|
1924
1928
|
if (this.destroyed)
|
|
1925
1929
|
return;
|
|
@@ -2108,13 +2112,9 @@ function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
|
2108
2112
|
if (typeof pos === 'number')
|
|
2109
2113
|
{
|
|
2110
2114
|
const textureInfo = textureInfos[textureIndex];
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
|
|
2115
|
-
}
|
|
2116
|
-
else
|
|
2117
|
-
pos = vec2();
|
|
2115
|
+
ASSERT(textureInfo, 'Texture not loaded');
|
|
2116
|
+
const cols = textureInfo.size.x / size.x |0;
|
|
2117
|
+
pos = vec2((pos%cols)*size.x, (pos/cols|0)*size.y);
|
|
2118
2118
|
}
|
|
2119
2119
|
|
|
2120
2120
|
// return a tile info object
|
|
@@ -2141,13 +2141,23 @@ class TileInfo
|
|
|
2141
2141
|
this.textureIndex = textureIndex;
|
|
2142
2142
|
}
|
|
2143
2143
|
|
|
2144
|
-
/** Returns
|
|
2144
|
+
/** Returns a copy of this tile offset by a vector
|
|
2145
2145
|
* @param {Vector2} offset - Offset to apply in pixels
|
|
2146
2146
|
* @return {TileInfo}
|
|
2147
2147
|
*/
|
|
2148
2148
|
offset(offset)
|
|
2149
2149
|
{ return new TileInfo(this.pos.add(offset), this.size, this.textureIndex); }
|
|
2150
2150
|
|
|
2151
|
+
/** Returns a copy of this tile offset by a number of animation frames
|
|
2152
|
+
* @param {Number} frame - Offset to apply in animation frames
|
|
2153
|
+
* @return {TileInfo}
|
|
2154
|
+
*/
|
|
2155
|
+
frame(frame)
|
|
2156
|
+
{
|
|
2157
|
+
ASSERT(typeof frame == 'number');
|
|
2158
|
+
return this.offset(vec2(frame*this.size.x, 0));
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2151
2161
|
/** Returns the texture info for this tile
|
|
2152
2162
|
* @return {TextureInfo}
|
|
2153
2163
|
*/
|
|
@@ -2203,6 +2213,11 @@ function worldToScreen(worldPos)
|
|
|
2203
2213
|
);
|
|
2204
2214
|
}
|
|
2205
2215
|
|
|
2216
|
+
/** Get the camera's visible area in world space
|
|
2217
|
+
* @return {Vector2}
|
|
2218
|
+
* @memberof Draw */
|
|
2219
|
+
function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
2220
|
+
|
|
2206
2221
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
2207
2222
|
* @param {Vector2} pos - Center of the tile in world space
|
|
2208
2223
|
* @param {Vector2} [size=(1,1)] - Size of the tile in world space
|
|
@@ -2293,21 +2308,6 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
2293
2308
|
drawTile(pos, size, undefined, color, angle, false, undefined, useWebGL, screenSpace, context);
|
|
2294
2309
|
}
|
|
2295
2310
|
|
|
2296
|
-
/** Draw colored polygon using passed in points
|
|
2297
|
-
* @param {Array} points - Array of Vector2 points
|
|
2298
|
-
* @param {Color} [color=(1,1,1,1)]
|
|
2299
|
-
* @param {Boolean} [screenSpace=false]
|
|
2300
|
-
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
2301
|
-
* @memberof Draw */
|
|
2302
|
-
function drawPoly(points, color=new Color, screenSpace, context=mainContext)
|
|
2303
|
-
{
|
|
2304
|
-
context.fillStyle = color.toString();
|
|
2305
|
-
context.beginPath();
|
|
2306
|
-
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
2307
|
-
context.lineTo(point.x, point.y);
|
|
2308
|
-
context.fill();
|
|
2309
|
-
}
|
|
2310
|
-
|
|
2311
2311
|
/** Draw colored line between two points
|
|
2312
2312
|
* @param {Vector2} posA
|
|
2313
2313
|
* @param {Vector2} posB
|
|
@@ -2734,9 +2734,21 @@ function mouseToScreen(mousePos)
|
|
|
2734
2734
|
///////////////////////////////////////////////////////////////////////////////
|
|
2735
2735
|
// Gamepad input
|
|
2736
2736
|
|
|
2737
|
+
// gamepad internal variables
|
|
2737
2738
|
const stickData = [];
|
|
2739
|
+
|
|
2740
|
+
// gamepads are updated by engine every frame automatically
|
|
2738
2741
|
function gamepadsUpdate()
|
|
2739
2742
|
{
|
|
2743
|
+
const applyDeadZones = (v)=>
|
|
2744
|
+
{
|
|
2745
|
+
const min=.3, max=.8;
|
|
2746
|
+
const deadZone = (v)=>
|
|
2747
|
+
v > min ? percent( v, min, max) :
|
|
2748
|
+
v < -min ? -percent(-v, min, max) : 0;
|
|
2749
|
+
return vec2(deadZone(v.x), deadZone(-v.y)).clampLength();
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2740
2752
|
// update touch gamepad if enabled
|
|
2741
2753
|
if (touchGamepadEnable && isTouchDevice)
|
|
2742
2754
|
{
|
|
@@ -2748,7 +2760,16 @@ function gamepadsUpdate()
|
|
|
2748
2760
|
{
|
|
2749
2761
|
// read virtual analog stick
|
|
2750
2762
|
const sticks = stickData[0] || (stickData[0] = []);
|
|
2751
|
-
sticks[0] = vec2(
|
|
2763
|
+
sticks[0] = vec2();
|
|
2764
|
+
if (touchGamepadAnalog)
|
|
2765
|
+
sticks[0] = applyDeadZones(touchGamepadStick);
|
|
2766
|
+
else if (touchGamepadStick.lengthSquared() > .3)
|
|
2767
|
+
{
|
|
2768
|
+
// convert to 8 way dpad
|
|
2769
|
+
sticks[0].x = Math.round(touchGamepadStick.x);
|
|
2770
|
+
sticks[0].y = -Math.round(touchGamepadStick.y);
|
|
2771
|
+
sticks[0] = sticks[0].clampLength();
|
|
2772
|
+
}
|
|
2752
2773
|
|
|
2753
2774
|
// read virtual gamepad buttons
|
|
2754
2775
|
const data = inputData[1] || (inputData[1] = []);
|
|
@@ -2760,7 +2781,12 @@ function gamepadsUpdate()
|
|
|
2760
2781
|
}
|
|
2761
2782
|
}
|
|
2762
2783
|
|
|
2763
|
-
|
|
2784
|
+
// return if gamepads are disabled or not supported
|
|
2785
|
+
if (!gamepadsEnable || !navigator || !navigator.getGamepads)
|
|
2786
|
+
return;
|
|
2787
|
+
|
|
2788
|
+
// only poll gamepads when focused or in debug mode
|
|
2789
|
+
if (!debug && !document.hasFocus())
|
|
2764
2790
|
return;
|
|
2765
2791
|
|
|
2766
2792
|
// poll gamepads
|
|
@@ -2774,14 +2800,9 @@ function gamepadsUpdate()
|
|
|
2774
2800
|
|
|
2775
2801
|
if (gamepad)
|
|
2776
2802
|
{
|
|
2777
|
-
// read clamp dead zone of analog sticks
|
|
2778
|
-
const deadZone = .3, deadZoneMax = .8, applyDeadZone = (v)=>
|
|
2779
|
-
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2780
|
-
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
2781
|
-
|
|
2782
2803
|
// read analog sticks
|
|
2783
2804
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
2784
|
-
sticks[j>>1] = vec2(
|
|
2805
|
+
sticks[j>>1] = applyDeadZones(vec2(gamepad.axes[j],gamepad.axes[j+1]));
|
|
2785
2806
|
|
|
2786
2807
|
// read buttons
|
|
2787
2808
|
for (let j = gamepad.buttons.length; j--;)
|
|
@@ -2880,7 +2901,7 @@ function createTouchGamepad()
|
|
|
2880
2901
|
touchGamepadStick = vec2();
|
|
2881
2902
|
|
|
2882
2903
|
const touchHandler = ontouchstart;
|
|
2883
|
-
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2904
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
2884
2905
|
{
|
|
2885
2906
|
// clear touch gamepad input
|
|
2886
2907
|
touchGamepadStick = vec2();
|
|
@@ -2910,14 +2931,7 @@ function createTouchGamepad()
|
|
|
2910
2931
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2911
2932
|
{
|
|
2912
2933
|
// virtual analog stick
|
|
2913
|
-
|
|
2914
|
-
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2915
|
-
else
|
|
2916
|
-
{
|
|
2917
|
-
// 8 way dpad
|
|
2918
|
-
const angle = touchPos.subtract(stickCenter).angle();
|
|
2919
|
-
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2920
|
-
}
|
|
2934
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2921
2935
|
}
|
|
2922
2936
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2923
2937
|
{
|
|
@@ -3010,7 +3024,7 @@ function touchGamepadRender()
|
|
|
3010
3024
|
|
|
3011
3025
|
|
|
3012
3026
|
/**
|
|
3013
|
-
* Sound Object - Stores a
|
|
3027
|
+
* Sound Object - Stores a sound for later use and can be played positionally
|
|
3014
3028
|
*
|
|
3015
3029
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
3016
3030
|
* @example
|
|
@@ -3025,7 +3039,7 @@ class Sound
|
|
|
3025
3039
|
/** Create a sound object and cache the zzfx samples for later use
|
|
3026
3040
|
* @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
|
|
3027
3041
|
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
3028
|
-
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
3042
|
+
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
3029
3043
|
*/
|
|
3030
3044
|
constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
|
|
3031
3045
|
{
|
|
@@ -3176,7 +3190,7 @@ class SoundWave extends Sound
|
|
|
3176
3190
|
* 1, 0, 9, 1 // channel notes
|
|
3177
3191
|
* ],
|
|
3178
3192
|
* [ // channel 1
|
|
3179
|
-
* 0, 1, // instrument
|
|
3193
|
+
* 0, 1, // instrument 0, right speaker
|
|
3180
3194
|
* 0, 12, 17, -1 // channel notes
|
|
3181
3195
|
* ]
|
|
3182
3196
|
* ],
|
|
@@ -3205,24 +3219,24 @@ class Music extends Sound
|
|
|
3205
3219
|
|
|
3206
3220
|
/** Play the music
|
|
3207
3221
|
* @param {Number} [volume=1] - How much to scale volume by
|
|
3208
|
-
* @param {Boolean} [loop
|
|
3222
|
+
* @param {Boolean} [loop] - True if the music should loop
|
|
3209
3223
|
* @return {AudioBufferSourceNode} - The audio source node
|
|
3210
3224
|
*/
|
|
3211
|
-
playMusic(volume, loop
|
|
3225
|
+
playMusic(volume, loop=false)
|
|
3212
3226
|
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
3213
3227
|
}
|
|
3214
3228
|
|
|
3215
3229
|
/** Play an mp3, ogg, or wav audio from a local file or url
|
|
3216
|
-
* @param {String}
|
|
3230
|
+
* @param {String} filename - Location of sound file to play
|
|
3217
3231
|
* @param {Number} [volume] - How much to scale volume by
|
|
3218
3232
|
* @param {Boolean} [loop] - True if the music should loop
|
|
3219
3233
|
* @return {HTMLAudioElement} - The audio element for this sound
|
|
3220
3234
|
* @memberof Audio */
|
|
3221
|
-
function playAudioFile(
|
|
3235
|
+
function playAudioFile(filename, volume=1, loop=false)
|
|
3222
3236
|
{
|
|
3223
3237
|
if (!soundEnable) return;
|
|
3224
3238
|
|
|
3225
|
-
const audio = new Audio(
|
|
3239
|
+
const audio = new Audio(filename);
|
|
3226
3240
|
audio.volume = soundVolume * volume;
|
|
3227
3241
|
audio.loop = loop;
|
|
3228
3242
|
audio.play();
|
|
@@ -3274,6 +3288,11 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
3274
3288
|
* @memberof Audio */
|
|
3275
3289
|
let audioContext = new AudioContext;
|
|
3276
3290
|
|
|
3291
|
+
/** Keep track if audio was suspended when last sound was played
|
|
3292
|
+
* @type {Boolean}
|
|
3293
|
+
* @memberof Audio */
|
|
3294
|
+
let audioSuspended = false;
|
|
3295
|
+
|
|
3277
3296
|
/** Play cached audio samples with given settings
|
|
3278
3297
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
3279
3298
|
* @param {Number} [volume] - How much to scale volume by
|
|
@@ -3288,11 +3307,15 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
3288
3307
|
if (!soundEnable) return;
|
|
3289
3308
|
|
|
3290
3309
|
// prevent sounds from building up if they can't be played
|
|
3291
|
-
|
|
3310
|
+
const audioWasSuspended = audioSuspended;
|
|
3311
|
+
if (audioSuspended = audioContext.state != 'running')
|
|
3292
3312
|
{
|
|
3293
3313
|
// fix stalled audio
|
|
3294
3314
|
audioContext.resume();
|
|
3295
|
-
|
|
3315
|
+
|
|
3316
|
+
// prevent suspended sounds from building up
|
|
3317
|
+
if (audioWasSuspended)
|
|
3318
|
+
return;
|
|
3296
3319
|
}
|
|
3297
3320
|
|
|
3298
3321
|
// create buffer and source
|
|
@@ -3560,7 +3583,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
3560
3583
|
/**
|
|
3561
3584
|
* LittleJS Tile Layer System
|
|
3562
3585
|
* - Caches arrays of tiles to off screen canvas for fast rendering
|
|
3563
|
-
* -
|
|
3586
|
+
* - Unlimited numbers of layers, allocates canvases as needed
|
|
3564
3587
|
* - Interfaces with EngineObject for collision
|
|
3565
3588
|
* - Collision layer is separate from visible layers
|
|
3566
3589
|
* - It is recommended to have a visible layer that matches the collision
|
|
@@ -3612,7 +3635,7 @@ function getTileCollisionData(pos)
|
|
|
3612
3635
|
|
|
3613
3636
|
/** Check if collision with another object should occur
|
|
3614
3637
|
* @param {Vector2} pos
|
|
3615
|
-
* @param {Vector2} [size=(
|
|
3638
|
+
* @param {Vector2} [size=(0,0)]
|
|
3616
3639
|
* @param {EngineObject} [object]
|
|
3617
3640
|
* @return {Boolean}
|
|
3618
3641
|
* @memberof TileCollision */
|
|
@@ -4129,16 +4152,17 @@ class ParticleEmitter extends EngineObject
|
|
|
4129
4152
|
|
|
4130
4153
|
// build particle
|
|
4131
4154
|
const particle = new Particle(pos, this.tileInfo, angle, colorStart, colorEnd, particleTime, sizeStart, sizeEnd, this.fadeRate, this.additive, this.trailScale, this.localSpace && this, this.particleDestroyCallback);
|
|
4132
|
-
particle.velocity
|
|
4133
|
-
particle.
|
|
4134
|
-
particle.
|
|
4135
|
-
particle.
|
|
4136
|
-
particle.
|
|
4137
|
-
particle.
|
|
4138
|
-
particle.
|
|
4139
|
-
particle.
|
|
4140
|
-
particle.
|
|
4141
|
-
particle.
|
|
4155
|
+
particle.velocity = vec2().setAngle(velocityAngle, speed);
|
|
4156
|
+
particle.angleVelocity = angleSpeed;
|
|
4157
|
+
particle.fadeRate = this.fadeRate;
|
|
4158
|
+
particle.damping = this.damping;
|
|
4159
|
+
particle.angleDamping = this.angleDamping;
|
|
4160
|
+
particle.elasticity = this.elasticity;
|
|
4161
|
+
particle.friction = this.friction;
|
|
4162
|
+
particle.gravityScale = this.gravityScale;
|
|
4163
|
+
particle.collideTiles = this.collideTiles;
|
|
4164
|
+
particle.renderOrder = this.renderOrder;
|
|
4165
|
+
particle.mirror = !!randInt(2);
|
|
4142
4166
|
|
|
4143
4167
|
// call particle create callaback
|
|
4144
4168
|
this.particleCreateCallback && this.particleCreateCallback(particle);
|
|
@@ -4529,7 +4553,7 @@ class Newgrounds
|
|
|
4529
4553
|
}
|
|
4530
4554
|
|
|
4531
4555
|
// build the input object
|
|
4532
|
-
const input =
|
|
4556
|
+
const input =
|
|
4533
4557
|
{
|
|
4534
4558
|
'app_id': this.app_id,
|
|
4535
4559
|
'session_id': this.session_id,
|
|
@@ -4744,8 +4768,6 @@ function glCreateTexture(image)
|
|
|
4744
4768
|
const filter = canvasPixelated ? gl_NEAREST : gl_LINEAR;
|
|
4745
4769
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MIN_FILTER, filter);
|
|
4746
4770
|
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_MAG_FILTER, filter);
|
|
4747
|
-
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_S, gl_CLAMP_TO_EDGE);
|
|
4748
|
-
glContext.texParameteri(gl_TEXTURE_2D, gl_TEXTURE_WRAP_T, gl_CLAMP_TO_EDGE);
|
|
4749
4771
|
|
|
4750
4772
|
return texture;
|
|
4751
4773
|
}
|
|
@@ -4823,7 +4845,7 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdd
|
|
|
4823
4845
|
///////////////////////////////////////////////////////////////////////////////
|
|
4824
4846
|
// post processing - can be enabled to pass other canvases through a final shader
|
|
4825
4847
|
|
|
4826
|
-
let glPostShader,
|
|
4848
|
+
let glPostShader, glPostTexture, glPostIncludeOverlay;
|
|
4827
4849
|
|
|
4828
4850
|
/** Set up a post processing shader
|
|
4829
4851
|
* @param {String} shaderCode
|
|
@@ -4859,7 +4881,6 @@ function glInitPostProcess(shaderCode, includeOverlay=false)
|
|
|
4859
4881
|
);
|
|
4860
4882
|
|
|
4861
4883
|
// create buffer and texture
|
|
4862
|
-
glPostArrayBuffer = glContext.createBuffer();
|
|
4863
4884
|
glPostTexture = glCreateTexture(undefined);
|
|
4864
4885
|
glPostIncludeOverlay = includeOverlay;
|
|
4865
4886
|
|
|
@@ -4931,10 +4952,7 @@ gl_NEAREST = 9728,
|
|
|
4931
4952
|
gl_LINEAR = 9729,
|
|
4932
4953
|
gl_TEXTURE_MAG_FILTER = 10240,
|
|
4933
4954
|
gl_TEXTURE_MIN_FILTER = 10241,
|
|
4934
|
-
gl_TEXTURE_WRAP_S = 10242,
|
|
4935
|
-
gl_TEXTURE_WRAP_T = 10243,
|
|
4936
4955
|
gl_COLOR_BUFFER_BIT = 16384,
|
|
4937
|
-
gl_CLAMP_TO_EDGE = 33071,
|
|
4938
4956
|
gl_TEXTURE0 = 33984,
|
|
4939
4957
|
gl_ARRAY_BUFFER = 34962,
|
|
4940
4958
|
gl_STATIC_DRAW = 35044,
|
|
@@ -4951,7 +4969,7 @@ gl_MAX_INSTANCES = 1e4,
|
|
|
4951
4969
|
gl_INSTANCE_BYTE_STRIDE = gl_INDICIES_PER_INSTANCE * 4, // 11 * 4
|
|
4952
4970
|
gl_INSTANCE_BUFFER_SIZE = gl_MAX_INSTANCES * gl_INSTANCE_BYTE_STRIDE;
|
|
4953
4971
|
/**
|
|
4954
|
-
* LittleJS - The Tiny JavaScript Game Engine
|
|
4972
|
+
* LittleJS - The Tiny Fast JavaScript Game Engine
|
|
4955
4973
|
* MIT License - Copyright 2021 Frank Force
|
|
4956
4974
|
*
|
|
4957
4975
|
* Engine Features
|
|
@@ -4982,9 +5000,9 @@ const engineName = 'LittleJS';
|
|
|
4982
5000
|
* @type {String}
|
|
4983
5001
|
* @default
|
|
4984
5002
|
* @memberof Engine */
|
|
4985
|
-
const engineVersion = '1.9.
|
|
5003
|
+
const engineVersion = '1.9.4';
|
|
4986
5004
|
|
|
4987
|
-
/** Frames per second to update
|
|
5005
|
+
/** Frames per second to update
|
|
4988
5006
|
* @type {Number}
|
|
4989
5007
|
* @default
|
|
4990
5008
|
* @memberof Engine */
|
|
@@ -5001,7 +5019,7 @@ const timeDelta = 1/frameRate;
|
|
|
5001
5019
|
* @memberof Engine */
|
|
5002
5020
|
let engineObjects = [];
|
|
5003
5021
|
|
|
5004
|
-
/** Array
|
|
5022
|
+
/** Array with only objects set to collide with other objects this frame (for optimization)
|
|
5005
5023
|
* @type {Array}
|
|
5006
5024
|
* @memberof Engine */
|
|
5007
5025
|
let engineObjectsCollide = [];
|
|
@@ -5011,7 +5029,7 @@ let engineObjectsCollide = [];
|
|
|
5011
5029
|
* @memberof Engine */
|
|
5012
5030
|
let frame = 0;
|
|
5013
5031
|
|
|
5014
|
-
/** Current engine time since start in seconds
|
|
5032
|
+
/** Current engine time since start in seconds
|
|
5015
5033
|
* @type {Number}
|
|
5016
5034
|
* @memberof Engine */
|
|
5017
5035
|
let time = 0;
|
|
@@ -5037,12 +5055,12 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
5037
5055
|
|
|
5038
5056
|
///////////////////////////////////////////////////////////////////////////////
|
|
5039
5057
|
|
|
5040
|
-
/**
|
|
5041
|
-
* @param {Function} gameInit
|
|
5042
|
-
* @param {Function} gameUpdate
|
|
5043
|
-
* @param {Function} gameUpdatePost
|
|
5044
|
-
* @param {Function} gameRender
|
|
5045
|
-
* @param {Function} gameRenderPost
|
|
5058
|
+
/** Startup LittleJS engine with your callback functions
|
|
5059
|
+
* @param {Function} gameInit - Called once after the engine starts up, setup the game
|
|
5060
|
+
* @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
|
|
5061
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
|
|
5062
|
+
* @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
|
|
5063
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
|
|
5046
5064
|
* @param {Array} [imageSources=['tiles.png']] - Image to load
|
|
5047
5065
|
* @memberof Engine */
|
|
5048
5066
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=['tiles.png'])
|
|
@@ -5064,33 +5082,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5064
5082
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
5065
5083
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
5066
5084
|
if (!debugSpeedUp)
|
|
5067
|
-
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp
|
|
5068
|
-
|
|
5069
|
-
if (canvasFixedSize.x)
|
|
5070
|
-
{
|
|
5071
|
-
// clear canvas and set fixed size
|
|
5072
|
-
mainCanvas.width = canvasFixedSize.x;
|
|
5073
|
-
mainCanvas.height = canvasFixedSize.y;
|
|
5074
|
-
|
|
5075
|
-
// fit to window by adding space on top or bottom if necessary
|
|
5076
|
-
const aspect = innerWidth / innerHeight;
|
|
5077
|
-
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
5078
|
-
(glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
5079
|
-
(glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
5080
|
-
}
|
|
5081
|
-
else
|
|
5082
|
-
{
|
|
5083
|
-
// clear canvas and set size to same as window
|
|
5084
|
-
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
5085
|
-
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
5086
|
-
}
|
|
5087
|
-
|
|
5088
|
-
// clear overlay canvas and set size
|
|
5089
|
-
overlayCanvas.width = mainCanvas.width;
|
|
5090
|
-
overlayCanvas.height = mainCanvas.height;
|
|
5091
|
-
|
|
5092
|
-
// save canvas size
|
|
5093
|
-
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
5085
|
+
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp in case of slow framerate
|
|
5086
|
+
updateCanvas();
|
|
5094
5087
|
|
|
5095
5088
|
if (paused)
|
|
5096
5089
|
{
|
|
@@ -5164,6 +5157,35 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5164
5157
|
requestAnimationFrame(engineUpdate);
|
|
5165
5158
|
}
|
|
5166
5159
|
|
|
5160
|
+
function updateCanvas()
|
|
5161
|
+
{
|
|
5162
|
+
if (canvasFixedSize.x)
|
|
5163
|
+
{
|
|
5164
|
+
// clear canvas and set fixed size
|
|
5165
|
+
mainCanvas.width = canvasFixedSize.x;
|
|
5166
|
+
mainCanvas.height = canvasFixedSize.y;
|
|
5167
|
+
|
|
5168
|
+
// fit to window by adding space on top or bottom if necessary
|
|
5169
|
+
const aspect = innerWidth / innerHeight;
|
|
5170
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
5171
|
+
(glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
5172
|
+
(glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
5173
|
+
}
|
|
5174
|
+
else
|
|
5175
|
+
{
|
|
5176
|
+
// clear canvas and set size to same as window
|
|
5177
|
+
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
5178
|
+
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
5179
|
+
}
|
|
5180
|
+
|
|
5181
|
+
// clear overlay canvas and set size
|
|
5182
|
+
overlayCanvas.width = mainCanvas.width;
|
|
5183
|
+
overlayCanvas.height = mainCanvas.height;
|
|
5184
|
+
|
|
5185
|
+
// save canvas size
|
|
5186
|
+
mainCanvasSize = vec2(mainCanvas.width, mainCanvas.height);
|
|
5187
|
+
}
|
|
5188
|
+
|
|
5167
5189
|
// setup html
|
|
5168
5190
|
const styleBody =
|
|
5169
5191
|
'margin:0;overflow:hidden;' + // fill the window
|
|
@@ -5188,6 +5210,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
5188
5210
|
const styleCanvas = 'position:absolute;' + // position
|
|
5189
5211
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center
|
|
5190
5212
|
(glCanvas||mainCanvas).style.cssText = mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
5213
|
+
updateCanvas();
|
|
5191
5214
|
|
|
5192
5215
|
// create promises for loading images
|
|
5193
5216
|
const promises = imageSources.map((src, textureIndex)=>
|
|
@@ -5384,9 +5407,9 @@ function drawEngineSplashScreen(t)
|
|
|
5384
5407
|
|
|
5385
5408
|
// big stack
|
|
5386
5409
|
rect(50,20,10,-10,color(0,1));
|
|
5387
|
-
rect(50,20,6,-10,color(0,2));
|
|
5388
|
-
rect(50,20,3,-10,color(0,3));
|
|
5389
|
-
rect(50,
|
|
5410
|
+
rect(50,20,6.5,-10,color(0,2));
|
|
5411
|
+
rect(50,20,3.5,-10,color(0,3));
|
|
5412
|
+
rect(50,20,10,-10);
|
|
5390
5413
|
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
5391
5414
|
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
5392
5415
|
circle(55,2,11.4,.5,PI-.5);
|
|
@@ -5405,7 +5428,7 @@ function drawEngineSplashScreen(t)
|
|
|
5405
5428
|
|
|
5406
5429
|
// engine outline
|
|
5407
5430
|
circle(36,30,10,PI/2,PI*3/2);
|
|
5408
|
-
circle(
|
|
5431
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
5409
5432
|
circle(60,30,10);
|
|
5410
5433
|
line(36,20,60,20);
|
|
5411
5434
|
|