littlejsengine 1.1.2 → 1.1.3
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 +55 -0
- package/build.bat +2 -1
- package/engine/engine.all.js +1320 -930
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +965 -601
- package/engine/engine.js +71 -60
- package/engine/engineAudio.js +80 -22
- package/engine/engineBuild.bat +2 -2
- package/engine/engineDebug.js +65 -36
- package/engine/engineDraw.js +71 -56
- package/engine/engineInput.js +178 -28
- package/engine/engineMedals.js +66 -58
- package/engine/engineObject.js +86 -71
- package/engine/engineParticles.js +101 -63
- package/engine/engineRelease.js +4 -1
- package/engine/engineSettings.js +62 -53
- package/engine/engineTileLayer.js +99 -83
- package/engine/engineUtilities.js +95 -59
- package/engine/engineWebGL.js +53 -48
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +7 -5
- package/examples/platformer/game.js +7 -4
- package/examples/platformer/gameEffects.js +8 -11
- package/examples/platformer/gameLevel.js +15 -8
- package/examples/platformer/gameObjects.js +2 -2
- package/examples/platformer/gamePlayer.js +6 -6
- package/examples/platformer/index.html +10 -8
- package/examples/puzzle/game.js +3 -3
- package/examples/puzzle/index.html +6 -4
- package/examples/stress/index.html +7 -5
- package/game.js +10 -10
- package/index.html +17 -15
- package/package.json +4 -7
- package/tiles.png +0 -0
- package/LittleJS.zip +0 -0
- package/docs/Audio.html +0 -2264
- package/docs/Color.html +0 -2506
- package/docs/Debug.html +0 -2929
- package/docs/Draw.html +0 -3854
- package/docs/EngineObject.html +0 -2506
- package/docs/Input.html +0 -2478
- package/docs/Medal.html +0 -992
- package/docs/Medals.html +0 -624
- package/docs/Music.html +0 -569
- package/docs/Newgrounds.html +0 -1384
- package/docs/Particle.html +0 -567
- package/docs/ParticleEmitter.html +0 -1537
- package/docs/Random.html +0 -1770
- package/docs/Settings.html +0 -2885
- package/docs/Sound.html +0 -998
- package/docs/TileLayer.html +0 -3211
- package/docs/TileLayerData.html +0 -569
- package/docs/Timer.html +0 -1168
- package/docs/Utilities.html +0 -3266
- package/docs/Vector2.html +0 -3956
- package/docs/WebGL.html +0 -1842
- package/docs/engine.js.html +0 -428
- package/docs/engineAudio.js.html +0 -567
- package/docs/engineDebug.js.html +0 -721
- package/docs/engineDraw.js.html +0 -409
- package/docs/engineInput.js.html +0 -397
- package/docs/engineMedals.js.html +0 -445
- package/docs/engineObject.js.html +0 -492
- package/docs/engineParticles.js.html +0 -399
- package/docs/engineSettings.js.html +0 -358
- package/docs/engineTileLayer.js.html +0 -482
- package/docs/engineUtilities.js.html +0 -600
- package/docs/engineWebGL.js.html +0 -507
- package/docs/fonts/MavenPro-Regular.ttf +0 -0
- package/docs/fonts/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Muli-Black.ttf +0 -0
- package/docs/fonts/OFL-hind.txt +0 -93
- package/docs/fonts/OFL-montserrat.txt +0 -93
- package/docs/global.html +0 -1635
- package/docs/index.html +0 -243
- package/docs/scripts/fix-code-block.js +0 -53
- package/docs/scripts/fix-navbar.js +0 -25
- package/docs/scripts/linenumber.js +0 -25
- package/docs/scripts/misc.js +0 -217
- package/docs/scripts/resize.js +0 -85
- package/docs/scripts/search.js +0 -83
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
- package/docs/scripts/third-party/fuse.js +0 -9
- package/docs/scripts/third-party/lang-css.js +0 -2
- package/docs/scripts/third-party/prettify.js +0 -28
- package/docs/static/favicon.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +0 -393
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -324
- package/docs/styles/clean-jsdoc-theme-light.css +0 -319
- package/docs/styles/reset.css +0 -346
- package/docs/styles/third-party/ionicons.min.css +0 -11
- package/docs/styles/third-party/prettify-jsdoc.css +0 -111
- package/docs/styles/third-party/prettify-tomorrow.css +0 -5
package/engine/engine.js
CHANGED
|
@@ -22,21 +22,21 @@
|
|
|
22
22
|
const engineName = 'LittleJS';
|
|
23
23
|
|
|
24
24
|
/** Version of engine */
|
|
25
|
-
const engineVersion = '1.1.
|
|
25
|
+
const engineVersion = '1.1.3';
|
|
26
26
|
|
|
27
27
|
/** Frames per second to update objects
|
|
28
28
|
* @default */
|
|
29
|
-
const
|
|
29
|
+
const frameRate = 60;
|
|
30
30
|
|
|
31
31
|
/** How many seconds each frame lasts, engine uses a fixed time step
|
|
32
32
|
* @default 1/60 */
|
|
33
|
-
const timeDelta = 1/
|
|
33
|
+
const timeDelta = 1/frameRate;
|
|
34
34
|
|
|
35
35
|
/** Array containing all engine objects */
|
|
36
36
|
let engineObjects = [];
|
|
37
37
|
|
|
38
|
-
/** Array containing only objects that are set to collide with other objects (for optimization) */
|
|
39
|
-
let
|
|
38
|
+
/** Array containing only objects that are set to collide with other objects this frame (for optimization) */
|
|
39
|
+
let engineObjectsCollide = [];
|
|
40
40
|
|
|
41
41
|
/** Current update frame, used to calculate time */
|
|
42
42
|
let frame = 0;
|
|
@@ -51,36 +51,40 @@ let timeReal = 0;
|
|
|
51
51
|
let paused = 0;
|
|
52
52
|
|
|
53
53
|
// Engine internal variables not exposed to documentation
|
|
54
|
-
let frameTimeLastMS = 0, frameTimeBufferMS = 0,
|
|
55
|
-
|
|
54
|
+
let frameTimeLastMS = 0, frameTimeBufferMS = 0, tileImageSize, tileImageFixBleed;
|
|
55
|
+
|
|
56
|
+
// Engine stat tracking, if showWatermark is true
|
|
57
|
+
let averageFPS, drawCount;
|
|
58
|
+
|
|
59
|
+
// css text used for elements created by engine
|
|
60
|
+
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
61
|
+
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
62
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
56
63
|
|
|
57
64
|
///////////////////////////////////////////////////////////////////////////////
|
|
58
65
|
|
|
59
66
|
/** Start up LittleJS engine with your callback functions
|
|
60
|
-
* @param {Function} gameInit
|
|
61
|
-
* @param {Function} gameUpdate
|
|
62
|
-
* @param {Function} gameUpdatePost
|
|
63
|
-
* @param {Function} gameRender
|
|
64
|
-
* @param {Function} gameRenderPost
|
|
65
|
-
* @param {String} tileImageSource
|
|
67
|
+
* @param {Function} gameInit - Called once after the engine starts up, setup the game
|
|
68
|
+
* @param {Function} gameUpdate - Called every frame at 60 frames per second, handle input and update the game state
|
|
69
|
+
* @param {Function} gameUpdatePost - Called after physics and objects are updated, setup camera and prepare for render
|
|
70
|
+
* @param {Function} gameRender - Called before objects are rendered, draw any background effects that appear behind objects
|
|
71
|
+
* @param {Function} gameRenderPost - Called after objects are rendered, draw effects or hud that appear above all objects
|
|
72
|
+
* @param {String} [tileImageSource] - Tile image to use, everything starts when the image is finished loading
|
|
66
73
|
*/
|
|
67
74
|
function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, tileImageSource)
|
|
68
75
|
{
|
|
69
|
-
// init engine when tiles load
|
|
70
|
-
tileImage.onload = ()=>
|
|
76
|
+
// init engine when tiles load or fail to load
|
|
77
|
+
tileImage.onerror = tileImage.onload = ()=>
|
|
71
78
|
{
|
|
72
79
|
// save tile image info
|
|
73
|
-
|
|
80
|
+
tileImageFixBleed = vec2(tileFixBleedScale).divide(tileImageSize = vec2(tileImage.width, tileImage.height));
|
|
74
81
|
debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
|
|
75
|
-
shrinkTilesX = tileBleedShrinkFix/tileImageSize.x;
|
|
76
|
-
shrinkTilesY = tileBleedShrinkFix/tileImageSize.y;
|
|
77
82
|
|
|
78
83
|
// setup html
|
|
84
|
+
document.body.style = styleBody;
|
|
79
85
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
80
|
-
document.body.style = 'margin:0;overflow:hidden;background:#000';
|
|
81
|
-
mainCanvas.style = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)' +
|
|
82
|
-
(pixelated ? ';image-rendering:crisp-edges;image-rendering:pixelated' : ''); // pixelated rendering
|
|
83
86
|
mainContext = mainCanvas.getContext('2d');
|
|
87
|
+
mainCanvas.style = styleCanvas;
|
|
84
88
|
|
|
85
89
|
// init stuff and start engine
|
|
86
90
|
debugInit();
|
|
@@ -88,10 +92,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
88
92
|
|
|
89
93
|
// create overlay canvas for hud to appear above gl canvas
|
|
90
94
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
91
|
-
overlayCanvas.style = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
92
95
|
overlayContext = overlayCanvas.getContext('2d');
|
|
93
|
-
|
|
96
|
+
overlayCanvas.style = styleCanvas;
|
|
97
|
+
|
|
94
98
|
gameInit();
|
|
99
|
+
touchGamepadCreate();
|
|
95
100
|
engineUpdate();
|
|
96
101
|
};
|
|
97
102
|
|
|
@@ -104,7 +109,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
104
109
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
105
110
|
frameTimeLastMS = frameTimeMS;
|
|
106
111
|
if (debug || showWatermark)
|
|
107
|
-
|
|
112
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
108
113
|
if (debug)
|
|
109
114
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
110
115
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -130,7 +135,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
130
135
|
}
|
|
131
136
|
|
|
132
137
|
// update multiple frames if necessary in case of slow framerate
|
|
133
|
-
for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 /
|
|
138
|
+
for (;frameTimeBufferMS >= 0; frameTimeBufferMS -= 1e3 / frameRate)
|
|
134
139
|
{
|
|
135
140
|
// update game and objects
|
|
136
141
|
inputUpdate();
|
|
@@ -147,45 +152,39 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
147
152
|
frameTimeBufferMS += deltaSmooth;
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
if (
|
|
155
|
+
if (canvasFixedSize.x)
|
|
151
156
|
{
|
|
152
157
|
// clear set fixed size
|
|
153
|
-
mainCanvas.width =
|
|
154
|
-
mainCanvas.height =
|
|
158
|
+
mainCanvas.width = canvasFixedSize.x;
|
|
159
|
+
mainCanvas.height = canvasFixedSize.y;
|
|
155
160
|
|
|
156
|
-
if
|
|
161
|
+
// fit to window by adding space on top or bottom if necessary
|
|
162
|
+
const aspect = innerWidth / innerHeight;
|
|
163
|
+
const fixedAspect = mainCanvas.width / mainCanvas.height;
|
|
164
|
+
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
165
|
+
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
166
|
+
if (glCanvas)
|
|
157
167
|
{
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const fixedAspect = fixedWidth / fixedHeight;
|
|
161
|
-
mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
|
|
162
|
-
mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
|
|
163
|
-
if (glCanvas)
|
|
164
|
-
{
|
|
165
|
-
glCanvas.style.width = mainCanvas.style.width;
|
|
166
|
-
glCanvas.style.height = mainCanvas.style.height;
|
|
167
|
-
}
|
|
168
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
169
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
168
170
|
}
|
|
169
171
|
}
|
|
170
172
|
else
|
|
171
173
|
{
|
|
172
174
|
// clear and set size to same as window
|
|
173
|
-
mainCanvas.width = min(innerWidth,
|
|
174
|
-
mainCanvas.height = min(innerHeight,
|
|
175
|
+
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
176
|
+
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
// save canvas size and clear overlay canvas
|
|
178
|
-
mainCanvasSize = vec2(overlayCanvas.width = mainCanvas.width, overlayCanvas.height = mainCanvas.height);
|
|
179
|
-
mainContext.imageSmoothingEnabled = !pixelated; // disable smoothing for pixel art
|
|
180
|
-
|
|
181
179
|
// render sort then render while removing destroyed objects
|
|
182
|
-
|
|
180
|
+
enginePreRender();
|
|
183
181
|
gameRender();
|
|
184
182
|
engineObjects.sort((a,b)=> a.renderOrder - b.renderOrder);
|
|
185
183
|
for (const o of engineObjects)
|
|
186
184
|
o.destroyed || o.render();
|
|
187
185
|
gameRenderPost();
|
|
188
186
|
medalsRender();
|
|
187
|
+
touchGamepadRender();
|
|
189
188
|
debugRender();
|
|
190
189
|
glCopyToContext(mainContext);
|
|
191
190
|
|
|
@@ -197,7 +196,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
197
196
|
overlayContext.font = '1em monospace';
|
|
198
197
|
overlayContext.fillStyle = '#000';
|
|
199
198
|
const text = engineName + ' ' + 'v' + engineVersion + ' / '
|
|
200
|
-
+ drawCount + ' / ' + engineObjects.length + ' / ' +
|
|
199
|
+
+ drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
|
|
200
|
+
+ ' ' + (glEnable ? 'GL' : '2D') ;
|
|
201
201
|
overlayContext.fillText(text, mainCanvas.width-3, 3);
|
|
202
202
|
overlayContext.fillStyle = '#fff';
|
|
203
203
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
@@ -209,12 +209,27 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
209
209
|
tileImageSource ? tileImage.src = tileImageSource : tileImage.onload();
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// called by engine to setup render system
|
|
213
|
+
function enginePreRender()
|
|
214
|
+
{
|
|
215
|
+
// save canvas size and clear canvases
|
|
216
|
+
mainCanvasSize = vec2(overlayCanvas.width = mainCanvas.width, overlayCanvas.height = mainCanvas.height);
|
|
217
|
+
|
|
218
|
+
// disable smoothing for pixel art
|
|
219
|
+
mainContext.imageSmoothingEnabled = !cavasPixelated;
|
|
220
|
+
|
|
221
|
+
// setup gl rendering if enabled
|
|
222
|
+
glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
|
|
223
|
+
}
|
|
212
224
|
|
|
213
225
|
///////////////////////////////////////////////////////////////////////////////
|
|
214
226
|
|
|
215
227
|
/** Calls update on each engine object (recursively if child), removes destroyed objects, and updated time */
|
|
216
228
|
function engineObjectsUpdate()
|
|
217
229
|
{
|
|
230
|
+
// get list of solid objects for physics optimzation
|
|
231
|
+
engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
|
|
232
|
+
|
|
218
233
|
// recursive object update
|
|
219
234
|
const updateObject = (o)=>
|
|
220
235
|
{
|
|
@@ -230,42 +245,38 @@ function engineObjectsUpdate()
|
|
|
230
245
|
|
|
231
246
|
// remove destroyed objects
|
|
232
247
|
engineObjects = engineObjects.filter(o=>!o.destroyed);
|
|
233
|
-
engineCollideObjects = engineCollideObjects.filter(o=>!o.destroyed);
|
|
234
248
|
|
|
235
249
|
// increment frame and update time
|
|
236
|
-
time = ++frame /
|
|
250
|
+
time = ++frame / frameRate;
|
|
237
251
|
}
|
|
238
252
|
|
|
239
|
-
/**
|
|
253
|
+
/** Destroy and remove all objects */
|
|
240
254
|
function engineObjectsDestroy()
|
|
241
255
|
{
|
|
242
256
|
for (const o of engineObjects)
|
|
243
|
-
o.
|
|
257
|
+
o.parent || o.destroy();
|
|
244
258
|
engineObjects = engineObjects.filter(o=>!o.destroyed);
|
|
245
259
|
}
|
|
246
260
|
|
|
247
261
|
/** Triggers a callback for each object within a given area
|
|
248
|
-
* @param {Vector2} [pos]
|
|
249
|
-
* @param {Number} [size]
|
|
250
|
-
* @param {Function} [callbackFunction]
|
|
262
|
+
* @param {Vector2} [pos] - Center of test area
|
|
263
|
+
* @param {Number} [size] - Radius of circle if float, rectangle size if Vector2
|
|
264
|
+
* @param {Function} [callbackFunction] - Calls this function on every object that passes the test
|
|
251
265
|
* @param {Array} [objects=engineObjects] - List of objects to check */
|
|
252
266
|
function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
|
|
253
267
|
{
|
|
254
|
-
if (!pos)
|
|
268
|
+
if (!pos) // all objects
|
|
255
269
|
{
|
|
256
|
-
// all objects
|
|
257
270
|
for (const o of objects)
|
|
258
271
|
callbackFunction(o);
|
|
259
272
|
}
|
|
260
|
-
else if (size.x != undefined)
|
|
273
|
+
else if (size.x != undefined) // bounding box test
|
|
261
274
|
{
|
|
262
|
-
// aabb test
|
|
263
275
|
for (const o of objects)
|
|
264
276
|
isOverlapping(pos, size, o.pos, o.size) && callbackFunction(o);
|
|
265
277
|
}
|
|
266
|
-
else
|
|
278
|
+
else // circle test
|
|
267
279
|
{
|
|
268
|
-
// circle test
|
|
269
280
|
const sizeSquared = size*size;
|
|
270
281
|
for (const o of objects)
|
|
271
282
|
pos.distanceSquared(o.pos) < sizeSquared && callbackFunction(o);
|
package/engine/engineAudio.js
CHANGED
|
@@ -1,28 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
2
|
+
* LittleJS Audio System
|
|
3
|
+
* <br> - <a href=https://killedbyapixel.github.io/ZzFX/>ZzFX Sound Effects</a>
|
|
4
|
+
* <br> - <a href=https://keithclark.github.io/ZzFXM/>ZzFXM Music</a>
|
|
5
|
+
* <br> - Caches sounds and music for fast playback
|
|
6
|
+
* <br> - Can attenuate and apply stereo panning to sounds
|
|
7
|
+
* <br> - Ability to play mp3, ogg, and wave files
|
|
8
|
+
* <br> - Speech synthesis wrapper functions
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/**
|
|
14
|
+
* Sound Object - Stores a zzfx sound for later use and can be played positionally
|
|
15
|
+
* <br>
|
|
16
|
+
* <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
|
|
17
|
+
* @example
|
|
18
|
+
* // create a sound
|
|
19
|
+
* const sound_example = new Sound([.5,.5]);
|
|
20
|
+
*
|
|
21
|
+
* // play the sound
|
|
22
|
+
* sound_example.play();
|
|
23
|
+
*/
|
|
14
24
|
class Sound
|
|
15
25
|
{
|
|
16
26
|
/** Create a sound object and cache the zzfx samples for later use
|
|
17
27
|
* @param {Array} zzfxSound - Array of zzfx parameters, ex. [.5,.5]
|
|
18
|
-
* @param {Number} [range=
|
|
19
|
-
* @param {Number} [taper=
|
|
28
|
+
* @param {Number} [range=soundDefaultRange] - World space max range of sound, will not play if camera is farther away
|
|
29
|
+
* @param {Number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering off
|
|
20
30
|
*/
|
|
21
|
-
constructor(zzfxSound, range=
|
|
31
|
+
constructor(zzfxSound, range=soundDefaultRange, taper=soundDefaultTaper)
|
|
22
32
|
{
|
|
23
33
|
if (!soundEnable) return;
|
|
24
34
|
|
|
35
|
+
/** @property {Number} - World space max range of sound, will not play if camera is farther away */
|
|
25
36
|
this.range = range;
|
|
37
|
+
|
|
38
|
+
/** @property {Number} - At what percentage of range should it start tapering off */
|
|
26
39
|
this.taper = taper;
|
|
27
40
|
|
|
28
41
|
// get randomness from sound parameters
|
|
@@ -56,7 +69,7 @@ class Sound
|
|
|
56
69
|
return; // out of range
|
|
57
70
|
|
|
58
71
|
// attenuate volume by distance
|
|
59
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
72
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
// get pan from screen space coords
|
|
@@ -82,7 +95,36 @@ class Sound
|
|
|
82
95
|
}
|
|
83
96
|
}
|
|
84
97
|
|
|
85
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* Music Object - Stores a zzfx music track for later use
|
|
100
|
+
* <br>
|
|
101
|
+
* <br><b><a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a></b>
|
|
102
|
+
* @example
|
|
103
|
+
* // create some music
|
|
104
|
+
* const music_example = new Music(
|
|
105
|
+
* [
|
|
106
|
+
* [ // instruments
|
|
107
|
+
* [,0,400] // simple note
|
|
108
|
+
* ],
|
|
109
|
+
* [ // patterns
|
|
110
|
+
* [ // pattern 1
|
|
111
|
+
* [ // channel 0
|
|
112
|
+
* 0, -1, // instrument 0, left speaker
|
|
113
|
+
* 1, 0, 9, 1 // channel notes
|
|
114
|
+
* ],
|
|
115
|
+
* [ // channel 1
|
|
116
|
+
* 0, 1, // instrument 1, right speaker
|
|
117
|
+
* 0, 12, 17, -1 // channel notes
|
|
118
|
+
* ]
|
|
119
|
+
* ],
|
|
120
|
+
* ],
|
|
121
|
+
* [0, 0, 0, 0], // sequence, play pattern 0 four times
|
|
122
|
+
* 90 // BPM
|
|
123
|
+
* ]);
|
|
124
|
+
*
|
|
125
|
+
* // play the music
|
|
126
|
+
* music_example.play();
|
|
127
|
+
*/
|
|
86
128
|
class Music
|
|
87
129
|
{
|
|
88
130
|
/** Create a music object and cache the zzfx music samples for later use
|
|
@@ -119,7 +161,7 @@ function playAudioFile(url, volume=1, loop=1)
|
|
|
119
161
|
if (!soundEnable) return;
|
|
120
162
|
|
|
121
163
|
const audio = new Audio(url);
|
|
122
|
-
audio.volume =
|
|
164
|
+
audio.volume = soundVolume * volume;
|
|
123
165
|
audio.loop = loop;
|
|
124
166
|
audio.play();
|
|
125
167
|
return audio;
|
|
@@ -144,7 +186,7 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
|
144
186
|
// build utterance and speak
|
|
145
187
|
const utterance = new SpeechSynthesisUtterance(text);
|
|
146
188
|
utterance.lang = language;
|
|
147
|
-
utterance.volume = 2*volume*
|
|
189
|
+
utterance.volume = 2*volume*soundVolume;
|
|
148
190
|
utterance.rate = rate;
|
|
149
191
|
utterance.pitch = pitch;
|
|
150
192
|
speechSynthesis.speak(utterance);
|
|
@@ -153,7 +195,7 @@ function speak(text, language='', volume=1, rate=1, pitch=1)
|
|
|
153
195
|
|
|
154
196
|
/** Stop all queued speech
|
|
155
197
|
* @memberof Audio */
|
|
156
|
-
const
|
|
198
|
+
const speakStop = ()=> speechSynthesis && speechSynthesis.cancel();
|
|
157
199
|
|
|
158
200
|
/** Get frequency of a note on a musical scale
|
|
159
201
|
* @param {Number} semitoneOffset - How many semitones away from the root note
|
|
@@ -184,6 +226,13 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
184
226
|
if (!audioContext)
|
|
185
227
|
audioContext = new (window.AudioContext||webkitAudioContext);
|
|
186
228
|
|
|
229
|
+
// fix stalled audio
|
|
230
|
+
audioContext.resume();
|
|
231
|
+
|
|
232
|
+
// prevent sounds from building up if they can't be played
|
|
233
|
+
if (audioContext.state != 'running')
|
|
234
|
+
return;
|
|
235
|
+
|
|
187
236
|
// create buffer and source
|
|
188
237
|
const buffer = audioContext.createBuffer(sampleChannels.length, sampleChannels[0].length, zzfxR),
|
|
189
238
|
source = audioContext.createBufferSource();
|
|
@@ -194,11 +243,18 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
194
243
|
source.playbackRate.value = rate;
|
|
195
244
|
source.loop = loop;
|
|
196
245
|
|
|
197
|
-
// create
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
246
|
+
// create and connect gain node (createGain is more widley spported then GainNode construtor)
|
|
247
|
+
const gainNode = audioContext.createGain();
|
|
248
|
+
gainNode.gain.value = soundVolume*volume;
|
|
249
|
+
gainNode.connect(audioContext.destination);
|
|
250
|
+
|
|
251
|
+
// connect source to gain
|
|
252
|
+
(
|
|
253
|
+
window.StereoPannerNode ? // create pan node if possible
|
|
254
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
255
|
+
: source
|
|
256
|
+
)
|
|
257
|
+
.connect(gainNode);
|
|
202
258
|
|
|
203
259
|
// play and return sound
|
|
204
260
|
source.start();
|
|
@@ -209,6 +265,8 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
209
265
|
// ZzFXMicro - Zuper Zmall Zound Zynth - v1.1.8 by Frank Force
|
|
210
266
|
|
|
211
267
|
/** Generate and play a ZzFX sound
|
|
268
|
+
* <br>
|
|
269
|
+
* <br><b><a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a></b>
|
|
212
270
|
* @param {Array} zzfxSound - Array of ZzFX parameters, ex. [.5,.5]
|
|
213
271
|
* @return {Array} - Array of audio samples
|
|
214
272
|
* @memberof Audio */
|
|
@@ -265,7 +323,7 @@ function zzfxG
|
|
|
265
323
|
1 - tremolo + tremolo*Math.sin(PI2*i/repeatTime) // tremolo
|
|
266
324
|
: 1) *
|
|
267
325
|
sign(s)*(abs(s)**shapeCurve) * // curve 0=square, 2=pointy
|
|
268
|
-
volume *
|
|
326
|
+
volume * soundVolume * ( // envelope
|
|
269
327
|
i < attack ? i/attack : // attack
|
|
270
328
|
i < attack + decay ? // decay
|
|
271
329
|
1-((i-attack)/decay)*(1-sustainVolume) : // decay falloff
|
package/engine/engineBuild.bat
CHANGED
|
@@ -15,8 +15,6 @@ type engineUtilities.js >> %OUTPUT_FILENAME%
|
|
|
15
15
|
echo.>> %OUTPUT_FILENAME%
|
|
16
16
|
type engineSettings.js >> %OUTPUT_FILENAME%
|
|
17
17
|
echo.>> %OUTPUT_FILENAME%
|
|
18
|
-
type engine.js >> %OUTPUT_FILENAME%
|
|
19
|
-
echo.>> %OUTPUT_FILENAME%
|
|
20
18
|
type engineObject.js >> %OUTPUT_FILENAME%
|
|
21
19
|
echo.>> %OUTPUT_FILENAME%
|
|
22
20
|
type engineDraw.js >> %OUTPUT_FILENAME%
|
|
@@ -33,6 +31,8 @@ type engineMedals.js >> %OUTPUT_FILENAME%
|
|
|
33
31
|
echo.>> %OUTPUT_FILENAME%
|
|
34
32
|
type engineWebGL.js >> %OUTPUT_FILENAME%
|
|
35
33
|
echo.>> %OUTPUT_FILENAME%
|
|
34
|
+
type engine.js >> %OUTPUT_FILENAME%
|
|
35
|
+
echo.>> %OUTPUT_FILENAME%
|
|
36
36
|
|
|
37
37
|
rem --- BUILD ENGINE RELEASE ---
|
|
38
38
|
|
package/engine/engineDebug.js
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LittleJS - Debug Build
|
|
3
|
+
MIT License - Copyright 2021 Frank Force
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
+
* LittleJS Debug System
|
|
8
|
+
* <br> - Press ~ to show debug overlay with mouse pick
|
|
9
|
+
* <br> - Number keys toggle debug functions
|
|
10
|
+
* <br> - +/- apply time scale
|
|
11
|
+
* <br> - Debug primitive rendering
|
|
12
|
+
* <br> - Save a 2d canvas as an image
|
|
13
|
+
* @namespace Debug
|
|
7
14
|
*/
|
|
8
15
|
|
|
9
16
|
'use strict';
|
|
@@ -34,7 +41,7 @@ let showWatermark = 1;
|
|
|
34
41
|
let godMode = 0;
|
|
35
42
|
|
|
36
43
|
// Engine internal variables not exposed to documentation
|
|
37
|
-
let debugPrimitives = [], debugOverlay = 0, debugPhysics = 0, debugRaycast = 0,
|
|
44
|
+
let debugPrimitives = [], debugOverlay = 0, debugPhysics = 0, debugRaycast = 0,
|
|
38
45
|
debugParticles = 0, debugGamepads = 0, debugMedals = 0, debugTakeScreenshot, downloadLink;
|
|
39
46
|
|
|
40
47
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -156,22 +163,25 @@ const debugUpdate = ()=>
|
|
|
156
163
|
|
|
157
164
|
if (keyWasPressed(192)) // ~
|
|
158
165
|
debugOverlay = !debugOverlay;
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
166
|
+
if (debugOverlay)
|
|
167
|
+
{
|
|
168
|
+
if (keyWasPressed(49)) // 1
|
|
169
|
+
debugPhysics = !debugPhysics, debugParticles = 0;
|
|
170
|
+
if (keyWasPressed(50)) // 2
|
|
171
|
+
debugParticles = !debugParticles, debugPhysics = 0;
|
|
172
|
+
if (keyWasPressed(51)) // 3
|
|
173
|
+
godMode = !godMode;
|
|
174
|
+
if (keyWasPressed(53)) // 5
|
|
175
|
+
debugTakeScreenshot = 1;
|
|
176
|
+
//if (keyWasPressed(54)) // 6
|
|
177
|
+
// debugToggleParticleEditor();
|
|
178
|
+
if (keyWasPressed(55)) // 7
|
|
179
|
+
debugGamepads = !debugGamepads;
|
|
180
|
+
//if (keyWasPressed(56)) // 8
|
|
181
|
+
//if (keyWasPressed(57)) // 9
|
|
182
|
+
if (keyWasPressed(48)) // 0
|
|
183
|
+
showWatermark = !showWatermark;
|
|
184
|
+
}
|
|
175
185
|
}
|
|
176
186
|
|
|
177
187
|
const debugRender = ()=>
|
|
@@ -224,12 +234,24 @@ const debugRender = ()=>
|
|
|
224
234
|
|
|
225
235
|
if (debugOverlay)
|
|
226
236
|
{
|
|
237
|
+
// mouse pick
|
|
238
|
+
let bestDistance = Infinity, bestObject;
|
|
227
239
|
for (const o of engineObjects)
|
|
228
240
|
{
|
|
229
|
-
if (o.canvas)
|
|
241
|
+
if (o.canvas || o.destroyed)
|
|
230
242
|
continue; // skip tile layers
|
|
231
243
|
|
|
232
244
|
const size = o.size.copy();
|
|
245
|
+
if (!size.x || !size.y)
|
|
246
|
+
continue;
|
|
247
|
+
|
|
248
|
+
const distance = mousePos.distanceSquared(o.pos);
|
|
249
|
+
if (distance < bestDistance)
|
|
250
|
+
{
|
|
251
|
+
bestDistance = distance;
|
|
252
|
+
bestObject = o
|
|
253
|
+
}
|
|
254
|
+
|
|
233
255
|
size.x = max(size.x, .2);
|
|
234
256
|
size.y = max(size.y, .2);
|
|
235
257
|
|
|
@@ -244,18 +266,6 @@ const debugRender = ()=>
|
|
|
244
266
|
drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
|
|
245
267
|
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
|
|
246
268
|
}
|
|
247
|
-
|
|
248
|
-
// mouse pick
|
|
249
|
-
let bestDistance = Infinity, bestObject;
|
|
250
|
-
for (const o of engineObjects)
|
|
251
|
-
{
|
|
252
|
-
const distance = mousePos.distanceSquared(o.pos);
|
|
253
|
-
if (distance < bestDistance)
|
|
254
|
-
{
|
|
255
|
-
bestDistance = distance;
|
|
256
|
-
bestObject = o
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
269
|
|
|
260
270
|
if (bestObject)
|
|
261
271
|
{
|
|
@@ -275,7 +285,8 @@ const debugRender = ()=>
|
|
|
275
285
|
pos = pos.add(height), ...args);
|
|
276
286
|
drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
|
|
277
287
|
drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
|
|
278
|
-
drawText('
|
|
288
|
+
drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
|
|
289
|
+
drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
|
|
279
290
|
mainContext = saveContext;
|
|
280
291
|
}
|
|
281
292
|
|
|
@@ -361,6 +372,24 @@ const debugRender = ()=>
|
|
|
361
372
|
//overlayContext.fillText('6: Particle Editor', x, y += h);
|
|
362
373
|
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
363
374
|
overlayContext.fillText('7: Debug Gamepads', x, y += h);
|
|
375
|
+
|
|
376
|
+
let keysPressed = '';
|
|
377
|
+
for(const i in inputData[0])
|
|
378
|
+
{
|
|
379
|
+
if (i && keyIsDown(i, 0))
|
|
380
|
+
keysPressed += i + ' ' ;
|
|
381
|
+
}
|
|
382
|
+
keysPressed && overlayContext.fillText('Keys Down: ' + keysPressed, x, y += h);
|
|
383
|
+
|
|
384
|
+
let buttonsPressed = '';
|
|
385
|
+
if (inputData[1])
|
|
386
|
+
for(const i in inputData[1])
|
|
387
|
+
{
|
|
388
|
+
if (i && keyIsDown(i, 1))
|
|
389
|
+
buttonsPressed += i + ' ' ;
|
|
390
|
+
}
|
|
391
|
+
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
392
|
+
|
|
364
393
|
}
|
|
365
394
|
else
|
|
366
395
|
{
|