littlejsengine 1.1.26 → 1.2.0
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 +3 -1
- package/engine/engine.all.js +248 -324
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +223 -105
- package/engine/engine.js +34 -11
- package/engine/engineAudio.js +2 -2
- package/engine/engineBuild.bat +9 -1
- package/engine/engineBuildSetup.bat +2 -1
- package/engine/engineDebug.js +26 -220
- package/engine/engineDraw.js +98 -9
- package/engine/engineInput.js +13 -6
- package/engine/engineObject.js +13 -10
- package/engine/engineTileLayer.js +11 -9
- package/engine/engineUtilities.js +12 -12
- package/engine/engineWebGL.js +40 -46
- package/engine/index.d.ts +1612 -0
- package/examples/breakout/game.js +9 -7
- package/examples/breakout/gameObjects.js +1 -1
- package/examples/breakout/index.html +3 -3
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +1 -1
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/platformer/gameObjects.js +1 -1
- package/examples/platformer/gamePlayer.js +11 -6
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/game.js +1 -1
- package/examples/puzzle/index.html +2 -2
- package/examples/stress/index.html +3 -3
- package/game.js +0 -3
- package/index.html +13 -13
- package/package.json +1 -1
package/engine/engine.all.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* LittleJS Debug System
|
|
8
8
|
* <br> - Press ~ to show debug overlay with mouse pick
|
|
9
9
|
* <br> - Number keys toggle debug functions
|
|
10
|
+
* <br> - +/- apply time scale
|
|
10
11
|
* <br> - Debug primitive rendering
|
|
11
12
|
* <br> - Save a 2d canvas as an image
|
|
12
13
|
* @namespace Debug
|
|
@@ -164,22 +165,22 @@ const debugUpdate = ()=>
|
|
|
164
165
|
debugOverlay = !debugOverlay;
|
|
165
166
|
if (debugOverlay)
|
|
166
167
|
{
|
|
168
|
+
if (keyWasPressed(48)) // 0
|
|
169
|
+
showWatermark = !showWatermark;
|
|
167
170
|
if (keyWasPressed(49)) // 1
|
|
168
171
|
debugPhysics = !debugPhysics, debugParticles = 0;
|
|
169
172
|
if (keyWasPressed(50)) // 2
|
|
170
173
|
debugParticles = !debugParticles, debugPhysics = 0;
|
|
171
174
|
if (keyWasPressed(51)) // 3
|
|
175
|
+
debugGamepads = !debugGamepads;
|
|
176
|
+
if (keyWasPressed(52)) // 4
|
|
172
177
|
godMode = !godMode;
|
|
173
178
|
if (keyWasPressed(53)) // 5
|
|
174
179
|
debugTakeScreenshot = 1;
|
|
175
180
|
//if (keyWasPressed(54)) // 6
|
|
176
|
-
//
|
|
177
|
-
if (keyWasPressed(55)) // 7
|
|
178
|
-
debugGamepads = !debugGamepads;
|
|
181
|
+
//if (keyWasPressed(55)) // 7
|
|
179
182
|
//if (keyWasPressed(56)) // 8
|
|
180
183
|
//if (keyWasPressed(57)) // 9
|
|
181
|
-
if (keyWasPressed(48)) // 0
|
|
182
|
-
showWatermark = !showWatermark;
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -200,21 +201,20 @@ const debugRender = ()=>
|
|
|
200
201
|
|
|
201
202
|
if (debugGamepads && gamepadsEnable && navigator.getGamepads)
|
|
202
203
|
{
|
|
203
|
-
//
|
|
204
|
+
// gamepad debug display
|
|
204
205
|
const gamepads = navigator.getGamepads();
|
|
205
206
|
for (let i = gamepads.length; i--;)
|
|
206
207
|
{
|
|
207
208
|
const gamepad = gamepads[i];
|
|
208
209
|
if (gamepad)
|
|
209
210
|
{
|
|
210
|
-
// gamepad debug display
|
|
211
211
|
const stickScale = 1;
|
|
212
212
|
const buttonScale = .2;
|
|
213
213
|
const centerPos = cameraPos;
|
|
214
214
|
const sticks = stickData[i];
|
|
215
215
|
for (let j = sticks.length; j--;)
|
|
216
216
|
{
|
|
217
|
-
const drawPos = centerPos.add(vec2(j*stickScale*2,i*stickScale*3));
|
|
217
|
+
const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
|
|
218
218
|
const stickPos = drawPos.add(sticks[j].scale(stickScale));
|
|
219
219
|
debugCircle(drawPos, stickScale, '#fff7',0,1);
|
|
220
220
|
debugLine(drawPos, stickPos, '#f00');
|
|
@@ -251,16 +251,14 @@ const debugRender = ()=>
|
|
|
251
251
|
bestObject = o
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
size.y = max(size.y, .2);
|
|
256
|
-
|
|
254
|
+
// show object info
|
|
257
255
|
const color = new Color(
|
|
258
256
|
o.collideTiles?1:0,
|
|
259
257
|
o.collideSolidObjects?1:0,
|
|
260
258
|
o.isSolid?1:0,
|
|
261
259
|
o.parent ? .2 : .5);
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
size.x = max(size.x, .2);
|
|
261
|
+
size.y = max(size.y, .2);
|
|
264
262
|
drawRect(o.pos, size, color);
|
|
265
263
|
drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
|
|
266
264
|
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
|
|
@@ -293,16 +291,15 @@ const debugRender = ()=>
|
|
|
293
291
|
}
|
|
294
292
|
|
|
295
293
|
{
|
|
296
|
-
//
|
|
297
|
-
overlayContext.
|
|
294
|
+
// draw debug primitives
|
|
295
|
+
overlayContext.save();
|
|
296
|
+
overlayContext.lineWidth = 2;
|
|
298
297
|
const pointSize = debugPointSize * cameraScale;
|
|
299
298
|
debugPrimitives.forEach(p=>
|
|
300
299
|
{
|
|
301
300
|
// create canvas transform from world space to screen space
|
|
302
301
|
const pos = worldToScreen(p.pos);
|
|
303
302
|
|
|
304
|
-
overlayContext.save();
|
|
305
|
-
overlayContext.lineWidth = 2;
|
|
306
303
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
307
304
|
overlayContext.rotate(p.angle);
|
|
308
305
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
@@ -335,15 +332,17 @@ const debugRender = ()=>
|
|
|
335
332
|
p.fill && overlayContext.fill();
|
|
336
333
|
overlayContext.stroke();
|
|
337
334
|
}
|
|
338
|
-
|
|
339
|
-
overlayContext.restore();
|
|
340
335
|
});
|
|
341
336
|
|
|
342
|
-
|
|
337
|
+
// remove expired pritives
|
|
338
|
+
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
339
|
+
|
|
340
|
+
overlayContext.restore();
|
|
343
341
|
}
|
|
344
342
|
|
|
345
343
|
{
|
|
346
|
-
|
|
344
|
+
// draw debug overlay
|
|
345
|
+
overlayContext.save();
|
|
347
346
|
overlayContext.fillStyle = '#fff';
|
|
348
347
|
overlayContext.textAlign = 'left';
|
|
349
348
|
overlayContext.textBaseline = 'top';
|
|
@@ -351,6 +350,7 @@ const debugRender = ()=>
|
|
|
351
350
|
overlayContext.shadowColor = '#000';
|
|
352
351
|
overlayContext.shadowBlur = 9;
|
|
353
352
|
|
|
353
|
+
let x = 9, y = -20, h = 30;
|
|
354
354
|
if (debugOverlay)
|
|
355
355
|
{
|
|
356
356
|
overlayContext.fillText(engineName, x, y += h);
|
|
@@ -363,14 +363,12 @@ const debugRender = ()=>
|
|
|
363
363
|
overlayContext.fillText('1: Debug Physics', x, y += h);
|
|
364
364
|
overlayContext.fillStyle = debugParticles ? '#f00' : '#fff';
|
|
365
365
|
overlayContext.fillText('2: Debug Particles', x, y += h);
|
|
366
|
+
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
367
|
+
overlayContext.fillText('3: Debug Gamepads', x, y += h);
|
|
366
368
|
overlayContext.fillStyle = godMode ? '#f00' : '#fff';
|
|
367
|
-
overlayContext.fillText('
|
|
369
|
+
overlayContext.fillText('4: God Mode', x, y += h);
|
|
368
370
|
overlayContext.fillStyle = '#fff';
|
|
369
371
|
overlayContext.fillText('5: Save Screenshot', x, y += h);
|
|
370
|
-
//overlayContext.fillStyle = debugParticleEditor ? '#f00' : '#fff';
|
|
371
|
-
//overlayContext.fillText('6: Particle Editor', x, y += h);
|
|
372
|
-
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
373
|
-
overlayContext.fillText('7: Debug Gamepads', x, y += h);
|
|
374
372
|
|
|
375
373
|
let keysPressed = '';
|
|
376
374
|
for(const i in inputData[0])
|
|
@@ -388,7 +386,6 @@ const debugRender = ()=>
|
|
|
388
386
|
buttonsPressed += i + ' ' ;
|
|
389
387
|
}
|
|
390
388
|
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
391
|
-
|
|
392
389
|
}
|
|
393
390
|
else
|
|
394
391
|
{
|
|
@@ -398,200 +395,9 @@ const debugRender = ()=>
|
|
|
398
395
|
overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
399
396
|
}
|
|
400
397
|
|
|
401
|
-
overlayContext.
|
|
398
|
+
overlayContext.restore();
|
|
402
399
|
}
|
|
403
|
-
|
|
404
|
-
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
405
400
|
}
|
|
406
|
-
|
|
407
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
408
|
-
// particle system editor (work in progress)
|
|
409
|
-
let debugParticleEditor = 0, debugParticleSystem, debugParticleSystemDiv, particleSystemCode;
|
|
410
|
-
|
|
411
|
-
const debugToggleParticleEditor = ()=>
|
|
412
|
-
{
|
|
413
|
-
debugParticleEditor = !debugParticleEditor;
|
|
414
|
-
|
|
415
|
-
if (debugParticleEditor)
|
|
416
|
-
{
|
|
417
|
-
if (!debugParticleSystem || debugParticleSystem.destroyed)
|
|
418
|
-
debugParticleSystem = new ParticleEmitter(cameraPos);
|
|
419
|
-
}
|
|
420
|
-
else if (debugParticleSystem && !debugParticleSystem.destroyed)
|
|
421
|
-
debugParticleSystem.destroy();
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const colorToHex = (color)=>
|
|
425
|
-
{
|
|
426
|
-
const componentToHex = (c)=>
|
|
427
|
-
{
|
|
428
|
-
const hex = (c*255|0).toString(16);
|
|
429
|
-
return hex.length == 1 ? '0' + hex : hex;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return '#' + componentToHex(color.r) + componentToHex(color.g) + componentToHex(color.b);
|
|
433
|
-
}
|
|
434
|
-
const hexToColor = (hex)=>
|
|
435
|
-
{
|
|
436
|
-
return new Color(
|
|
437
|
-
parseInt(hex.substr(1,2), 16)/255,
|
|
438
|
-
parseInt(hex.substr(3,2), 16)/255,
|
|
439
|
-
parseInt(hex.substr(5,2), 16)/255)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (!debugParticleSystemDiv)
|
|
443
|
-
{
|
|
444
|
-
const div = debugParticleSystemDiv = document.createElement('div');
|
|
445
|
-
div.innerHTML = '<big><b>Particle Editor';
|
|
446
|
-
div.style = 'position:absolute;top:10;left:10;color:#fff';
|
|
447
|
-
document.body.appendChild(div);
|
|
448
|
-
|
|
449
|
-
for ( const setting of debugParticleSettings)
|
|
450
|
-
{
|
|
451
|
-
const input = setting[2] = document.createElement('input');
|
|
452
|
-
const name = setting[0];
|
|
453
|
-
const type = setting[1];
|
|
454
|
-
if (type)
|
|
455
|
-
{
|
|
456
|
-
if (type == 'color')
|
|
457
|
-
{
|
|
458
|
-
input.type = type;
|
|
459
|
-
const color = debugParticleSystem[name];
|
|
460
|
-
input.value = colorToHex(color);
|
|
461
|
-
}
|
|
462
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
463
|
-
input.value = debugParticleSystem.colorStartA.a;
|
|
464
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
465
|
-
input.value = debugParticleSystem.colorEndA.a;
|
|
466
|
-
else if (name == 'tileSizeX')
|
|
467
|
-
input.value = debugParticleSystem.tileSize.x;
|
|
468
|
-
else if (name == 'tileSizeY')
|
|
469
|
-
input.value = debugParticleSystem.tileSize.y;
|
|
470
|
-
}
|
|
471
|
-
else
|
|
472
|
-
input.value = debugParticleSystem[name] || '0';
|
|
473
|
-
|
|
474
|
-
input.oninput = (e)=>
|
|
475
|
-
{
|
|
476
|
-
const inputFloat = parseFloat(input.value) || 0;
|
|
477
|
-
if (type)
|
|
478
|
-
{
|
|
479
|
-
if (type == 'color')
|
|
480
|
-
{
|
|
481
|
-
const color = hexToColor(input.value);
|
|
482
|
-
debugParticleSystem[name].r = color.r;
|
|
483
|
-
debugParticleSystem[name].g = color.g;
|
|
484
|
-
debugParticleSystem[name].b = color.b;
|
|
485
|
-
}
|
|
486
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
487
|
-
{
|
|
488
|
-
debugParticleSystem.colorStartA.a = clamp(inputFloat);
|
|
489
|
-
debugParticleSystem.colorStartB.a = clamp(inputFloat);
|
|
490
|
-
}
|
|
491
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
492
|
-
{
|
|
493
|
-
debugParticleSystem.colorEndA.a = clamp(inputFloat);
|
|
494
|
-
debugParticleSystem.colorEndB.a = clamp(inputFloat);
|
|
495
|
-
}
|
|
496
|
-
else if (name == 'tileSizeX')
|
|
497
|
-
{
|
|
498
|
-
debugParticleSystem.tileSize = vec2(parseInt(input.value), debugParticleSystem.tileSize.y);
|
|
499
|
-
}
|
|
500
|
-
else if (name == 'tileSizeY')
|
|
501
|
-
{
|
|
502
|
-
debugParticleSystem.tileSize.y = vec2(debugParticleSystem.tileSize.x, parseInt(input.value));
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
else
|
|
506
|
-
debugParticleSystem[name] = inputFloat;
|
|
507
|
-
|
|
508
|
-
updateCode();
|
|
509
|
-
}
|
|
510
|
-
div.appendChild(document.createElement('br'));
|
|
511
|
-
div.appendChild(input);
|
|
512
|
-
div.appendChild(document.createTextNode(' ' + name));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
div.appendChild(document.createElement('br'));
|
|
516
|
-
div.appendChild(document.createElement('br'));
|
|
517
|
-
div.appendChild(particleSystemCode = document.createElement('input'));
|
|
518
|
-
particleSystemCode.disabled = true;
|
|
519
|
-
div.appendChild(document.createTextNode(' code'));
|
|
520
|
-
|
|
521
|
-
div.appendChild(document.createElement('br'));
|
|
522
|
-
const button = document.createElement('button')
|
|
523
|
-
div.appendChild(button);
|
|
524
|
-
button.innerHTML = 'Copy To Clipboard';
|
|
525
|
-
|
|
526
|
-
button.onclick = (e)=> navigator.clipboard.writeText(particleSystemCode.value);
|
|
527
|
-
|
|
528
|
-
const updateCode = ()=>
|
|
529
|
-
{
|
|
530
|
-
let code = '';
|
|
531
|
-
let count = 0;
|
|
532
|
-
for ( const setting of debugParticleSettings)
|
|
533
|
-
{
|
|
534
|
-
const name = setting[0];
|
|
535
|
-
const type = setting[1];
|
|
536
|
-
let value;
|
|
537
|
-
if (name == 'tileSizeX' || type == 'alpha')
|
|
538
|
-
continue;
|
|
539
|
-
|
|
540
|
-
if (count++)
|
|
541
|
-
code += ', ';
|
|
542
|
-
|
|
543
|
-
if (name == 'tileSizeY')
|
|
544
|
-
{
|
|
545
|
-
value = `vec2(${debugParticleSystem.tileSize.x},${debugParticleSystem.tileSize.y})`;
|
|
546
|
-
}
|
|
547
|
-
else if (type == 'color')
|
|
548
|
-
{
|
|
549
|
-
const c = debugParticleSystem[name];
|
|
550
|
-
value = `new Color(${c.r},${c.g},${c.b},${c.a})`;
|
|
551
|
-
}
|
|
552
|
-
else
|
|
553
|
-
value = debugParticleSystem[name];
|
|
554
|
-
code += value;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
particleSystemCode.value = '...[' + code + ']';
|
|
558
|
-
}
|
|
559
|
-
updateCode();
|
|
560
|
-
}
|
|
561
|
-
debugParticleSystemDiv.style.display = debugParticleEditor ? '' : 'none'
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const debugParticleSettings =
|
|
565
|
-
[
|
|
566
|
-
['emitSize'],
|
|
567
|
-
['emitTime'],
|
|
568
|
-
['emitRate'],
|
|
569
|
-
['emitConeAngle'],
|
|
570
|
-
['tileIndex'],
|
|
571
|
-
['tileSizeX', 'tileSize'],
|
|
572
|
-
['tileSizeY', 'tileSize'],
|
|
573
|
-
['colorStartA', 'color'],
|
|
574
|
-
['colorStartB', 'color'],
|
|
575
|
-
['colorStartAlpha', 'alpha'],
|
|
576
|
-
['colorEndA', 'color'],
|
|
577
|
-
['colorEndB', 'color'],
|
|
578
|
-
['colorEndAlpha', 'alpha'],
|
|
579
|
-
['particleTime'],
|
|
580
|
-
['sizeStart'],
|
|
581
|
-
['sizeEnd'],
|
|
582
|
-
['speed'],
|
|
583
|
-
['angleSpeed'],
|
|
584
|
-
['damping'],
|
|
585
|
-
['angleDamping'],
|
|
586
|
-
['gravityScale'],
|
|
587
|
-
['particleConeAngle'],
|
|
588
|
-
['fadeRate'],
|
|
589
|
-
['randomness'],
|
|
590
|
-
['collideTiles'],
|
|
591
|
-
['additive'],
|
|
592
|
-
['randomColorComponents'],
|
|
593
|
-
['renderOrder'],
|
|
594
|
-
];
|
|
595
401
|
/**
|
|
596
402
|
* LittleJS Utility Classes and Functions
|
|
597
403
|
* <br> - General purpose math library
|
|
@@ -643,27 +449,27 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
643
449
|
|
|
644
450
|
/** Clamps the value beween max and min
|
|
645
451
|
* @param {Number} value
|
|
646
|
-
* @param {Number} [max=1]
|
|
647
452
|
* @param {Number} [min=0]
|
|
453
|
+
* @param {Number} [max=1]
|
|
648
454
|
* @return {Number}
|
|
649
455
|
* @memberof Utilities */
|
|
650
|
-
const clamp = (v,
|
|
456
|
+
const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
651
457
|
|
|
652
458
|
/** Returns what percentage the value is between max and min
|
|
653
459
|
* @param {Number} value
|
|
654
|
-
* @param {Number} [max=1]
|
|
655
460
|
* @param {Number} [min=0]
|
|
461
|
+
* @param {Number} [max=1]
|
|
656
462
|
* @return {Number}
|
|
657
463
|
* @memberof Utilities */
|
|
658
|
-
const percent = (v,
|
|
464
|
+
const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
659
465
|
|
|
660
466
|
/** Linearly interpolates the percent value between max and min
|
|
661
467
|
* @param {Number} percent
|
|
662
|
-
* @param {Number} [max=1]
|
|
663
468
|
* @param {Number} [min=0]
|
|
469
|
+
* @param {Number} [max=1]
|
|
664
470
|
* @return {Number}
|
|
665
471
|
* @memberof Utilities */
|
|
666
|
-
const lerp = (p,
|
|
472
|
+
const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
|
|
667
473
|
|
|
668
474
|
/** Applies smoothstep function to the percentage value
|
|
669
475
|
* @param {Number} value
|
|
@@ -678,11 +484,11 @@ const smoothStep = (p)=> p * p * (3 - 2 * p);
|
|
|
678
484
|
const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
679
485
|
|
|
680
486
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
681
|
-
* @param {Vector2} pointA
|
|
682
|
-
* @param {Vector2} sizeA
|
|
683
|
-
* @param {Vector2} pointB
|
|
684
|
-
* @param {Vector2} sizeB
|
|
685
|
-
* @return {Boolean}
|
|
487
|
+
* @param {Vector2} pointA - Center of box A
|
|
488
|
+
* @param {Vector2} sizeA - Size of box A
|
|
489
|
+
* @param {Vector2} pointB - Center of box B
|
|
490
|
+
* @param {Vector2} [sizeB] - Size of box B
|
|
491
|
+
* @return {Boolean} - True if overlapping
|
|
686
492
|
* @memberof Utilities */
|
|
687
493
|
const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x & abs(pA.y - pB.y)*2 < sA.y + sB.y;
|
|
688
494
|
|
|
@@ -1071,7 +877,7 @@ class Timer
|
|
|
1071
877
|
|
|
1072
878
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
1073
879
|
* @return {Number} */
|
|
1074
|
-
getPercent() { return this.isSet()? percent(this.time - time,
|
|
880
|
+
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
1075
881
|
}
|
|
1076
882
|
/**
|
|
1077
883
|
* LittleJS Engine Settings
|
|
@@ -1319,7 +1125,7 @@ let medalDisplayIconSize = 50;
|
|
|
1319
1125
|
class EngineObject
|
|
1320
1126
|
{
|
|
1321
1127
|
/** Create an engine object and adds it to the list of objects
|
|
1322
|
-
* @param {Vector2} [position=new Vector2(
|
|
1128
|
+
* @param {Vector2} [position=new Vector2()] - World space position of the object
|
|
1323
1129
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1324
1130
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1325
1131
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
@@ -1364,12 +1170,15 @@ class EngineObject
|
|
|
1364
1170
|
this.gravityScale = 1;
|
|
1365
1171
|
/** @property {Number} [renderOrder=0] - Objects are sorted by render order */
|
|
1366
1172
|
this.renderOrder = renderOrder;
|
|
1173
|
+
/** @property {Vector2} [velocity=new Vector2()] - Velocity of the object */
|
|
1174
|
+
this.velocity = new Vector2();
|
|
1175
|
+
/** @property {Number} [angleVelocity=0] - Angular velocity of the object */
|
|
1176
|
+
this.angleVelocity = 0;
|
|
1367
1177
|
|
|
1368
1178
|
// init other internal object stuff
|
|
1369
1179
|
this.spawnTime = time;
|
|
1370
|
-
this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
|
|
1371
|
-
this.collideTiles = 1;
|
|
1372
1180
|
this.children = [];
|
|
1181
|
+
this.collideTiles = 1;
|
|
1373
1182
|
|
|
1374
1183
|
// add to list of objects
|
|
1375
1184
|
engineObjects.push(this);
|
|
@@ -1388,8 +1197,8 @@ class EngineObject
|
|
|
1388
1197
|
}
|
|
1389
1198
|
|
|
1390
1199
|
// limit max speed to prevent missing collisions
|
|
1391
|
-
this.velocity.x = clamp(this.velocity.x, objectMaxSpeed,
|
|
1392
|
-
this.velocity.y = clamp(this.velocity.y, objectMaxSpeed,
|
|
1200
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
1201
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
1393
1202
|
|
|
1394
1203
|
// apply physics
|
|
1395
1204
|
const oldPos = this.pos.copy();
|
|
@@ -1479,8 +1288,8 @@ class EngineObject
|
|
|
1479
1288
|
|
|
1480
1289
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1481
1290
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1482
|
-
this.velocity.y = lerp(elasticity,
|
|
1483
|
-
o.velocity.y = lerp(elasticity,
|
|
1291
|
+
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
1292
|
+
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
1484
1293
|
}
|
|
1485
1294
|
}
|
|
1486
1295
|
if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
|
|
@@ -1500,8 +1309,8 @@ class EngineObject
|
|
|
1500
1309
|
|
|
1501
1310
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1502
1311
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1503
|
-
this.velocity.x = lerp(elasticity,
|
|
1504
|
-
o.velocity.x = lerp(elasticity,
|
|
1312
|
+
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
1313
|
+
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
1505
1314
|
}
|
|
1506
1315
|
else // bounce if other object is fixed
|
|
1507
1316
|
this.velocity.x *= -this.elasticity;
|
|
@@ -1531,7 +1340,7 @@ class EngineObject
|
|
|
1531
1340
|
|
|
1532
1341
|
// adjust next velocity to settle on ground
|
|
1533
1342
|
const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
|
|
1534
|
-
if (o < 0 && o >
|
|
1343
|
+
if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
|
|
1535
1344
|
this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
|
|
1536
1345
|
|
|
1537
1346
|
// move to previous position
|
|
@@ -1842,6 +1651,18 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1842
1651
|
context.restore();
|
|
1843
1652
|
}
|
|
1844
1653
|
|
|
1654
|
+
/** Enable normal or additive blend mode
|
|
1655
|
+
* @param {Boolean} [additive=0]
|
|
1656
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
1657
|
+
* @memberof Draw */
|
|
1658
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
1659
|
+
{
|
|
1660
|
+
if (glEnable && useWebGL)
|
|
1661
|
+
glSetBlendMode(additive);
|
|
1662
|
+
else
|
|
1663
|
+
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1845
1666
|
/** Draw text on overlay canvas in world space
|
|
1846
1667
|
* @param {String} text
|
|
1847
1668
|
* @param {Vector2} pos
|
|
@@ -1867,16 +1688,93 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
|
|
|
1867
1688
|
overlayContext.fillText(text, pos.x, pos.y);
|
|
1868
1689
|
}
|
|
1869
1690
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
*
|
|
1874
|
-
|
|
1691
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
1695
|
+
* <br> - 96 characters (from space to tilde) are stored in an image
|
|
1696
|
+
* <br> - Uses a default 8x8 font if none is supplied
|
|
1697
|
+
* <br> - You can also use fonts from the main tile sheet
|
|
1698
|
+
* @example
|
|
1699
|
+
* // use built in font
|
|
1700
|
+
* const font = new ImageFont;
|
|
1701
|
+
*
|
|
1702
|
+
* // draw text
|
|
1703
|
+
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1704
|
+
*/
|
|
1705
|
+
class FontImage
|
|
1875
1706
|
{
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1707
|
+
/** Create an image font
|
|
1708
|
+
* @param {Image} [image] - The image the font is stored in, if undefined the default font is used
|
|
1709
|
+
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1710
|
+
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1711
|
+
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
1712
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1713
|
+
*/
|
|
1714
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1715
|
+
{
|
|
1716
|
+
if (!image)
|
|
1717
|
+
{
|
|
1718
|
+
// use default font image
|
|
1719
|
+
image = new Image();
|
|
1720
|
+
image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6MCIBdFY+Q0PMNgf0yCgsSAaZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWw1hPWNRcEyWh2npdpBmSUBrRaN1WSlQAWXaWLF0kkwR7C/N7XUvIaQr4xpeqeQigjLwBRZ/6KkufAACcv5z7GYhvgWHII8xN/4C2hvLCkmZSVgIJMHNTxpYOsJOiitEjUQnIQAFFP+opoQfCbjWmfzWiNzCjEvxAPFARmw/Y5Te0sl2zEp4M+m7cToJjdJS7y1xcFlFT7EtWBZ8nl7uLSff7UgYewtlyY6yHM4RrjNrnsfn/W0DBK9xNZctZ7gsTeXTe5eqLqXFlKnAn+vkNXzH6TqPW10ksKoSlauKk1TcncZLSTxNnQ8Av/8RGPXOFIuMCZ/fpwIZx5vU4QIRQ44HwUyHx/QrZhm5VtXFex7ja2se0KBpnPT0DSwS4xd86uAy+SqnLBgkPY5JolhJ33ENyK6n0VyRzCnzebIkFdY/NUtM66lTdhix1KUss6q5snH5ftVJXG0QITb1gUDvzD3z/BbyOLMirwCtPzAAAAAElFTkSuQmCC';
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
this.image = image;
|
|
1724
|
+
this.tileSize = tileSize;
|
|
1725
|
+
this.paddingSize = paddingSize;
|
|
1726
|
+
this.startTileIndex = startTileIndex;
|
|
1727
|
+
this.context = context;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/** Draw text in screen space using the image font
|
|
1731
|
+
* @param {String} text
|
|
1732
|
+
* @param {Vector2} pos
|
|
1733
|
+
* @param {Number} [scale=4]
|
|
1734
|
+
* @param {Boolean} [center]
|
|
1735
|
+
*/
|
|
1736
|
+
drawTextScreen(text, pos, scale=4, center)
|
|
1737
|
+
{
|
|
1738
|
+
const context = this.context;
|
|
1739
|
+
context.save();
|
|
1740
|
+
context.imageSmoothingEnabled = !cavasPixelated;
|
|
1741
|
+
|
|
1742
|
+
const size = this.tileSize;
|
|
1743
|
+
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1744
|
+
const cols = this.image.width / this.tileSize.x |0;
|
|
1745
|
+
text.split('\n').forEach((line, i)=>
|
|
1746
|
+
{
|
|
1747
|
+
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1748
|
+
for(let j=line.length; j--;)
|
|
1749
|
+
{
|
|
1750
|
+
// draw each character
|
|
1751
|
+
let charCode = line[j].charCodeAt();
|
|
1752
|
+
if (charCode < 32 || charCode > 127)
|
|
1753
|
+
charCode = 127; // unknown character
|
|
1754
|
+
|
|
1755
|
+
// get the character source location and draw it
|
|
1756
|
+
const tile = this.startTileIndex + charCode - 32;
|
|
1757
|
+
const x = tile % cols;
|
|
1758
|
+
const y = tile / cols |0;
|
|
1759
|
+
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
1760
|
+
context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
|
|
1761
|
+
drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
|
|
1762
|
+
}
|
|
1763
|
+
});
|
|
1764
|
+
|
|
1765
|
+
context.restore();
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/** Draw text in world space using the image font
|
|
1769
|
+
* @param {String} text
|
|
1770
|
+
* @param {Vector2} pos
|
|
1771
|
+
* @param {Number} [scale=.25]
|
|
1772
|
+
* @param {Boolean} [center]
|
|
1773
|
+
*/
|
|
1774
|
+
drawText(text, pos, scale=1, center)
|
|
1775
|
+
{
|
|
1776
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1777
|
+
}
|
|
1880
1778
|
}
|
|
1881
1779
|
|
|
1882
1780
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2067,7 +1965,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
2067
1965
|
|
|
2068
1966
|
const rect = mainCanvas.getBoundingClientRect();
|
|
2069
1967
|
return mainCanvasSize.multiply(
|
|
2070
|
-
vec2(percent(mousePos.x, rect.
|
|
1968
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
2071
1969
|
}
|
|
2072
1970
|
|
|
2073
1971
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2108,8 +2006,8 @@ function gamepadsUpdate()
|
|
|
2108
2006
|
// read clamp dead zone of analog sticks
|
|
2109
2007
|
const deadZone = .3, deadZoneMax = .8;
|
|
2110
2008
|
const applyDeadZone = (v)=>
|
|
2111
|
-
v > deadZone ? percent( v,
|
|
2112
|
-
v < -deadZone ? -percent(-v,
|
|
2009
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2010
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
2113
2011
|
|
|
2114
2012
|
// read analog sticks
|
|
2115
2013
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -2189,7 +2087,7 @@ if (isTouchDevice)
|
|
|
2189
2087
|
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
2190
2088
|
|
|
2191
2089
|
// touch input internal variables
|
|
2192
|
-
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
|
|
2090
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2(), touchGamepadAnalog = 1;
|
|
2193
2091
|
|
|
2194
2092
|
// create the touch gamepad, called automatically by the engine
|
|
2195
2093
|
function touchGamepadCreate()
|
|
@@ -2235,7 +2133,14 @@ function touchGamepadCreate()
|
|
|
2235
2133
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2236
2134
|
{
|
|
2237
2135
|
// virtual analog stick
|
|
2238
|
-
|
|
2136
|
+
if (touchGamepadAnalog)
|
|
2137
|
+
touchGamepadStick = touchPos.subtract(stickCenter).clampLength(touchGamepadSize/2).scale(2/touchGamepadSize);
|
|
2138
|
+
else
|
|
2139
|
+
{
|
|
2140
|
+
// 8 way dpad
|
|
2141
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
2142
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2143
|
+
}
|
|
2239
2144
|
}
|
|
2240
2145
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2241
2146
|
{
|
|
@@ -2259,7 +2164,7 @@ function touchGamepadRender()
|
|
|
2259
2164
|
return;
|
|
2260
2165
|
|
|
2261
2166
|
// fade off when not touching or paused
|
|
2262
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
2167
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
2263
2168
|
if (!alpha || paused)
|
|
2264
2169
|
return;
|
|
2265
2170
|
|
|
@@ -2362,7 +2267,7 @@ class Sound
|
|
|
2362
2267
|
return; // out of range
|
|
2363
2268
|
|
|
2364
2269
|
// attenuate volume by distance
|
|
2365
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
2270
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
2366
2271
|
}
|
|
2367
2272
|
|
|
2368
2273
|
// get pan from screen space coords
|
|
@@ -2544,7 +2449,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2544
2449
|
// connect source to gain
|
|
2545
2450
|
(
|
|
2546
2451
|
window.StereoPannerNode ? // create pan node if possible
|
|
2547
|
-
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1,
|
|
2452
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
2548
2453
|
: source
|
|
2549
2454
|
)
|
|
2550
2455
|
.connect(gainNode);
|
|
@@ -2917,11 +2822,11 @@ class TileLayerData
|
|
|
2917
2822
|
class TileLayer extends EngineObject
|
|
2918
2823
|
{
|
|
2919
2824
|
/** Create a tile layer object
|
|
2920
|
-
* @param {Vector2} [position=new Vector2(
|
|
2921
|
-
* @param {Vector2} [size=objectDefaultSize]
|
|
2922
|
-
* @param {Vector2} [tileSize=tileSizeDefault]
|
|
2923
|
-
* @param {Vector2} [scale=new Vector2(1,1)]
|
|
2924
|
-
* @param {Number} [renderOrder=0]
|
|
2825
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2826
|
+
* @param {Vector2} [size=objectDefaultSize] - World space size
|
|
2827
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2828
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2829
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2925
2830
|
*/
|
|
2926
2831
|
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2927
2832
|
{
|
|
@@ -2981,17 +2886,19 @@ class TileLayer extends EngineObject
|
|
|
2981
2886
|
);
|
|
2982
2887
|
}
|
|
2983
2888
|
|
|
2984
|
-
/** Draw all the tile data to an offscreen canvas
|
|
2889
|
+
/** Draw all the tile data to an offscreen canvas
|
|
2890
|
+
* - This may be slow in some browsers
|
|
2891
|
+
*/
|
|
2985
2892
|
redraw()
|
|
2986
2893
|
{
|
|
2987
|
-
this.redrawStart();
|
|
2894
|
+
this.redrawStart(1);
|
|
2988
2895
|
this.drawAllTileData();
|
|
2989
2896
|
this.redrawEnd();
|
|
2990
2897
|
}
|
|
2991
2898
|
|
|
2992
2899
|
/** Call to start the redraw process
|
|
2993
|
-
* @param {Boolean} [clear=
|
|
2994
|
-
redrawStart(clear =
|
|
2900
|
+
* @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
|
|
2901
|
+
redrawStart(clear = 0)
|
|
2995
2902
|
{
|
|
2996
2903
|
if (clear)
|
|
2997
2904
|
{
|
|
@@ -3722,8 +3629,8 @@ function glInit()
|
|
|
3722
3629
|
|
|
3723
3630
|
// create the canvas and tile texture
|
|
3724
3631
|
glCanvas = document.createElement('canvas');
|
|
3725
|
-
glContext = glCanvas.getContext('webgl');
|
|
3726
|
-
glCanvas.style =
|
|
3632
|
+
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3633
|
+
glCanvas.style = styleCanvas;
|
|
3727
3634
|
glTileTexture = glCreateTexture(tileImage);
|
|
3728
3635
|
|
|
3729
3636
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -3733,14 +3640,13 @@ function glInit()
|
|
|
3733
3640
|
glShader = glCreateProgram(
|
|
3734
3641
|
'precision highp float;'+ // use highp for better accuracy, lowp for better perf
|
|
3735
3642
|
'uniform mat4 m;'+ // transform matrix
|
|
3736
|
-
'attribute
|
|
3737
|
-
'attribute
|
|
3738
|
-
'attribute vec4 c,b;'+ // color, additiveColor
|
|
3643
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3644
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3739
3645
|
'varying vec2 v;'+ // return uv
|
|
3740
3646
|
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3741
3647
|
'void main(){'+ // shader entry point
|
|
3742
|
-
'gl_Position=m*vec4(
|
|
3743
|
-
'v=t;d=c;e=
|
|
3648
|
+
'gl_Position=m*vec4(p,1,1);'+// transform position
|
|
3649
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3744
3650
|
'}' // end of shader
|
|
3745
3651
|
,
|
|
3746
3652
|
'precision highp float;'+ // use highp for better accuracy, lowp for better perf
|
|
@@ -3767,12 +3673,10 @@ function glInit()
|
|
|
3767
3673
|
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3768
3674
|
offset += size*typeSize;
|
|
3769
3675
|
}
|
|
3770
|
-
initVertexAttribArray('a', gl_FLOAT, 4, 1); // angle
|
|
3771
3676
|
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3772
|
-
initVertexAttribArray('s', gl_FLOAT, 4, 2); // size
|
|
3773
3677
|
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3774
3678
|
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3775
|
-
initVertexAttribArray('
|
|
3679
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3776
3680
|
}
|
|
3777
3681
|
|
|
3778
3682
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3887,6 +3791,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3887
3791
|
|
|
3888
3792
|
// clear and set to same size as main canvas
|
|
3889
3793
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3794
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3890
3795
|
|
|
3891
3796
|
// set up the shader
|
|
3892
3797
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
@@ -3933,12 +3838,10 @@ function glCopyToContext(context, forceDraw)
|
|
|
3933
3838
|
if (!glEnable || !glBatchCount) return;
|
|
3934
3839
|
|
|
3935
3840
|
glFlush();
|
|
3841
|
+
|
|
3842
|
+
// do not draw in overlay mode because the canvas is visible
|
|
3936
3843
|
if (!glOverlay || forceDraw)
|
|
3937
|
-
{
|
|
3938
|
-
// do not draw/clear in overlay mode because the canvas is visible
|
|
3939
3844
|
context.drawImage(glCanvas, 0, 0);
|
|
3940
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3941
|
-
}
|
|
3942
3845
|
}
|
|
3943
3846
|
|
|
3944
3847
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
@@ -3961,51 +3864,49 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3961
3864
|
// flush if there is no room for more verts or if different blend mode
|
|
3962
3865
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
3963
3866
|
glFlush();
|
|
3867
|
+
|
|
3868
|
+
// prepare to create the verts from size and angle
|
|
3869
|
+
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3870
|
+
const cX = c*sizeX, cY = c*sizeY, sX = s*sizeX, sY = s*sizeY;
|
|
3964
3871
|
|
|
3965
3872
|
// setup 2 triangles to form a quad
|
|
3966
3873
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3967
3874
|
|
|
3968
3875
|
// vertex 0
|
|
3969
|
-
glPositionData[offset++] =
|
|
3970
|
-
glPositionData[offset++] =
|
|
3971
|
-
glPositionData[offset++] =
|
|
3972
|
-
|
|
3973
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3876
|
+
glPositionData[offset++] = x - cX - sY;
|
|
3877
|
+
glPositionData[offset++] = y - cY + sX;
|
|
3878
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3879
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3974
3880
|
|
|
3975
3881
|
// vertex 1
|
|
3976
|
-
glPositionData[offset++] =
|
|
3977
|
-
glPositionData[offset++] =
|
|
3978
|
-
glPositionData[offset++] =
|
|
3979
|
-
|
|
3980
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3882
|
+
glPositionData[offset++] = x + cX + sY;
|
|
3883
|
+
glPositionData[offset++] = y + cY - sX;
|
|
3884
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3885
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3981
3886
|
|
|
3982
3887
|
// vertex 2
|
|
3983
|
-
glPositionData[offset++] =
|
|
3984
|
-
glPositionData[offset++] =
|
|
3985
|
-
glPositionData[offset++] =
|
|
3986
|
-
|
|
3987
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3888
|
+
glPositionData[offset++] = x - cX + sY;
|
|
3889
|
+
glPositionData[offset++] = y + cY + sX;
|
|
3890
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3891
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3988
3892
|
|
|
3989
3893
|
// vertex 0
|
|
3990
|
-
glPositionData[offset++] =
|
|
3991
|
-
glPositionData[offset++] =
|
|
3992
|
-
glPositionData[offset++] =
|
|
3993
|
-
|
|
3994
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3894
|
+
glPositionData[offset++] = x - cX - sY;
|
|
3895
|
+
glPositionData[offset++] = y - cY + sX;
|
|
3896
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3897
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3995
3898
|
|
|
3996
3899
|
// vertex 3
|
|
3997
|
-
glPositionData[offset++] =
|
|
3998
|
-
glPositionData[offset++] =
|
|
3999
|
-
glPositionData[offset++] =
|
|
4000
|
-
|
|
4001
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3900
|
+
glPositionData[offset++] = x + cX - sY;
|
|
3901
|
+
glPositionData[offset++] = y - cY - sX;
|
|
3902
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3903
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
4002
3904
|
|
|
4003
3905
|
// vertex 1
|
|
4004
|
-
glPositionData[offset++] =
|
|
4005
|
-
glPositionData[offset++] =
|
|
4006
|
-
glPositionData[offset++] =
|
|
4007
|
-
|
|
4008
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3906
|
+
glPositionData[offset++] = x + cX + sY;
|
|
3907
|
+
glPositionData[offset++] = y + cY - sX;
|
|
3908
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3909
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
4009
3910
|
}
|
|
4010
3911
|
|
|
4011
3912
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4037,9 +3938,9 @@ gl_LINK_STATUS = 35714,
|
|
|
4037
3938
|
|
|
4038
3939
|
// constants for batch rendering
|
|
4039
3940
|
gl_VERTICES_PER_QUAD = 6,
|
|
4040
|
-
gl_INDICIES_PER_VERT =
|
|
3941
|
+
gl_INDICIES_PER_VERT = 6,
|
|
4041
3942
|
gl_MAX_BATCH = 1<<16,
|
|
4042
|
-
gl_VERTEX_BYTE_STRIDE =
|
|
3943
|
+
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
4043
3944
|
/*
|
|
4044
3945
|
LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4045
3946
|
MIT License - Copyright 2021 Frank Force
|
|
@@ -4064,7 +3965,7 @@ gl_VERTEX_BYTE_STRIDE = 4 + (4 * 2) * 3 + (4) * 2; // float + vec2 * 3 + (char *
|
|
|
4064
3965
|
const engineName = 'LittleJS';
|
|
4065
3966
|
|
|
4066
3967
|
/** Version of engine */
|
|
4067
|
-
const engineVersion = '1.
|
|
3968
|
+
const engineVersion = '1.2.0';
|
|
4068
3969
|
|
|
4069
3970
|
/** Frames per second to update objects
|
|
4070
3971
|
* @default */
|
|
@@ -4101,7 +4002,7 @@ let averageFPS, drawCount;
|
|
|
4101
4002
|
// css text used for elements created by engine
|
|
4102
4003
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
4103
4004
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
4104
|
-
const
|
|
4005
|
+
const styleCanvas = 'position:absolute';
|
|
4105
4006
|
|
|
4106
4007
|
///////////////////////////////////////////////////////////////////////////////
|
|
4107
4008
|
|
|
@@ -4126,7 +4027,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4126
4027
|
document.body.style = styleBody;
|
|
4127
4028
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4128
4029
|
mainContext = mainCanvas.getContext('2d');
|
|
4129
|
-
mainCanvas.style =
|
|
4030
|
+
mainCanvas.style = styleCanvas;
|
|
4130
4031
|
|
|
4131
4032
|
// init stuff and start engine
|
|
4132
4033
|
debugInit();
|
|
@@ -4135,7 +4036,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4135
4036
|
// create overlay canvas for hud to appear above gl canvas
|
|
4136
4037
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4137
4038
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4138
|
-
overlayCanvas.style =
|
|
4039
|
+
overlayCanvas.style = styleCanvas;
|
|
4139
4040
|
|
|
4140
4041
|
gameInit();
|
|
4141
4042
|
touchGamepadCreate();
|
|
@@ -4151,7 +4052,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4151
4052
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
4152
4053
|
frameTimeLastMS = frameTimeMS;
|
|
4153
4054
|
if (debug || showWatermark)
|
|
4154
|
-
averageFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1)
|
|
4055
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
4155
4056
|
if (debug)
|
|
4156
4057
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
4157
4058
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -4202,13 +4103,24 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4202
4103
|
|
|
4203
4104
|
// fit to window by adding space on top or bottom if necessary
|
|
4204
4105
|
const aspect = innerWidth / innerHeight;
|
|
4205
|
-
const fixedAspect =
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4106
|
+
const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
|
|
4107
|
+
|
|
4108
|
+
// clear css
|
|
4109
|
+
mainCanvas.style.width = overlayCanvas.style.width =
|
|
4110
|
+
mainCanvas.style.height = overlayCanvas.style.height =
|
|
4111
|
+
mainCanvas.style.top = overlayCanvas.style.top =
|
|
4112
|
+
mainCanvas.style.left = overlayCanvas.style.left = '';
|
|
4113
|
+
|
|
4114
|
+
// letterbox center canvas
|
|
4115
|
+
if (aspect < fixedAspect)
|
|
4116
|
+
{
|
|
4117
|
+
mainCanvas.style.width = overlayCanvas.style.width = innerWidth;
|
|
4118
|
+
mainCanvas.style.top = overlayCanvas.style.top = (innerHeight - innerWidth/fixedAspect)/2;
|
|
4119
|
+
}
|
|
4120
|
+
else
|
|
4209
4121
|
{
|
|
4210
|
-
|
|
4211
|
-
|
|
4122
|
+
mainCanvas.style.height = overlayCanvas.style.height = innerHeight;
|
|
4123
|
+
mainCanvas.style.left = overlayCanvas.style.left = (innerWidth - innerHeight*fixedAspect)/2;
|
|
4212
4124
|
}
|
|
4213
4125
|
}
|
|
4214
4126
|
else
|
|
@@ -4216,6 +4128,18 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4216
4128
|
// clear and set size to same as window
|
|
4217
4129
|
mainCanvas.width = min(innerWidth, canvasMaxSize.x);
|
|
4218
4130
|
mainCanvas.height = min(innerHeight, canvasMaxSize.y);
|
|
4131
|
+
|
|
4132
|
+
// center canvas
|
|
4133
|
+
overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
|
|
4134
|
+
overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
if (glEnable)
|
|
4138
|
+
{
|
|
4139
|
+
glCanvas.style.width = mainCanvas.style.width;
|
|
4140
|
+
glCanvas.style.height = mainCanvas.style.height;
|
|
4141
|
+
glCanvas.style.top = mainCanvas.style.top;
|
|
4142
|
+
glCanvas.style.left = mainCanvas.style.left;
|
|
4219
4143
|
}
|
|
4220
4144
|
|
|
4221
4145
|
// render sort then render while removing destroyed objects
|