littlejsengine 1.11.13 → 1.12.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/dist/littlejs.d.ts +1418 -109
- package/dist/littlejs.esm.js +3495 -581
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +3258 -399
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +3147 -379
- package/examples/box2d/game.js +73 -45
- package/examples/box2d/gameObjects.js +96 -92
- package/examples/box2d/index.html +2 -7
- package/examples/box2d/scenes.js +82 -92
- package/examples/breakout/game.js +62 -30
- package/examples/breakout/gameObjects.js +45 -47
- package/examples/breakout/index.html +1 -5
- package/examples/breakoutTutorial/game.js +36 -29
- package/examples/breakoutTutorial/index.html +1 -3
- package/examples/empty/game.js +5 -2
- package/examples/empty/index.html +1 -3
- package/examples/htmlMenu/game.js +25 -19
- package/examples/htmlMenu/index.html +5 -7
- package/examples/index.html +2 -3
- package/examples/module/game.js +32 -34
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +27 -22
- package/examples/platformer/game.js +71 -48
- package/examples/platformer/gameCharacter.js +42 -34
- package/examples/platformer/gameEffects.js +82 -75
- package/examples/platformer/gameLevel.js +67 -38
- package/examples/platformer/{gameLevelData.js → gameLevelData.json} +1 -11
- package/examples/platformer/gameObjects.js +70 -64
- package/examples/platformer/gamePlayer.js +12 -7
- package/examples/platformer/index.html +1 -9
- package/examples/puzzle/game.js +58 -42
- package/examples/puzzle/index.html +1 -3
- package/examples/shorts/base.html +2 -2
- package/examples/shorts/particles.js +1 -1
- package/examples/shorts/platformer.js +1 -1
- package/examples/shorts/tileLayer.js +5 -6
- package/examples/shorts/tiles.png +0 -0
- package/examples/starter/build.js +4 -4
- package/examples/starter/game.js +9 -12
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +44 -43
- package/examples/typescript/build.js +17 -18
- package/examples/typescript/game.js +32 -34
- package/examples/typescript/game.ts +32 -34
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +33 -36
- package/examples/uiSystem/index.html +1 -5
- package/package.json +3 -3
- package/plugins/box2d.js +1556 -640
- package/plugins/{Box2D_v2.3.1_min.wasm.js → box2d.wasm.js} +1 -1
- package/plugins/newgrounds.js +7 -8
- package/plugins/pluginExport.js +50 -0
- package/plugins/postProcess.js +94 -93
- package/plugins/uiSystem.js +145 -161
- package/plugins/zzfxm.js +163 -0
- package/reference.md +29 -27
- package/src/engine.js +15 -20
- package/src/engineAudio.js +74 -204
- package/src/engineBuild.js +29 -4
- package/src/engineDebug.js +105 -9
- package/src/engineDraw.js +27 -14
- package/src/engineExport.js +12 -8
- package/src/engineInput.js +3 -1
- package/src/engineObject.js +18 -14
- package/src/engineParticles.js +6 -1
- package/src/engineRelease.js +6 -1
- package/src/engineSettings.js +8 -8
- package/src/engineTileLayer.js +179 -96
- package/src/engineUtilities.js +5 -11
- package/src/engineWebGL.js +19 -7
- package/examples/starter/build/index.html +0 -2
- package/examples/starter/build/index.js +0 -1
- package/examples/starter/build/tiles.png +0 -0
- package/examples/starter/game.zip +0 -0
- package/examples/typescript/build/dist/littlejs.esm.js +0 -4834
- package/examples/typescript/build/examples/typescript/build.js +0 -24
- package/examples/typescript/build/examples/typescript/game.js +0 -102
- /package/plugins/{Box2D_v2.3.1_min.wasm.wasm → box2d.wasm.wasm} +0 -0
package/reference.md
CHANGED
|
@@ -13,7 +13,7 @@ To start LittleJS, you need to create a few functions and pass them to engineIni
|
|
|
13
13
|
|
|
14
14
|
```javascript
|
|
15
15
|
// Start up LittleJS engine with your callback functions
|
|
16
|
-
engineInit(init, update, updatePost, render, renderPost, imageSources=[
|
|
16
|
+
engineInit(init, update, updatePost, render, renderPost, imageSources=[]);
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## LittleJS Utilities Classes and Functions
|
|
@@ -29,7 +29,7 @@ engineInit(init, update, updatePost, render, renderPost, imageSources=['tiles.pn
|
|
|
29
29
|
vec2(x=0, y=x) // Create a 2D vector with Vector or floats
|
|
30
30
|
rgb(r=1, g=1, b=1, a=1) // Create a color object with RGBA values
|
|
31
31
|
hsl(h=0, s=0, l=1, a=1) // Create a color object with HSLA values
|
|
32
|
-
tile(pos=(
|
|
32
|
+
tile(pos, size=(16,16), texture=0, padding=0) // Create a tile info object
|
|
33
33
|
|
|
34
34
|
// Helper functions
|
|
35
35
|
abs(value) // Get absolute value
|
|
@@ -131,12 +131,12 @@ Timer.valueOf() // Get how long since elapsed, 0 if not set
|
|
|
131
131
|
|
|
132
132
|
```javascript
|
|
133
133
|
// Drawing functions
|
|
134
|
-
drawTile(pos, size=(1,1), tileInfo, color, angle=0, mirror, additiveColor)
|
|
135
|
-
drawRect(pos, size=(1,1), color=
|
|
136
|
-
drawLine(posA, posB, thickness=.1, color=
|
|
134
|
+
drawTile(pos, size=(1,1), tileInfo, color=WHITE, angle=0, mirror, additiveColor)
|
|
135
|
+
drawRect(pos, size=(1,1), color=WHITE, angle=0)
|
|
136
|
+
drawLine(posA, posB, thickness=.1, color=WHITE)
|
|
137
137
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
138
|
-
drawText(text, pos, size=1, color=
|
|
139
|
-
drawTextScreen(text, pos, size=1, color=
|
|
138
|
+
drawText(text, pos, size=1, color=WHITE, lineWidth, lineColor)
|
|
139
|
+
drawTextScreen(text, pos, size=1, color=WHITE, lineWidth, lineColor)
|
|
140
140
|
setBlendMode(additive)
|
|
141
141
|
toggleFullscreen()
|
|
142
142
|
isFullscreen()
|
|
@@ -172,10 +172,15 @@ getCameraSize() // Get the camera's visible area in world space
|
|
|
172
172
|
canvasMaxSize = (1920, 1200) // The max size of the canvas
|
|
173
173
|
canvasFixedSize = (0, 0) // Fixed size of the canvas
|
|
174
174
|
fontDefault = 'arial' // Default font used for text rendering
|
|
175
|
-
canvasPixelated = true //
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
canvasPixelated = true // Use nearest neighbor canvas scaling for more pixelated look
|
|
176
|
+
tilesPixelated = true // Disable filtering for crisper pixel art
|
|
177
|
+
showSplashScreen = false // Show the LittleJS splash screen on startup
|
|
178
|
+
glEnable = true // Enable fast WebGL rendering
|
|
179
|
+
glOverlay = true // Prevent compositing the WebGL canvas
|
|
180
|
+
|
|
181
|
+
// Tile sheet settings
|
|
182
|
+
tileSizeDefault = (16,16) // Default size of tiles in pixels
|
|
183
|
+
tileFixBleedScale = .3 // How much smaller to draw tiles to prevent bleeding
|
|
179
184
|
```
|
|
180
185
|
|
|
181
186
|
## LittleJS Audio System
|
|
@@ -224,7 +229,7 @@ soundDefaultTaper = .7 // Default range percent to taper off sound (0-1)
|
|
|
224
229
|
keyIsDown(key) // Is key down?
|
|
225
230
|
keyWasPressed(key) // Was key pressed this frame?
|
|
226
231
|
keyWasReleased(key) // Was key released this frame?
|
|
227
|
-
keyDirection() // Get input vector from arrow keys or wasd
|
|
232
|
+
keyDirection(up, down, left, right) // Get input vector from arrow keys or wasd
|
|
228
233
|
|
|
229
234
|
// Mouse / Touch
|
|
230
235
|
mousePos // World space mouse position
|
|
@@ -276,7 +281,7 @@ touchGamepadAlpha = .3 // Transparency of touch gamepad overlay
|
|
|
276
281
|
|
|
277
282
|
```javascript
|
|
278
283
|
// Engine Object
|
|
279
|
-
EngineObject(pos
|
|
284
|
+
EngineObject(pos, size=(1,1), tileInfo, angle=0, color, renderOrder=0)
|
|
280
285
|
EngineObject.update() // Update object, called automatically
|
|
281
286
|
EngineObject.render() // Render object, called automatically
|
|
282
287
|
EngineObject.destroy() // Destroy this object and children
|
|
@@ -317,7 +322,7 @@ objectDefaultAngleDamping = 1 // How much to slow angular velocity each frame (0
|
|
|
317
322
|
objectDefaultElasticity = 0 // How much to bounce when a collision occurs (0-1)
|
|
318
323
|
objectDefaultFriction = .8 // How much to slow when touching (0-1)
|
|
319
324
|
objectMaxSpeed = 1 // Clamp max speed to avoid fast objects missing collisions
|
|
320
|
-
gravity = 0
|
|
325
|
+
gravity = (0,0) // How much gravity to apply to objects
|
|
321
326
|
|
|
322
327
|
// Engine Object functions
|
|
323
328
|
engineObjectsCollect(pos, size, objects=engineObjects)k
|
|
@@ -336,15 +341,8 @@ engineObjectsDestroy()
|
|
|
336
341
|
- Drawn directly to the main canvas without using WebGL
|
|
337
342
|
|
|
338
343
|
```javascript
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
initTileCollision(size) // Clear and initialize tile collision
|
|
342
|
-
setTileCollisionData(pos, data=0) // Set tile collision data at pos
|
|
343
|
-
getTileCollisionData(pos) // Get tile collision data at pos
|
|
344
|
-
tileCollisionTest(pos, size=(0,0), object) // Check if collision should occur
|
|
345
|
-
tileCollisionRaycast(posStart, posEnd, object) // Return the center of tile if hit
|
|
346
|
-
|
|
347
|
-
// Tile Layer Object
|
|
344
|
+
|
|
345
|
+
// LittleJS Layer System
|
|
348
346
|
TileLayer(position, size, tileInfo, scale) // Create a tile layer object
|
|
349
347
|
TileLayer.setData(layerPos, data, redraw) // Set data at position
|
|
350
348
|
TileLayer.getData(layerPos) // Get data at position
|
|
@@ -355,12 +353,16 @@ TileLayer.drawTile(pos, size=(1,1), tileInfo, color, angle, mirror) // Draw tile
|
|
|
355
353
|
TileLayer.drawCanvas2D(pos, size, angle, mirror, drawFunction) // Draw to 2D canvas
|
|
356
354
|
|
|
357
355
|
// Tile Layer Data Object
|
|
358
|
-
TileLayerData(tile, direction=0, mirror=false, color=
|
|
356
|
+
TileLayerData(tile, direction=0, mirror=false, color=WHITE) // Create tile data object
|
|
359
357
|
TileLayerData.clear() // Clear this tile data
|
|
360
358
|
|
|
361
|
-
// Tile
|
|
362
|
-
|
|
363
|
-
|
|
359
|
+
// Tile Collision Layer
|
|
360
|
+
TileCollisionLayer(position, size, tileInfo=tile()) // Create a tile collision layer object
|
|
361
|
+
TileCollisionLayer.size // Size of the tile collision layer
|
|
362
|
+
TileCollisionLayer.setCollisionData(pos, data=1) // Set tile collision data at pos
|
|
363
|
+
getTileCollisionData(pos) // Get tile collision data at pos
|
|
364
|
+
tileCollisionTest(pos, size=(0,0), object) // Check if collision should occur
|
|
365
|
+
tileCollisionRaycast(posStart, posEnd, object) // Return the center of tile if hit
|
|
364
366
|
```
|
|
365
367
|
|
|
366
368
|
## LittleJS Particle System
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {string}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.
|
|
33
|
+
const engineVersion = '1.12.4';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -118,16 +118,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
118
118
|
ASSERT(Array.isArray(imageSources), 'pass in images as array');
|
|
119
119
|
|
|
120
120
|
// allow passing in empty functions
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
gameUpdatePost = ()=>{};
|
|
127
|
-
if (!gameRender)
|
|
128
|
-
gameRender = ()=>{};
|
|
129
|
-
if (!gameRenderPost)
|
|
130
|
-
gameRenderPost = ()=>{};
|
|
121
|
+
gameInit ||= ()=>{};
|
|
122
|
+
gameUpdate ||= ()=>{};
|
|
123
|
+
gameUpdatePost ||= ()=>{};
|
|
124
|
+
gameRender ||= ()=>{};
|
|
125
|
+
gameRenderPost ||= ()=>{};
|
|
131
126
|
|
|
132
127
|
// Called automatically by engine to setup render system
|
|
133
128
|
function enginePreRender()
|
|
@@ -154,11 +149,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
154
149
|
const debugSpeedUp = debug && keyIsDown('Equal'); // +
|
|
155
150
|
const debugSpeedDown = debug && keyIsDown('Minus'); // -
|
|
156
151
|
if (debug) // +/- to speed/slow time
|
|
157
|
-
frameTimeDeltaMS *= debugSpeedUp ?
|
|
152
|
+
frameTimeDeltaMS *= debugSpeedUp ? 10 : debugSpeedDown ? .1 : 1;
|
|
158
153
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
159
154
|
frameTimeBufferMS += paused ? 0 : frameTimeDeltaMS;
|
|
160
155
|
if (!debugSpeedUp)
|
|
161
|
-
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp
|
|
156
|
+
frameTimeBufferMS = min(frameTimeBufferMS, 50); // clamp min framerate
|
|
157
|
+
if (debug && debugVideoCaptureIsActive())
|
|
158
|
+
frameTimeBufferMS = 0; // disable time smoothing when capturing video
|
|
162
159
|
|
|
163
160
|
updateCanvas();
|
|
164
161
|
|
|
@@ -237,6 +234,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
237
234
|
}
|
|
238
235
|
}
|
|
239
236
|
|
|
237
|
+
debugVideoCaptureUpdate();
|
|
240
238
|
requestAnimationFrame(engineUpdate);
|
|
241
239
|
}
|
|
242
240
|
|
|
@@ -284,11 +282,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
284
282
|
|
|
285
283
|
// setup html
|
|
286
284
|
const styleRoot =
|
|
287
|
-
'margin:0;
|
|
288
|
-
'width:100vw;height:100vh;' + // fill the window
|
|
289
|
-
'display:flex;' + // use flexbox
|
|
290
|
-
'align-items:center;' + // horizontal center
|
|
291
|
-
'justify-content:center;' + // vertical center
|
|
285
|
+
'margin:0;' + // fill the window
|
|
292
286
|
'background:#000;' + // set background color
|
|
293
287
|
(canvasPixelated ? 'image-rendering:pixelated;' : '') + // pixel art
|
|
294
288
|
'user-select:none;' + // prevent hold to select
|
|
@@ -311,7 +305,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
311
305
|
overlayContext = overlayCanvas.getContext('2d');
|
|
312
306
|
|
|
313
307
|
// set canvas style
|
|
314
|
-
const styleCanvas = 'position:absolute'
|
|
308
|
+
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
309
|
+
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
315
310
|
mainCanvas.style.cssText = overlayCanvas.style.cssText = styleCanvas;
|
|
316
311
|
if (glCanvas)
|
|
317
312
|
glCanvas.style.cssText = styleCanvas;
|
|
@@ -322,12 +317,12 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
322
317
|
new Promise(resolve =>
|
|
323
318
|
{
|
|
324
319
|
const image = new Image;
|
|
325
|
-
image.crossOrigin = 'anonymous';
|
|
326
320
|
image.onerror = image.onload = ()=>
|
|
327
321
|
{
|
|
328
322
|
textureInfos[textureIndex] = new TextureInfo(image);
|
|
329
323
|
resolve();
|
|
330
324
|
}
|
|
325
|
+
image.crossOrigin = 'anonymous';
|
|
331
326
|
image.src = src;
|
|
332
327
|
})
|
|
333
328
|
);
|
package/src/engineAudio.js
CHANGED
|
@@ -19,16 +19,15 @@ let audioContext = new AudioContext;
|
|
|
19
19
|
/** Master gain node for all audio to pass through
|
|
20
20
|
* @type {GainNode}
|
|
21
21
|
* @memberof Audio */
|
|
22
|
-
let
|
|
22
|
+
let audioMasterGain;
|
|
23
23
|
|
|
24
24
|
function audioInit()
|
|
25
25
|
{
|
|
26
26
|
if (!soundEnable || headlessMode) return;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
audioGainNode.gain.value = soundVolume; // set starting value
|
|
28
|
+
audioMasterGain = audioContext.createGain();
|
|
29
|
+
audioMasterGain.connect(audioContext.destination);
|
|
30
|
+
audioMasterGain.gain.value = soundVolume; // set starting value
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -157,6 +156,8 @@ class Sound
|
|
|
157
156
|
isLoading() { return !this.sampleChannels; }
|
|
158
157
|
}
|
|
159
158
|
|
|
159
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
160
|
+
|
|
160
161
|
/**
|
|
161
162
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
162
163
|
* - this can be used to play wave, mp3, and ogg files
|
|
@@ -208,59 +209,7 @@ function playAudioFile(filename, volume=1, loop=false)
|
|
|
208
209
|
return new SoundWave(filename,0,0,0, s=>s.play(undefined, volume, 1, 1, loop));
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
|
|
212
|
-
* Music Object - Stores a zzfx music track for later use
|
|
213
|
-
*
|
|
214
|
-
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
215
|
-
* @example
|
|
216
|
-
* // create some music
|
|
217
|
-
* const music_example = new Music(
|
|
218
|
-
* [
|
|
219
|
-
* [ // instruments
|
|
220
|
-
* [,0,400] // simple note
|
|
221
|
-
* ],
|
|
222
|
-
* [ // patterns
|
|
223
|
-
* [ // pattern 1
|
|
224
|
-
* [ // channel 0
|
|
225
|
-
* 0, -1, // instrument 0, left speaker
|
|
226
|
-
* 1, 0, 9, 1 // channel notes
|
|
227
|
-
* ],
|
|
228
|
-
* [ // channel 1
|
|
229
|
-
* 0, 1, // instrument 0, right speaker
|
|
230
|
-
* 0, 12, 17, -1 // channel notes
|
|
231
|
-
* ]
|
|
232
|
-
* ],
|
|
233
|
-
* ],
|
|
234
|
-
* [0, 0, 0, 0], // sequence, play pattern 0 four times
|
|
235
|
-
* 90 // BPM
|
|
236
|
-
* ]);
|
|
237
|
-
*
|
|
238
|
-
* // play the music
|
|
239
|
-
* music_example.play();
|
|
240
|
-
*/
|
|
241
|
-
class Music extends Sound
|
|
242
|
-
{
|
|
243
|
-
/** Create a music object and cache the zzfx music samples for later use
|
|
244
|
-
* @param {[Array, Array, Array, number]} zzfxMusic - Array of zzfx music parameters
|
|
245
|
-
*/
|
|
246
|
-
constructor(zzfxMusic)
|
|
247
|
-
{
|
|
248
|
-
super(undefined);
|
|
249
|
-
|
|
250
|
-
if (!soundEnable || headlessMode) return;
|
|
251
|
-
this.randomness = 0;
|
|
252
|
-
this.sampleChannels = zzfxM(...zzfxMusic);
|
|
253
|
-
this.sampleRate = zzfxR;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/** Play the music
|
|
257
|
-
* @param {number} [volume=1] - How much to scale volume by
|
|
258
|
-
* @param {boolean} [loop] - True if the music should loop
|
|
259
|
-
* @return {AudioBufferSourceNode} - The audio source node
|
|
260
|
-
*/
|
|
261
|
-
playMusic(volume, loop=false)
|
|
262
|
-
{ return super.play(undefined, volume, 1, 1, loop); }
|
|
263
|
-
}
|
|
212
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
264
213
|
|
|
265
214
|
/** Speak text with passed in settings
|
|
266
215
|
* @param {string} text - The text to speak
|
|
@@ -332,7 +281,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
332
281
|
// create and connect gain node
|
|
333
282
|
gainNode = gainNode || audioContext.createGain();
|
|
334
283
|
gainNode.gain.value = volume;
|
|
335
|
-
gainNode.connect(
|
|
284
|
+
gainNode.connect(audioMasterGain);
|
|
336
285
|
|
|
337
286
|
// connect source to stereo panner and gain
|
|
338
287
|
const pannerNode = new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)});
|
|
@@ -352,7 +301,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
352
301
|
}
|
|
353
302
|
|
|
354
303
|
///////////////////////////////////////////////////////////////////////////////
|
|
355
|
-
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.
|
|
304
|
+
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.3.2 by Frank Force
|
|
356
305
|
|
|
357
306
|
/** Generate and play a ZzFX sound
|
|
358
307
|
*
|
|
@@ -394,21 +343,45 @@ const zzfxR = 44100;
|
|
|
394
343
|
*/
|
|
395
344
|
function zzfxG
|
|
396
345
|
(
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
346
|
+
volume = 1,
|
|
347
|
+
randomness = .05,
|
|
348
|
+
frequency = 220,
|
|
349
|
+
attack = 0,
|
|
350
|
+
sustain = 0,
|
|
351
|
+
release = .1,
|
|
352
|
+
shape = 0,
|
|
353
|
+
shapeCurve = 1,
|
|
354
|
+
slide = 0,
|
|
355
|
+
deltaSlide = 0,
|
|
356
|
+
pitchJump = 0,
|
|
357
|
+
pitchJumpTime = 0,
|
|
358
|
+
repeatTime = 0,
|
|
359
|
+
noise = 0,
|
|
360
|
+
modulation = 0,
|
|
361
|
+
bitCrush = 0,
|
|
362
|
+
delay = 0,
|
|
363
|
+
sustainVolume = 1,
|
|
364
|
+
decay = 0,
|
|
365
|
+
tremolo = 0,
|
|
366
|
+
filter = 0
|
|
402
367
|
)
|
|
403
368
|
{
|
|
404
|
-
// LJS Note: ZZFX modded so randomness is handled by Sound class
|
|
405
|
-
|
|
406
369
|
// init parameters
|
|
407
|
-
let
|
|
370
|
+
let sampleRate = zzfxR,
|
|
371
|
+
PI2 = PI*2,
|
|
408
372
|
startSlide = slide *= 500 * PI2 / sampleRate / sampleRate,
|
|
409
373
|
startFrequency = frequency *=
|
|
410
|
-
|
|
411
|
-
|
|
374
|
+
(1 + rand(randomness,-randomness)) * PI2 / sampleRate,
|
|
375
|
+
modOffset = 0, // modulation offset
|
|
376
|
+
repeat = 0, // repeat offset
|
|
377
|
+
crush = 0, // bit crush offset
|
|
378
|
+
jump = 1, // pitch jump timer
|
|
379
|
+
length, // sample length
|
|
380
|
+
b = [], // sample buffer
|
|
381
|
+
t = 0, // sample time
|
|
382
|
+
i = 0, // sample index
|
|
383
|
+
s = 0, // sample value
|
|
384
|
+
f, // wave frequency
|
|
412
385
|
|
|
413
386
|
// biquad LP/HP filter
|
|
414
387
|
quality = 2, w = PI2 * abs(filter) * 2 / sampleRate,
|
|
@@ -418,35 +391,37 @@ function zzfxG
|
|
|
418
391
|
b1 = -(sign(filter) + cos) / a0, b2 = b0,
|
|
419
392
|
x2 = 0, x1 = 0, y2 = 0, y1 = 0;
|
|
420
393
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
394
|
+
// scale by sample rate
|
|
395
|
+
const minAttack = 9; // prevent pop if attack is 0
|
|
396
|
+
attack = attack * sampleRate || minAttack;
|
|
397
|
+
decay *= sampleRate;
|
|
398
|
+
sustain *= sampleRate;
|
|
399
|
+
release *= sampleRate;
|
|
400
|
+
delay *= sampleRate;
|
|
401
|
+
deltaSlide *= 500 * PI2 / sampleRate**3;
|
|
402
|
+
modulation *= PI2 / sampleRate;
|
|
403
|
+
pitchJump *= PI2 / sampleRate;
|
|
404
|
+
pitchJumpTime *= sampleRate;
|
|
405
|
+
repeatTime = repeatTime * sampleRate | 0;
|
|
432
406
|
|
|
433
407
|
// generate waveform
|
|
434
408
|
for(length = attack + decay + sustain + release + delay | 0;
|
|
435
|
-
i < length; b[i++] = s * volume)
|
|
409
|
+
i < length; b[i++] = s * volume) // sample
|
|
436
410
|
{
|
|
437
|
-
if (!(++
|
|
411
|
+
if (!(++crush%(bitCrush*100|0))) // bit crush
|
|
438
412
|
{
|
|
439
|
-
s = shape? shape>1? shape>2? shape>3?
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
1-
|
|
444
|
-
Math.
|
|
413
|
+
s = shape? shape>1? shape>2? shape>3? shape>4? // wave shape
|
|
414
|
+
(t/PI2%1 < shapeCurve/2? 1 : -1) : // 5 square duty
|
|
415
|
+
Math.sin(t**3) : // 4 noise
|
|
416
|
+
Math.max(Math.min(Math.tan(t),1),-1): // 3 tan
|
|
417
|
+
1-(2*t/PI2%2+2)%2: // 2 saw
|
|
418
|
+
1-4*abs(Math.round(t/PI2)-t/PI2): // 1 triangle
|
|
419
|
+
Math.sin(t); // 0 sin
|
|
445
420
|
|
|
446
421
|
s = (repeatTime ?
|
|
447
422
|
1 - tremolo + tremolo*Math.sin(PI2*i/repeatTime) // tremolo
|
|
448
423
|
: 1) *
|
|
449
|
-
sign(s)*
|
|
424
|
+
(shape>4?s:sign(s)*abs(s)**shapeCurve) * // shape curve
|
|
450
425
|
(i < attack ? i/attack : // attack
|
|
451
426
|
i < attack + decay ? // decay
|
|
452
427
|
1-((i-attack)/decay)*(1-sustainVolume) : // decay falloff
|
|
@@ -461,133 +436,28 @@ function zzfxG
|
|
|
461
436
|
(i<length-delay? 1 : (length-i)/delay) * // release delay
|
|
462
437
|
b[i-delay|0]/2/volume) : s; // sample delay
|
|
463
438
|
|
|
464
|
-
if (filter)
|
|
439
|
+
if (filter) // apply filter
|
|
465
440
|
s = y1 = b2*x2 + b1*(x2=x1) + b0*(x1=s) - a2*y2 - a1*(y2=y1);
|
|
466
441
|
}
|
|
467
442
|
|
|
468
443
|
f = (frequency += slide += deltaSlide) *// frequency
|
|
469
|
-
Math.cos(modulation*
|
|
444
|
+
Math.cos(modulation*modOffset++); // modulation
|
|
470
445
|
t += f + f*noise*Math.sin(i**5); // noise
|
|
471
446
|
|
|
472
|
-
if (
|
|
447
|
+
if (jump && ++jump > pitchJumpTime) // pitch jump
|
|
473
448
|
{
|
|
474
449
|
frequency += pitchJump; // apply pitch jump
|
|
475
450
|
startFrequency += pitchJump; // also apply to start
|
|
476
|
-
|
|
451
|
+
jump = 0; // stop pitch jump time
|
|
477
452
|
}
|
|
478
453
|
|
|
479
|
-
if (repeatTime && !(++
|
|
454
|
+
if (repeatTime && !(++repeat % repeatTime)) // repeat
|
|
480
455
|
{
|
|
481
|
-
frequency = startFrequency;
|
|
482
|
-
slide = startSlide;
|
|
483
|
-
|
|
456
|
+
frequency = startFrequency; // reset frequency
|
|
457
|
+
slide = startSlide; // reset slide
|
|
458
|
+
jump ||= 1; // reset pitch jump time
|
|
484
459
|
}
|
|
485
460
|
}
|
|
486
461
|
|
|
487
|
-
return b;
|
|
462
|
+
return b; // return sample buffer
|
|
488
463
|
}
|
|
489
|
-
|
|
490
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
491
|
-
// ZzFX Music Renderer v2.0.3 by Keith Clark and Frank Force
|
|
492
|
-
|
|
493
|
-
/** Generate samples for a ZzFM song with given parameters
|
|
494
|
-
* @param {Array} instruments - Array of ZzFX sound parameters
|
|
495
|
-
* @param {Array} patterns - Array of pattern data
|
|
496
|
-
* @param {Array} sequence - Array of pattern indexes
|
|
497
|
-
* @param {number} [BPM] - Playback speed of the song in BPM
|
|
498
|
-
* @return {Array} - Left and right channel sample data
|
|
499
|
-
* @memberof Audio */
|
|
500
|
-
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
501
|
-
{
|
|
502
|
-
let i, j, k;
|
|
503
|
-
let instrumentParameters;
|
|
504
|
-
let note;
|
|
505
|
-
let sample;
|
|
506
|
-
let patternChannel;
|
|
507
|
-
let notFirstBeat;
|
|
508
|
-
let stop;
|
|
509
|
-
let instrument;
|
|
510
|
-
let attenuation;
|
|
511
|
-
let outSampleOffset;
|
|
512
|
-
let isSequenceEnd;
|
|
513
|
-
let sampleOffset = 0;
|
|
514
|
-
let nextSampleOffset;
|
|
515
|
-
let sampleBuffer = [];
|
|
516
|
-
let leftChannelBuffer = [];
|
|
517
|
-
let rightChannelBuffer = [];
|
|
518
|
-
let channelIndex = 0;
|
|
519
|
-
let panning = 0;
|
|
520
|
-
let hasMore = 1;
|
|
521
|
-
let sampleCache = {};
|
|
522
|
-
let beatLength = zzfxR / BPM * 60 >> 2;
|
|
523
|
-
|
|
524
|
-
// for each channel in order until there are no more
|
|
525
|
-
for (; hasMore; channelIndex++) {
|
|
526
|
-
|
|
527
|
-
// reset current values
|
|
528
|
-
sampleBuffer = [hasMore = notFirstBeat = outSampleOffset = 0];
|
|
529
|
-
|
|
530
|
-
// for each pattern in sequence
|
|
531
|
-
sequence.forEach((patternIndex, sequenceIndex) => {
|
|
532
|
-
// get pattern for current channel, use empty 1 note pattern if none found
|
|
533
|
-
patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
|
|
534
|
-
|
|
535
|
-
// check if there are more channels
|
|
536
|
-
hasMore |= patterns[patternIndex][channelIndex]&&1;
|
|
537
|
-
|
|
538
|
-
// get next offset, use the length of first channel
|
|
539
|
-
nextSampleOffset = outSampleOffset + (patterns[patternIndex][0].length - 2 - (notFirstBeat?0:1)) * beatLength;
|
|
540
|
-
// for each beat in pattern, plus one extra if end of sequence
|
|
541
|
-
isSequenceEnd = sequenceIndex == sequence.length - 1;
|
|
542
|
-
for (i = 2, k = outSampleOffset; i < patternChannel.length + isSequenceEnd; notFirstBeat = ++i) {
|
|
543
|
-
|
|
544
|
-
// <channel-note>
|
|
545
|
-
note = patternChannel[i];
|
|
546
|
-
|
|
547
|
-
// stop if end, different instrument or new note
|
|
548
|
-
stop = i == patternChannel.length + isSequenceEnd - 1 && isSequenceEnd ||
|
|
549
|
-
instrument != (patternChannel[0] || 0) || note | 0;
|
|
550
|
-
|
|
551
|
-
// fill buffer with samples for previous beat, most cpu intensive part
|
|
552
|
-
for (j = 0; j < beatLength && notFirstBeat;
|
|
553
|
-
|
|
554
|
-
// fade off attenuation at end of beat if stopping note, prevents clicking
|
|
555
|
-
j++ > beatLength - 99 && stop && attenuation < 1? attenuation += 1 / 99 : 0
|
|
556
|
-
) {
|
|
557
|
-
// copy sample to stereo buffers with panning
|
|
558
|
-
sample = (1 - attenuation) * sampleBuffer[sampleOffset++] / 2 || 0;
|
|
559
|
-
leftChannelBuffer[k] = (leftChannelBuffer[k] || 0) - sample * panning + sample;
|
|
560
|
-
rightChannelBuffer[k] = (rightChannelBuffer[k++] || 0) + sample * panning + sample;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// set up for next note
|
|
564
|
-
if (note) {
|
|
565
|
-
// set attenuation
|
|
566
|
-
attenuation = note % 1;
|
|
567
|
-
panning = patternChannel[1] || 0;
|
|
568
|
-
if (note |= 0) {
|
|
569
|
-
// get cached sample
|
|
570
|
-
sampleBuffer = sampleCache[
|
|
571
|
-
[
|
|
572
|
-
instrument = patternChannel[sampleOffset = 0] || 0,
|
|
573
|
-
note
|
|
574
|
-
]
|
|
575
|
-
] = sampleCache[[instrument, note]] || (
|
|
576
|
-
// add sample to cache
|
|
577
|
-
instrumentParameters = [...instruments[instrument]],
|
|
578
|
-
instrumentParameters[2] *= 2 ** ((note - 12) / 12),
|
|
579
|
-
|
|
580
|
-
// allow negative values to stop notes
|
|
581
|
-
note > 0 ? zzfxG(...instrumentParameters) : []
|
|
582
|
-
);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// update the sample offset
|
|
588
|
-
outSampleOffset = nextSampleOffset;
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return [leftChannelBuffer, rightChannelBuffer];
|
|
593
|
-
}
|
package/src/engineBuild.js
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
const ENGINE_NAME = 'littlejs';
|
|
14
14
|
const BUILD_FOLDER = 'dist';
|
|
15
15
|
const SOURCE_FOLDER = 'src';
|
|
16
|
+
const PLUGIN_FOLDER = 'plugins';
|
|
16
17
|
const engineSourceFiles =
|
|
17
18
|
[
|
|
18
19
|
`${SOURCE_FOLDER}/engineUtilities.js`,
|
|
@@ -27,6 +28,14 @@ const engineSourceFiles =
|
|
|
27
28
|
`${SOURCE_FOLDER}/engineWebGL.js`,
|
|
28
29
|
`${SOURCE_FOLDER}/engine.js`,
|
|
29
30
|
];
|
|
31
|
+
const enginePluginFiles =
|
|
32
|
+
[
|
|
33
|
+
`${PLUGIN_FOLDER}/newgrounds.js`,
|
|
34
|
+
`${PLUGIN_FOLDER}/postProcess.js`,
|
|
35
|
+
`${PLUGIN_FOLDER}/zzfxm.js`,
|
|
36
|
+
`${PLUGIN_FOLDER}/uiSystem.js`,
|
|
37
|
+
`${PLUGIN_FOLDER}/box2d.js`,
|
|
38
|
+
];
|
|
30
39
|
const asciiArt =`
|
|
31
40
|
~~~~°°°°ooo°oOo°ooOooOooOo.
|
|
32
41
|
__________ ________ ____'°oO.
|
|
@@ -55,7 +64,11 @@ Build
|
|
|
55
64
|
(
|
|
56
65
|
'Build Engine -- all',
|
|
57
66
|
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
|
|
58
|
-
[
|
|
67
|
+
[
|
|
68
|
+
`${SOURCE_FOLDER}/engineDebug.js`,
|
|
69
|
+
...engineSourceFiles,
|
|
70
|
+
...enginePluginFiles
|
|
71
|
+
],
|
|
59
72
|
[], true
|
|
60
73
|
);
|
|
61
74
|
|
|
@@ -63,7 +76,11 @@ Build
|
|
|
63
76
|
(
|
|
64
77
|
'Build Engine -- release',
|
|
65
78
|
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
|
|
66
|
-
[
|
|
79
|
+
[
|
|
80
|
+
`${SOURCE_FOLDER}/engineRelease.js`,
|
|
81
|
+
...engineSourceFiles,
|
|
82
|
+
...enginePluginFiles
|
|
83
|
+
],
|
|
67
84
|
[], true
|
|
68
85
|
);
|
|
69
86
|
|
|
@@ -79,7 +96,11 @@ Build
|
|
|
79
96
|
(
|
|
80
97
|
'Build Engine -- ESM',
|
|
81
98
|
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.js`,
|
|
82
|
-
[
|
|
99
|
+
[
|
|
100
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
|
|
101
|
+
`${SOURCE_FOLDER}/engineExport.js`,
|
|
102
|
+
`${PLUGIN_FOLDER}/pluginExport.js`
|
|
103
|
+
],
|
|
83
104
|
[typeScriptBuildStep]
|
|
84
105
|
);
|
|
85
106
|
|
|
@@ -87,7 +108,11 @@ Build
|
|
|
87
108
|
(
|
|
88
109
|
'Build Engine -- ESM minified release',
|
|
89
110
|
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.min.js`,
|
|
90
|
-
[
|
|
111
|
+
[
|
|
112
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
|
|
113
|
+
`${SOURCE_FOLDER}/engineExport.js`,
|
|
114
|
+
`${PLUGIN_FOLDER}/pluginExport.js`
|
|
115
|
+
],
|
|
91
116
|
[uglifyBuildStep]
|
|
92
117
|
);
|
|
93
118
|
|