littlejsengine 1.9.5 → 1.9.7
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 +76 -58
- package/dist/littlejs.esm.js +349 -221
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +346 -221
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +343 -217
- package/examples/breakout/index.html +3 -3
- package/examples/breakoutTutorial/index.html +2 -2
- package/examples/electron/index.html +2 -2
- package/examples/js13k/build.js +1 -0
- package/examples/js13k/index.html +13 -13
- package/examples/module/index.html +1 -1
- package/examples/particles/index.html +1 -1
- package/examples/platformer/index.html +8 -8
- package/examples/puzzle/index.html +2 -2
- package/examples/starter/index.html +13 -13
- package/examples/stress/index.html +1 -1
- package/examples/typescript/index.html +1 -1
- package/package.json +1 -1
- package/src/engine.js +64 -40
- package/src/engineAudio.js +67 -46
- package/src/engineDebug.js +3 -4
- package/src/engineDraw.js +13 -10
- package/src/engineExport.js +3 -0
- package/src/engineInput.js +80 -64
- package/src/engineObject.js +35 -9
- package/src/engineParticles.js +17 -13
- package/src/engineSettings.js +19 -3
- package/src/engineTileLayer.js +15 -4
- package/src/engineUtilities.js +13 -10
- package/src/engineWebGL.js +20 -18
- package/LittleJS.zip +0 -0
package/src/engineDraw.js
CHANGED
|
@@ -74,6 +74,9 @@ let drawCount;
|
|
|
74
74
|
*/
|
|
75
75
|
function tile(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
76
76
|
{
|
|
77
|
+
if (headlessMode)
|
|
78
|
+
return new TileInfo;
|
|
79
|
+
|
|
77
80
|
// if size is a number, make it a vector
|
|
78
81
|
if (typeof size === 'number')
|
|
79
82
|
{
|
|
@@ -107,9 +110,9 @@ class TileInfo
|
|
|
107
110
|
constructor(pos=vec2(), size=tileSizeDefault, textureIndex=0)
|
|
108
111
|
{
|
|
109
112
|
/** @property {Vector2} - Top left corner of tile in pixels */
|
|
110
|
-
this.pos = pos;
|
|
113
|
+
this.pos = pos.copy();
|
|
111
114
|
/** @property {Vector2} - Size of tile in pixels */
|
|
112
|
-
this.size = size;
|
|
115
|
+
this.size = size.copy();
|
|
113
116
|
/** @property {Number} - Texture index to use */
|
|
114
117
|
this.textureIndex = textureIndex;
|
|
115
118
|
}
|
|
@@ -201,7 +204,7 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
201
204
|
* @param {Color} [additiveColor=(0,0,0,0)] - Additive color to be applied
|
|
202
205
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
203
206
|
* @param {Boolean} [screenSpace=false] - If true the pos and size are in screen space
|
|
204
|
-
* @param {CanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
207
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
205
208
|
* @memberof Draw */
|
|
206
209
|
function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
207
210
|
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace, context)
|
|
@@ -274,7 +277,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
274
277
|
* @param {Number} [angle]
|
|
275
278
|
* @param {Boolean} [useWebGL=glEnable]
|
|
276
279
|
* @param {Boolean} [screenSpace=false]
|
|
277
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
280
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
278
281
|
* @memberof Draw */
|
|
279
282
|
function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
280
283
|
{
|
|
@@ -288,7 +291,7 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
288
291
|
* @param {Color} [color=(1,1,1,1)]
|
|
289
292
|
* @param {Boolean} [useWebGL=glEnable]
|
|
290
293
|
* @param {Boolean} [screenSpace=false]
|
|
291
|
-
* @param {CanvasRenderingContext2D} [context]
|
|
294
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
292
295
|
* @memberof Draw */
|
|
293
296
|
function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, context)
|
|
294
297
|
{
|
|
@@ -304,7 +307,7 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
|
|
|
304
307
|
* @param {Boolean} mirror
|
|
305
308
|
* @param {Function} drawFunction
|
|
306
309
|
* @param {Boolean} [screenSpace=false]
|
|
307
|
-
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
310
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
308
311
|
* @memberof Draw */
|
|
309
312
|
function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, context=mainContext)
|
|
310
313
|
{
|
|
@@ -325,7 +328,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
325
328
|
/** Enable normal or additive blend mode
|
|
326
329
|
* @param {Boolean} [additive]
|
|
327
330
|
* @param {Boolean} [useWebGL=glEnable]
|
|
328
|
-
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
331
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=mainContext]
|
|
329
332
|
* @memberof Draw */
|
|
330
333
|
function setBlendMode(additive, useWebGL=glEnable, context)
|
|
331
334
|
{
|
|
@@ -350,7 +353,7 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
350
353
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
351
354
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
352
355
|
* @param {String} [font=fontDefault]
|
|
353
|
-
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
356
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
354
357
|
* @memberof Draw */
|
|
355
358
|
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, context)
|
|
356
359
|
{
|
|
@@ -367,7 +370,7 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
367
370
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
368
371
|
* @param {CanvasTextAlign} [textAlign]
|
|
369
372
|
* @param {String} [font=fontDefault]
|
|
370
|
-
* @param {CanvasRenderingContext2D} [context=overlayContext]
|
|
373
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
371
374
|
* @memberof Draw */
|
|
372
375
|
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault, context=overlayContext)
|
|
373
376
|
{
|
|
@@ -410,7 +413,7 @@ class FontImage
|
|
|
410
413
|
* @param {HTMLImageElement} [image] - Image for the font, if undefined default font is used
|
|
411
414
|
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
412
415
|
* @param {Vector2} [paddingSize=(0,1)] - How much extra space to add between characters
|
|
413
|
-
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
416
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
414
417
|
*/
|
|
415
418
|
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), context=overlayContext)
|
|
416
419
|
{
|
package/src/engineExport.js
CHANGED
|
@@ -45,6 +45,7 @@ export {
|
|
|
45
45
|
canvasPixelated,
|
|
46
46
|
fontDefault,
|
|
47
47
|
showSplashScreen,
|
|
48
|
+
headlessMode,
|
|
48
49
|
tileSizeDefault,
|
|
49
50
|
tileFixBleedScale,
|
|
50
51
|
enablePhysicsSolver,
|
|
@@ -83,6 +84,7 @@ export {
|
|
|
83
84
|
setCanvasPixelated,
|
|
84
85
|
setFontDefault,
|
|
85
86
|
setShowSplashScreen,
|
|
87
|
+
setHeadlessMode,
|
|
86
88
|
setGlEnable,
|
|
87
89
|
setGlOverlay,
|
|
88
90
|
setTileSizeDefault,
|
|
@@ -176,6 +178,7 @@ export {
|
|
|
176
178
|
FontImage,
|
|
177
179
|
isFullscreen,
|
|
178
180
|
toggleFullscreen,
|
|
181
|
+
getCameraSize,
|
|
179
182
|
|
|
180
183
|
// WebGL
|
|
181
184
|
glCanvas,
|
package/src/engineInput.js
CHANGED
|
@@ -122,7 +122,7 @@ function gamepadWasReleased(button, gamepad=0)
|
|
|
122
122
|
* @return {Vector2}
|
|
123
123
|
* @memberof Input */
|
|
124
124
|
function gamepadStick(stick, gamepad=0)
|
|
125
|
-
{ return
|
|
125
|
+
{ return gamepadStickData[gamepad] ? gamepadStickData[gamepad][stick] || vec2() : vec2(); }
|
|
126
126
|
|
|
127
127
|
///////////////////////////////////////////////////////////////////////////////
|
|
128
128
|
// Input update called by engine
|
|
@@ -133,6 +133,8 @@ let inputData = [[]];
|
|
|
133
133
|
|
|
134
134
|
function inputUpdate()
|
|
135
135
|
{
|
|
136
|
+
if (headlessMode) return;
|
|
137
|
+
|
|
136
138
|
// clear input when lost focus (prevent stuck keys)
|
|
137
139
|
isTouchDevice || document.hasFocus() || clearInput();
|
|
138
140
|
|
|
@@ -145,6 +147,8 @@ function inputUpdate()
|
|
|
145
147
|
|
|
146
148
|
function inputUpdatePost()
|
|
147
149
|
{
|
|
150
|
+
if (headlessMode) return;
|
|
151
|
+
|
|
148
152
|
// clear input to prepare for next frame
|
|
149
153
|
for (const deviceInputData of inputData)
|
|
150
154
|
for (const i in deviceInputData)
|
|
@@ -153,9 +157,12 @@ function inputUpdatePost()
|
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
///////////////////////////////////////////////////////////////////////////////
|
|
156
|
-
//
|
|
160
|
+
// Input event handlers
|
|
157
161
|
|
|
162
|
+
function inputInit()
|
|
158
163
|
{
|
|
164
|
+
if (headlessMode) return;
|
|
165
|
+
|
|
159
166
|
onkeydown = (e)=>
|
|
160
167
|
{
|
|
161
168
|
if (debug && e.target != document.body) return;
|
|
@@ -186,21 +193,29 @@ function inputUpdatePost()
|
|
|
186
193
|
c == 'KeyA' ? 'ArrowLeft' :
|
|
187
194
|
c == 'KeyD' ? 'ArrowRight' : c : c;
|
|
188
195
|
}
|
|
196
|
+
|
|
197
|
+
// mouse event handlers
|
|
198
|
+
onmousedown = (e)=>
|
|
199
|
+
{
|
|
200
|
+
isUsingGamepad = false;
|
|
201
|
+
inputData[0][e.button] = 3;
|
|
202
|
+
mousePosScreen = mouseToScreen(e);
|
|
203
|
+
e.button && e.preventDefault();
|
|
204
|
+
}
|
|
205
|
+
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
206
|
+
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
207
|
+
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
208
|
+
oncontextmenu = (e)=> false; // prevent right click menu
|
|
209
|
+
|
|
210
|
+
// init touch input
|
|
211
|
+
if (isTouchDevice)
|
|
212
|
+
touchInputInit();
|
|
189
213
|
}
|
|
190
214
|
|
|
191
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
192
|
-
// Mouse event handlers
|
|
193
|
-
|
|
194
|
-
onmousedown = (e)=> {isUsingGamepad = false; inputData[0][e.button] = 3; mousePosScreen = mouseToScreen(e); e.button && e.preventDefault();}
|
|
195
|
-
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
196
|
-
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
197
|
-
onwheel = (e)=> mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
198
|
-
oncontextmenu = (e)=> false; // prevent right click menu
|
|
199
|
-
|
|
200
215
|
// convert a mouse or touch event position to screen space
|
|
201
216
|
function mouseToScreen(mousePos)
|
|
202
217
|
{
|
|
203
|
-
if (!mainCanvas)
|
|
218
|
+
if (!mainCanvas || headlessMode)
|
|
204
219
|
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
205
220
|
|
|
206
221
|
const rect = mainCanvas.getBoundingClientRect();
|
|
@@ -212,7 +227,7 @@ function mouseToScreen(mousePos)
|
|
|
212
227
|
// Gamepad input
|
|
213
228
|
|
|
214
229
|
// gamepad internal variables
|
|
215
|
-
const
|
|
230
|
+
const gamepadStickData = [];
|
|
216
231
|
|
|
217
232
|
// gamepads are updated by engine every frame automatically
|
|
218
233
|
function gamepadsUpdate()
|
|
@@ -229,14 +244,11 @@ function gamepadsUpdate()
|
|
|
229
244
|
// update touch gamepad if enabled
|
|
230
245
|
if (touchGamepadEnable && isTouchDevice)
|
|
231
246
|
{
|
|
232
|
-
|
|
233
|
-
if (!touchGamepadButtons)
|
|
234
|
-
createTouchGamepad();
|
|
235
|
-
|
|
247
|
+
ASSERT(touchGamepadButtons, 'set touchGamepadEnable before calling init!');
|
|
236
248
|
if (touchGamepadTimer.isSet())
|
|
237
249
|
{
|
|
238
250
|
// read virtual analog stick
|
|
239
|
-
const sticks =
|
|
251
|
+
const sticks = gamepadStickData[0] || (gamepadStickData[0] = []);
|
|
240
252
|
sticks[0] = vec2();
|
|
241
253
|
if (touchGamepadAnalog)
|
|
242
254
|
sticks[0] = applyDeadZones(touchGamepadStick);
|
|
@@ -253,7 +265,8 @@ function gamepadsUpdate()
|
|
|
253
265
|
for (let i=10; i--;)
|
|
254
266
|
{
|
|
255
267
|
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
256
|
-
|
|
268
|
+
const wasDown = gamepadIsDown(j,0);
|
|
269
|
+
data[j] = touchGamepadButtons[i] ? wasDown ? 1 : 3 : wasDown ? 4 : 0;
|
|
257
270
|
}
|
|
258
271
|
}
|
|
259
272
|
}
|
|
@@ -273,7 +286,7 @@ function gamepadsUpdate()
|
|
|
273
286
|
// get or create gamepad data
|
|
274
287
|
const gamepad = gamepads[i];
|
|
275
288
|
const data = inputData[i+1] || (inputData[i+1] = []);
|
|
276
|
-
const sticks =
|
|
289
|
+
const sticks = gamepadStickData[i] || (gamepadStickData[i] = []);
|
|
277
290
|
|
|
278
291
|
if (gamepad)
|
|
279
292
|
{
|
|
@@ -312,28 +325,44 @@ function gamepadsUpdate()
|
|
|
312
325
|
* @param {Number|Array} [pattern] - single value in ms or vibration interval array
|
|
313
326
|
* @memberof Input */
|
|
314
327
|
function vibrate(pattern=100)
|
|
315
|
-
{ vibrateEnable && navigator && navigator.vibrate && navigator.vibrate(pattern); }
|
|
328
|
+
{ vibrateEnable && !headlessMode && navigator && navigator.vibrate && navigator.vibrate(pattern); }
|
|
316
329
|
|
|
317
330
|
/** Cancel any ongoing vibration
|
|
318
331
|
* @memberof Input */
|
|
319
332
|
function vibrateStop() { vibrate(0); }
|
|
320
333
|
|
|
321
334
|
///////////////////////////////////////////////////////////////////////////////
|
|
322
|
-
// Touch input
|
|
335
|
+
// Touch input & virtual on screen gamepad
|
|
323
336
|
|
|
324
337
|
/** True if a touch device has been detected
|
|
325
338
|
* @memberof Input */
|
|
326
|
-
const isTouchDevice = window.ontouchstart !== undefined;
|
|
339
|
+
const isTouchDevice = !headlessMode && window.ontouchstart !== undefined;
|
|
340
|
+
|
|
341
|
+
// touch gamepad internal variables
|
|
342
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
327
343
|
|
|
328
344
|
// try to enable touch mouse
|
|
329
|
-
|
|
345
|
+
function touchInputInit()
|
|
330
346
|
{
|
|
347
|
+
// add non passive touch event listeners
|
|
348
|
+
let handleTouch = handleTouchDefault;
|
|
349
|
+
if (touchGamepadEnable)
|
|
350
|
+
{
|
|
351
|
+
// touch input internal variables
|
|
352
|
+
handleTouch = handleTouchGamepad;
|
|
353
|
+
touchGamepadButtons = [];
|
|
354
|
+
touchGamepadStick = vec2();
|
|
355
|
+
}
|
|
356
|
+
document.addEventListener('touchstart', (e) => handleTouch(e), { passive: false });
|
|
357
|
+
document.addEventListener('touchmove', (e) => handleTouch(e), { passive: false });
|
|
358
|
+
document.addEventListener('touchend', (e) => handleTouch(e), { passive: false });
|
|
359
|
+
|
|
331
360
|
// override mouse events
|
|
332
|
-
let wasTouching;
|
|
333
361
|
onmousedown = onmouseup = ()=> 0;
|
|
334
362
|
|
|
335
363
|
// handle all touch events the same way
|
|
336
|
-
|
|
364
|
+
let wasTouching;
|
|
365
|
+
function handleTouchDefault(e)
|
|
337
366
|
{
|
|
338
367
|
// fix stalled audio requiring user interaction
|
|
339
368
|
if (soundEnable && audioContext && audioContext.state != 'running')
|
|
@@ -362,27 +391,14 @@ if (isTouchDevice)
|
|
|
362
391
|
// must return true so the document will get focus
|
|
363
392
|
return true;
|
|
364
393
|
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
368
|
-
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
369
|
-
|
|
370
|
-
// touch input internal variables
|
|
371
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
372
394
|
|
|
373
|
-
//
|
|
374
|
-
function
|
|
375
|
-
{
|
|
376
|
-
// touch input internal variables
|
|
377
|
-
touchGamepadButtons = [];
|
|
378
|
-
touchGamepadStick = vec2();
|
|
379
|
-
|
|
380
|
-
const touchHandler = ontouchstart;
|
|
381
|
-
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
395
|
+
// special handling for virtual gamepad mode
|
|
396
|
+
function handleTouchGamepad(e)
|
|
382
397
|
{
|
|
383
398
|
// clear touch gamepad input
|
|
384
399
|
touchGamepadStick = vec2();
|
|
385
400
|
touchGamepadButtons = [];
|
|
401
|
+
isUsingGamepad = true;
|
|
386
402
|
|
|
387
403
|
const touching = e.touches.length;
|
|
388
404
|
if (touching)
|
|
@@ -423,9 +439,8 @@ function createTouchGamepad()
|
|
|
423
439
|
}
|
|
424
440
|
}
|
|
425
441
|
|
|
426
|
-
// call default touch handler
|
|
427
|
-
|
|
428
|
-
isUsingGamepad = true;
|
|
442
|
+
// call default touch handler so normal touch events still work
|
|
443
|
+
handleTouchDefault(e);
|
|
429
444
|
|
|
430
445
|
// must return true so the document will get focus
|
|
431
446
|
return true;
|
|
@@ -444,32 +459,33 @@ function touchGamepadRender()
|
|
|
444
459
|
return;
|
|
445
460
|
|
|
446
461
|
// setup the canvas
|
|
447
|
-
overlayContext
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
const context = overlayContext;
|
|
463
|
+
context.save();
|
|
464
|
+
context.globalAlpha = alpha*touchGamepadAlpha;
|
|
465
|
+
context.strokeStyle = '#fff';
|
|
466
|
+
context.lineWidth = 3;
|
|
451
467
|
|
|
452
468
|
// draw left analog stick
|
|
453
|
-
|
|
454
|
-
|
|
469
|
+
context.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
470
|
+
context.beginPath();
|
|
455
471
|
|
|
456
472
|
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
457
473
|
if (touchGamepadAnalog) // draw circle shaped gamepad
|
|
458
474
|
{
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
475
|
+
context.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
476
|
+
context.fill();
|
|
477
|
+
context.stroke();
|
|
462
478
|
}
|
|
463
479
|
else // draw cross shaped gamepad
|
|
464
480
|
{
|
|
465
481
|
for(let i=10; i--;)
|
|
466
482
|
{
|
|
467
483
|
const angle = i*PI/4;
|
|
468
|
-
|
|
469
|
-
i%2 &&
|
|
470
|
-
i==1 &&
|
|
484
|
+
context.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
485
|
+
i%2 && context.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
486
|
+
i==1 && context.fill();
|
|
471
487
|
}
|
|
472
|
-
|
|
488
|
+
context.stroke();
|
|
473
489
|
}
|
|
474
490
|
|
|
475
491
|
// draw right face buttons
|
|
@@ -477,13 +493,13 @@ function touchGamepadRender()
|
|
|
477
493
|
for (let i=4; i--;)
|
|
478
494
|
{
|
|
479
495
|
const pos = rightCenter.add(vec2().setDirection(i, touchGamepadSize/2));
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
496
|
+
context.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
|
|
497
|
+
context.beginPath();
|
|
498
|
+
context.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
|
|
499
|
+
context.fill();
|
|
500
|
+
context.stroke();
|
|
485
501
|
}
|
|
486
502
|
|
|
487
503
|
// set canvas back to normal
|
|
488
|
-
|
|
504
|
+
context.restore();
|
|
489
505
|
}
|
package/src/engineObject.js
CHANGED
|
@@ -85,6 +85,8 @@ class EngineObject
|
|
|
85
85
|
this.spawnTime = time;
|
|
86
86
|
/** @property {Array} - List of children of this object */
|
|
87
87
|
this.children = [];
|
|
88
|
+
/** @property {Boolean} - Limit object speed using linear or circular math */
|
|
89
|
+
this.clampSpeedLinear = true;
|
|
88
90
|
|
|
89
91
|
// parent child system
|
|
90
92
|
/** @property {EngineObject} - Parent of object if in local space */
|
|
@@ -108,21 +110,46 @@ class EngineObject
|
|
|
108
110
|
engineObjects.push(this);
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
/** Update the object transform
|
|
112
|
-
|
|
113
|
+
/** Update the object transform, called automatically by engine even when paused */
|
|
114
|
+
updateTransforms()
|
|
113
115
|
{
|
|
114
116
|
const parent = this.parent;
|
|
115
117
|
if (parent)
|
|
116
118
|
{
|
|
117
119
|
// copy parent pos/angle
|
|
118
|
-
|
|
119
|
-
this.
|
|
120
|
-
|
|
120
|
+
const mirror = parent.getMirrorSign();
|
|
121
|
+
this.pos = this.localPos.multiply(vec2(mirror,1)).rotate(-parent.angle).add(parent.pos);
|
|
122
|
+
this.angle = mirror*this.localAngle + parent.angle;
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
// update children
|
|
126
|
+
for (const child of this.children)
|
|
127
|
+
child.updateTransforms();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Update the object physics, called automatically by engine once each frame */
|
|
131
|
+
update()
|
|
132
|
+
{
|
|
133
|
+
// child objects do not have physics
|
|
134
|
+
if (this.parent)
|
|
135
|
+
return;
|
|
136
|
+
|
|
123
137
|
// limit max speed to prevent missing collisions
|
|
124
|
-
|
|
125
|
-
|
|
138
|
+
if (this.clampSpeedLinear)
|
|
139
|
+
{
|
|
140
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
141
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
142
|
+
}
|
|
143
|
+
else
|
|
144
|
+
{
|
|
145
|
+
const length2 = this.velocity.lengthSquared();
|
|
146
|
+
if (length2 > objectMaxSpeed*objectMaxSpeed)
|
|
147
|
+
{
|
|
148
|
+
const s = objectMaxSpeed / length2**.5;
|
|
149
|
+
this.velocity.x *= s;
|
|
150
|
+
this.velocity.y *= s;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
126
153
|
|
|
127
154
|
// apply physics
|
|
128
155
|
const oldPos = this.pos.copy();
|
|
@@ -134,8 +161,7 @@ class EngineObject
|
|
|
134
161
|
// physics sanity checks
|
|
135
162
|
ASSERT(this.angleDamping >= 0 && this.angleDamping <= 1);
|
|
136
163
|
ASSERT(this.damping >= 0 && this.damping <= 1);
|
|
137
|
-
|
|
138
|
-
if (!enablePhysicsSolver || !this.mass) // do not update collision for fixed objects
|
|
164
|
+
if (!enablePhysicsSolver || !this.mass) // dont do collision for fixed objects
|
|
139
165
|
return;
|
|
140
166
|
|
|
141
167
|
const wasMovingDown = this.velocity.y < 0;
|
package/src/engineParticles.js
CHANGED
|
@@ -233,20 +233,21 @@ class ParticleEmitter extends EngineObject
|
|
|
233
233
|
class Particle extends EngineObject
|
|
234
234
|
{
|
|
235
235
|
/**
|
|
236
|
-
* Create a particle with the
|
|
237
|
-
*
|
|
238
|
-
* @param {
|
|
239
|
-
* @param {
|
|
240
|
-
* @param {
|
|
241
|
-
* @param {Color}
|
|
242
|
-
* @param {
|
|
243
|
-
* @param {Number}
|
|
244
|
-
* @param {Number}
|
|
245
|
-
* @param {Number}
|
|
246
|
-
* @param {
|
|
247
|
-
* @param {
|
|
236
|
+
* Create a particle with the passed in settings
|
|
237
|
+
* Typically this is created automatically by a ParticleEmitter
|
|
238
|
+
* @param {Vector2} position - World space position of the particle
|
|
239
|
+
* @param {TileInfo} tileInfo - Tile info to render particles
|
|
240
|
+
* @param {Number} angle - Angle to rotate the particle
|
|
241
|
+
* @param {Color} colorStart - Color at start of life
|
|
242
|
+
* @param {Color} colorEnd - Color at end of life
|
|
243
|
+
* @param {Number} lifeTime - How long to live for
|
|
244
|
+
* @param {Number} sizeStart - Size at start of life
|
|
245
|
+
* @param {Number} sizeEnd - Size at end of life
|
|
246
|
+
* @param {Number} fadeRate - How quick to fade in/out
|
|
247
|
+
* @param {Boolean} additive - Does it use additive blend mode
|
|
248
|
+
* @param {Number} trailScale - If a trail, how long to make it
|
|
248
249
|
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
249
|
-
* @param {Function}
|
|
250
|
+
* @param {Function} [destroyCallback] - Callback when particle dies
|
|
250
251
|
*/
|
|
251
252
|
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
252
253
|
)
|
|
@@ -273,6 +274,9 @@ class Particle extends EngineObject
|
|
|
273
274
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
274
275
|
/** @property {Function} - Called when particle dies */
|
|
275
276
|
this.destroyCallback = destroyCallback;
|
|
277
|
+
|
|
278
|
+
// particles use circular clamped speed
|
|
279
|
+
this.clampSpeedLinear = false;
|
|
276
280
|
}
|
|
277
281
|
|
|
278
282
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
package/src/engineSettings.js
CHANGED
|
@@ -55,6 +55,12 @@ let fontDefault = 'arial';
|
|
|
55
55
|
* @memberof Settings */
|
|
56
56
|
let showSplashScreen = false;
|
|
57
57
|
|
|
58
|
+
/** Disables all rendering, audio, and input for servers
|
|
59
|
+
* @type {Boolean}
|
|
60
|
+
* @default
|
|
61
|
+
* @memberof Settings */
|
|
62
|
+
let headlessMode = false;
|
|
63
|
+
|
|
58
64
|
///////////////////////////////////////////////////////////////////////////////
|
|
59
65
|
// WebGL settings
|
|
60
66
|
|
|
@@ -83,7 +89,7 @@ let tileSizeDefault = vec2(16);
|
|
|
83
89
|
* @type {Number}
|
|
84
90
|
* @default
|
|
85
91
|
* @memberof Settings */
|
|
86
|
-
let tileFixBleedScale = .
|
|
92
|
+
let tileFixBleedScale = .5;
|
|
87
93
|
|
|
88
94
|
///////////////////////////////////////////////////////////////////////////////
|
|
89
95
|
// Object settings
|
|
@@ -208,7 +214,7 @@ let soundEnable = true;
|
|
|
208
214
|
* @type {Number}
|
|
209
215
|
* @default
|
|
210
216
|
* @memberof Settings */
|
|
211
|
-
let soundVolume = .
|
|
217
|
+
let soundVolume = .3;
|
|
212
218
|
|
|
213
219
|
/** Default range where sound no longer plays
|
|
214
220
|
* @type {Number}
|
|
@@ -293,6 +299,11 @@ function setFontDefault(font) { fontDefault = font; }
|
|
|
293
299
|
* @memberof Settings */
|
|
294
300
|
function setShowSplashScreen(show) { showSplashScreen = show; }
|
|
295
301
|
|
|
302
|
+
/** Set to disalbe rendering, audio, and input for servers
|
|
303
|
+
* @param {Boolean} headless
|
|
304
|
+
* @memberof Settings */
|
|
305
|
+
function setHeadlessMode(headless) { headlessMode = headless; }
|
|
306
|
+
|
|
296
307
|
/** Set if webgl rendering is enabled
|
|
297
308
|
* @param {Boolean} enable
|
|
298
309
|
* @memberof Settings */
|
|
@@ -406,7 +417,12 @@ function setSoundEnable(enable) { soundEnable = enable; }
|
|
|
406
417
|
/** Set volume scale to apply to all sound, music and speech
|
|
407
418
|
* @param {Number} volume
|
|
408
419
|
* @memberof Settings */
|
|
409
|
-
function setSoundVolume(volume)
|
|
420
|
+
function setSoundVolume(volume)
|
|
421
|
+
{
|
|
422
|
+
soundVolume = volume;
|
|
423
|
+
if (soundEnable && !headlessMode && audioGainNode)
|
|
424
|
+
audioGainNode.gain.value = volume; // update gain immediatly
|
|
425
|
+
}
|
|
410
426
|
|
|
411
427
|
/** Set default range where sound no longer plays
|
|
412
428
|
* @param {Number} range
|
package/src/engineTileLayer.js
CHANGED
|
@@ -181,7 +181,7 @@ class TileLayer extends EngineObject
|
|
|
181
181
|
|
|
182
182
|
/** @property {HTMLCanvasElement} - The canvas used by this tile layer */
|
|
183
183
|
this.canvas = document.createElement('canvas');
|
|
184
|
-
/** @property {CanvasRenderingContext2D} - The 2D canvas context used by this tile layer */
|
|
184
|
+
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this tile layer */
|
|
185
185
|
this.context = this.canvas.getContext('2d');
|
|
186
186
|
/** @property {Vector2} - How much to scale this layer when rendered */
|
|
187
187
|
this.scale = scale;
|
|
@@ -192,6 +192,17 @@ class TileLayer extends EngineObject
|
|
|
192
192
|
this.data = [];
|
|
193
193
|
for (let j = this.size.area(); j--;)
|
|
194
194
|
this.data.push(new TileLayerData);
|
|
195
|
+
|
|
196
|
+
if (headlessMode)
|
|
197
|
+
{
|
|
198
|
+
// disable rendering
|
|
199
|
+
this.redraw = () => {};
|
|
200
|
+
this.render = () => {};
|
|
201
|
+
this.redrawStart = () => {};
|
|
202
|
+
this.redrawEnd = () => {};
|
|
203
|
+
this.drawTileData = () => {};
|
|
204
|
+
this.drawCanvas2D = () => {};
|
|
205
|
+
}
|
|
195
206
|
}
|
|
196
207
|
|
|
197
208
|
/** Set data at a given position in the array
|
|
@@ -222,7 +233,7 @@ class TileLayer extends EngineObject
|
|
|
222
233
|
ASSERT(mainContext != this.context, 'must call redrawEnd() after drawing tiles');
|
|
223
234
|
|
|
224
235
|
// flush and copy gl canvas because tile canvas does not use webgl
|
|
225
|
-
|
|
236
|
+
!glOverlay && !this.isOverlay && glCopyToContext(mainContext);
|
|
226
237
|
|
|
227
238
|
// draw the entire cached level onto the canvas
|
|
228
239
|
const pos = worldToScreen(this.pos.add(vec2(0,this.size.y*this.scale.y)));
|
|
@@ -272,14 +283,14 @@ class TileLayer extends EngineObject
|
|
|
272
283
|
this.context.imageSmoothingEnabled = !canvasPixelated;
|
|
273
284
|
|
|
274
285
|
// setup gl rendering if enabled
|
|
275
|
-
|
|
286
|
+
glPreRender();
|
|
276
287
|
}
|
|
277
288
|
|
|
278
289
|
/** Call to end the redraw process */
|
|
279
290
|
redrawEnd()
|
|
280
291
|
{
|
|
281
292
|
ASSERT(mainContext == this.context, 'must call redrawStart() before drawing tiles');
|
|
282
|
-
|
|
293
|
+
glCopyToContext(mainContext, true);
|
|
283
294
|
//debugSaveCanvas(this.canvas);
|
|
284
295
|
|
|
285
296
|
// set stuff back to normal
|