littlejsengine 1.16.2 → 1.17.5
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 +291 -246
- package/dist/littlejs.esm.js +1520 -1271
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +1178 -920
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +1170 -912
- package/examples/box2d/gameObjects.js +4 -4
- package/examples/breakout/game.js +5 -6
- package/examples/electron/index.html +2 -2
- package/examples/electron/tiles.png +0 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +9 -8
- package/examples/logo.png +0 -0
- package/examples/module/build.bat +7 -0
- package/examples/module/build.js +121 -0
- package/examples/module/tiles.png +0 -0
- package/examples/platformer/game.js +1 -1
- package/examples/platformer/gameEffects.js +25 -29
- package/examples/platformer/gameLevel.js +27 -28
- package/examples/puzzle/game.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2dPool.js +2 -2
- package/examples/shorts/box2dTileLayer.js +48 -0
- package/examples/shorts/clock.js +3 -3
- package/examples/shorts/fontImage.js +4 -3
- package/examples/shorts/music.js +2 -2
- package/examples/shorts/musicPlayer.js +2 -2
- package/examples/shorts/parallax.js +1 -1
- package/examples/shorts/sequencer.js +1 -1
- package/examples/shorts/shapes.js +1 -1
- package/examples/shorts/texture.js +10 -6
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +2 -0
- package/examples/starter/index.html +2 -2
- package/examples/starter/tiles.png +0 -0
- package/examples/typescript/tiles.png +0 -0
- package/examples/uiSystem/game.js +1 -1
- package/examples/uiSystem/tiles.png +0 -0
- package/package.json +5 -5
- package/plugins/box2d.js +167 -63
- package/plugins/postProcess.js +47 -28
- package/plugins/uiSystem.js +18 -18
- package/plugins/zzfxm.js +2 -2
- package/reference.md +4 -3
- package/src/engine.js +182 -181
- package/src/engineAudio.js +47 -63
- package/src/engineDebug.js +8 -8
- package/src/engineDraw.js +184 -124
- package/src/engineExport.js +325 -334
- package/src/engineFont.png +0 -0
- package/src/engineInput.js +18 -24
- package/src/engineMath.js +24 -113
- package/src/engineObject.js +27 -26
- package/src/engineParticles.js +2 -2
- package/src/engineTileLayer.js +229 -192
- package/src/engineUtilities.js +100 -4
- package/src/engineWebGL.js +124 -73
package/plugins/uiSystem.js
CHANGED
|
@@ -30,7 +30,7 @@ let uiDebug = 0;
|
|
|
30
30
|
|
|
31
31
|
/** Enable UI system debug drawing
|
|
32
32
|
* 0=off, 1=normal, 2=show invisible
|
|
33
|
-
* @param {number|boolean}
|
|
33
|
+
* @param {number|boolean} debugMode
|
|
34
34
|
* @memberof UISystem */
|
|
35
35
|
function uiSetDebug(debugMode)
|
|
36
36
|
{ uiDebug = typeof debugMode === 'boolean' ? (debugMode ? 1 : 0) : debugMode; }
|
|
@@ -512,7 +512,7 @@ class UISystemPlugin
|
|
|
512
512
|
const up = 'ArrowUp', down = 'ArrowDown', left = 'ArrowLeft', right = 'ArrowRight';
|
|
513
513
|
if (both)
|
|
514
514
|
{
|
|
515
|
-
return keyIsDown(up) || keyIsDown(left) ? -1 :
|
|
515
|
+
return keyIsDown(up) || keyIsDown(left) ? -1 :
|
|
516
516
|
keyIsDown(down) || keyIsDown(right) ? 1 : 0;
|
|
517
517
|
}
|
|
518
518
|
const back = vertical ? up : left;
|
|
@@ -543,7 +543,7 @@ class UISystemPlugin
|
|
|
543
543
|
* @return {boolean} */
|
|
544
544
|
getNavigationWasPressed()
|
|
545
545
|
{
|
|
546
|
-
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
546
|
+
return isUsingGamepad ? gamepadWasPressed(0, gamepadPrimary) :
|
|
547
547
|
keyWasPressed('Space') || keyWasPressed('Enter');
|
|
548
548
|
}
|
|
549
549
|
|
|
@@ -591,7 +591,7 @@ class UISystemPlugin
|
|
|
591
591
|
buttonYes.textHeight = 40;
|
|
592
592
|
buttonYes.navigationIndex = 1;
|
|
593
593
|
buttonYes.hoverColor = hsl(0,1,.5);
|
|
594
|
-
buttonYes.onClick = ()=> { closeMenu(); yesCallback && yesCallback(); };
|
|
594
|
+
buttonYes.onClick = ()=> { closeMenu(); yesCallback && yesCallback(); };
|
|
595
595
|
confirmMenu.addChild(buttonYes);
|
|
596
596
|
|
|
597
597
|
// no button
|
|
@@ -621,8 +621,8 @@ class UISystemPlugin
|
|
|
621
621
|
class UIObject
|
|
622
622
|
{
|
|
623
623
|
/** Create a UIObject
|
|
624
|
-
* @param {Vector2} [pos=(
|
|
625
|
-
* @param {Vector2} [size=(1
|
|
624
|
+
* @param {Vector2} [pos=vec2()]
|
|
625
|
+
* @param {Vector2} [size=vec2(1)]
|
|
626
626
|
*/
|
|
627
627
|
constructor(pos=vec2(), size=vec2())
|
|
628
628
|
{
|
|
@@ -637,7 +637,7 @@ class UIObject
|
|
|
637
637
|
this.size = size.copy();
|
|
638
638
|
/** @property {Color} - Color of the object */
|
|
639
639
|
this.color = uiSystem.defaultColor.copy();
|
|
640
|
-
/** @property {Color} - Color of the object when active, uses
|
|
640
|
+
/** @property {Color} - Color of the object when active, uses hoverColor if undefined */
|
|
641
641
|
this.activeColor = undefined;
|
|
642
642
|
/** @property {string} - Text for this ui object */
|
|
643
643
|
this.text = undefined;
|
|
@@ -735,10 +735,10 @@ class UIObject
|
|
|
735
735
|
|
|
736
736
|
// disconnect from parent and destroy children
|
|
737
737
|
this.destroyed = 1;
|
|
738
|
-
this.parent
|
|
738
|
+
this.parent?.removeChild(this);
|
|
739
739
|
for (const child of this.children)
|
|
740
740
|
{
|
|
741
|
-
child.parent =
|
|
741
|
+
child.parent = undefined;
|
|
742
742
|
child.destroy();
|
|
743
743
|
}
|
|
744
744
|
}
|
|
@@ -834,10 +834,10 @@ class UIObject
|
|
|
834
834
|
this.interactive && this.isActiveObject() && !this.disabled ?
|
|
835
835
|
this.color : this.lineColor;
|
|
836
836
|
const color = isNavigationObject ? this.hoverColor :
|
|
837
|
-
this.disabled ? this.disabledColor :
|
|
838
|
-
this.interactive ?
|
|
839
|
-
this.
|
|
840
|
-
this.
|
|
837
|
+
this.disabled ? this.disabledColor :
|
|
838
|
+
this.interactive ?
|
|
839
|
+
this.isActiveObject() ? this.activeColor || this.hoverColor :
|
|
840
|
+
this.isHoverObject() ? this.hoverColor :
|
|
841
841
|
this.color : this.color;
|
|
842
842
|
const lineWidth = this.lineWidth * (isNavigationObject ? 1.5 : 1);
|
|
843
843
|
|
|
@@ -849,7 +849,7 @@ class UIObject
|
|
|
849
849
|
getTextSize()
|
|
850
850
|
{
|
|
851
851
|
return vec2(
|
|
852
|
-
this.textWidth || this.textFitScale * this.size.x,
|
|
852
|
+
this.textWidth || this.textFitScale * this.size.x,
|
|
853
853
|
this.textHeight || this.textFitScale * this.size.y);
|
|
854
854
|
}
|
|
855
855
|
|
|
@@ -897,9 +897,9 @@ class UIObject
|
|
|
897
897
|
renderDebug(visible=true)
|
|
898
898
|
{
|
|
899
899
|
// apply color based on state
|
|
900
|
-
const color =
|
|
900
|
+
const color =
|
|
901
901
|
!visible ? GREEN :
|
|
902
|
-
this.isHoverObject() ? YELLOW :
|
|
902
|
+
this.isHoverObject() ? YELLOW :
|
|
903
903
|
this.disabled ? PURPLE :
|
|
904
904
|
this.interactive ? RED : BLUE;
|
|
905
905
|
uiSystem.drawRect(this.pos, this.size, CLEAR_BLACK, 4, color);
|
|
@@ -1231,8 +1231,8 @@ class UIScrollbar extends UIObject
|
|
|
1231
1231
|
class UIVideo extends UIObject
|
|
1232
1232
|
{
|
|
1233
1233
|
/** Create a video player UI object
|
|
1234
|
-
* @param {Vector2}
|
|
1235
|
-
* @param {Vector2}
|
|
1234
|
+
* @param {Vector2} pos
|
|
1235
|
+
* @param {Vector2} size
|
|
1236
1236
|
* @param {string} src - Video file path or URL
|
|
1237
1237
|
* @param {boolean} [autoplay=false] - Start playing immediately?
|
|
1238
1238
|
* @param {boolean} [loop=false] - Loop the video?
|
package/plugins/zzfxm.js
CHANGED
|
@@ -55,7 +55,7 @@ class ZzFXMusic extends Sound
|
|
|
55
55
|
/** Play the music that loops by default
|
|
56
56
|
* @param {number} [volume] - Volume to play the music at
|
|
57
57
|
* @param {boolean} [loop] - Should the music loop?
|
|
58
|
-
* @return {
|
|
58
|
+
* @return {SoundInstance} - The sound instance
|
|
59
59
|
*/
|
|
60
60
|
playMusic(volume=1, loop=true)
|
|
61
61
|
{ return super.play(undefined, volume, 1, 0, loop); }
|
|
@@ -102,7 +102,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
102
102
|
sampleBuffer = [hasMore = notFirstBeat = outSampleOffset = 0];
|
|
103
103
|
|
|
104
104
|
// for each pattern in sequence
|
|
105
|
-
sequence.forEach((patternIndex, sequenceIndex)
|
|
105
|
+
sequence.forEach((patternIndex, sequenceIndex)=> {
|
|
106
106
|
// get pattern for current channel, use empty 1 note pattern if none found
|
|
107
107
|
patternChannel = patterns[patternIndex][channelIndex] || [0, 0, 0];
|
|
108
108
|
|
package/reference.md
CHANGED
|
@@ -205,8 +205,8 @@ tileDefaultBleed = .3 // How much smaller to draw tiles to prevent bleeding
|
|
|
205
205
|
|
|
206
206
|
```javascript
|
|
207
207
|
// Sound Object
|
|
208
|
-
Sound(zzfxSound, range, taper)
|
|
209
|
-
|
|
208
|
+
Sound(zzfxSound, randomness, range, taper) // Create a zzfx sound
|
|
209
|
+
Sound(filename, randomness, range, taper) // Load a wave, mp3, or ogg
|
|
210
210
|
Sound.play(pos, volume=1, pitch=1, randomness=1, loop) // Play a sound, returns SoundInstance
|
|
211
211
|
Sound.playMusic(volume=1, loop=true) // Play as music with looping
|
|
212
212
|
Sound.playNote(semitoneOffset, pos, volume=1) // Play as note with a semitone offset
|
|
@@ -375,11 +375,12 @@ CanvasLayer(position, size) // Create a canvas layer object
|
|
|
375
375
|
CanvasLayer.canvas // The canvas used by this layer
|
|
376
376
|
CanvasLayer.context // The 2D context of the canvas
|
|
377
377
|
CanvasLayer.getImageData() // Get image data from canvas
|
|
378
|
-
CanvasLayer.
|
|
378
|
+
CanvasLayer.updateWebGL() // Creates or updates WebGL texture
|
|
379
379
|
|
|
380
380
|
// LittleJS Layer System
|
|
381
381
|
TileLayer(position, size, tileInfo, scale) // Create a tile layer object
|
|
382
382
|
TileLayer.setData(layerPos, data, redraw) // Set data at position
|
|
383
|
+
TileLayer.clearData(layerPos, redraw) // Clear data at position
|
|
383
384
|
TileLayer.getData(layerPos) // Get data at position
|
|
384
385
|
TileLayer.redraw() // Draw to an offscreen canvas
|
|
385
386
|
TileLayer.drawTileData(layerPos, clear=true) // Draw the tile
|
package/src/engine.js
CHANGED
|
@@ -30,7 +30,7 @@ const engineName = 'LittleJS';
|
|
|
30
30
|
* @type {string}
|
|
31
31
|
* @default
|
|
32
32
|
* @memberof Engine */
|
|
33
|
-
const engineVersion = '1.
|
|
33
|
+
const engineVersion = '1.17.5';
|
|
34
34
|
|
|
35
35
|
/** Frames per second to update
|
|
36
36
|
* @type {number}
|
|
@@ -85,8 +85,9 @@ function getPaused() { return paused; }
|
|
|
85
85
|
* @memberof Engine */
|
|
86
86
|
function setPaused(isPaused=true) { paused = isPaused; }
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// Engine internal variables
|
|
89
89
|
let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
90
|
+
let showEngineVersion = true;
|
|
90
91
|
|
|
91
92
|
///////////////////////////////////////////////////////////////////////////////
|
|
92
93
|
// plugin hooks
|
|
@@ -150,16 +151,17 @@ function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
|
150
151
|
* @example
|
|
151
152
|
* // Basic engine startup
|
|
152
153
|
* engineInit(
|
|
153
|
-
* ()
|
|
154
|
-
* ()
|
|
155
|
-
* ()
|
|
156
|
-
* ()
|
|
157
|
-
* ()
|
|
154
|
+
* ()=> { LOG('Game initialized!'); }, // gameInit
|
|
155
|
+
* ()=> { updateGameLogic(); }, // gameUpdate
|
|
156
|
+
* ()=> { updateUI(); }, // gameUpdatePost
|
|
157
|
+
* ()=> { drawBackground(); }, // gameRender
|
|
158
|
+
* ()=> { drawHUD(); }, // gameRenderPost
|
|
158
159
|
* ['tiles.png', 'tilesLevel.png'] // images to load
|
|
159
160
|
* );
|
|
160
161
|
* @memberof Engine */
|
|
161
162
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
162
163
|
{
|
|
164
|
+
showEngineVersion && console.log(`${engineName} Engine v${engineVersion}`);
|
|
163
165
|
ASSERT(!mainContext, 'engine already initialized');
|
|
164
166
|
ASSERT(isArray(imageSources), 'pass in images as array');
|
|
165
167
|
|
|
@@ -317,7 +319,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
317
319
|
|
|
318
320
|
// responsive aspect ratio with native resolution
|
|
319
321
|
const innerAspect = innerWidth / innerHeight;
|
|
320
|
-
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
322
|
+
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
321
323
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
322
324
|
{
|
|
323
325
|
// full height
|
|
@@ -374,7 +376,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
374
376
|
debugInit();
|
|
375
377
|
|
|
376
378
|
// setup canvases
|
|
377
|
-
// transform way is still more reliable
|
|
379
|
+
// transform way is still more reliable than flexbox or grid
|
|
378
380
|
const styleCanvas = 'position:absolute;'+ // allow canvases to overlap
|
|
379
381
|
'top:50%;left:50%;transform:translate(-50%,-50%)'; // center on screen
|
|
380
382
|
mainCanvas.style.cssText = styleCanvas;
|
|
@@ -419,18 +421,20 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
419
421
|
}));
|
|
420
422
|
}
|
|
421
423
|
|
|
424
|
+
// load engine font image
|
|
425
|
+
promises.push(fontImageInit());
|
|
426
|
+
|
|
422
427
|
if (showSplashScreen)
|
|
423
428
|
{
|
|
424
429
|
// draw splash screen
|
|
425
430
|
promises.push(new Promise(resolve =>
|
|
426
431
|
{
|
|
427
432
|
let t = 0;
|
|
428
|
-
console.log(`${engineName} Engine v${engineVersion}`);
|
|
429
433
|
updateSplash();
|
|
430
434
|
function updateSplash()
|
|
431
435
|
{
|
|
432
436
|
inputClear();
|
|
433
|
-
|
|
437
|
+
drawEngineLogo(t+=.01);
|
|
434
438
|
t>1 ? resolve() : setTimeout(updateSplash, 16);
|
|
435
439
|
}
|
|
436
440
|
}));
|
|
@@ -446,172 +450,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
446
450
|
await gameInit();
|
|
447
451
|
engineUpdate();
|
|
448
452
|
}
|
|
449
|
-
|
|
450
|
-
///////////////////////////////////////////////////////////////////////////
|
|
451
|
-
// LittleJS Splash Screen
|
|
452
|
-
function drawEngineSplashScreen(t)
|
|
453
|
-
{
|
|
454
|
-
const x = mainContext;
|
|
455
|
-
const w = mainCanvas.width = innerWidth;
|
|
456
|
-
const h = mainCanvas.height = innerHeight;
|
|
457
|
-
|
|
458
|
-
{
|
|
459
|
-
// background
|
|
460
|
-
const p3 = percent(t, 1, .8);
|
|
461
|
-
const p4 = percent(t, 0, .5);
|
|
462
|
-
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
463
|
-
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
464
|
-
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
465
|
-
x.save();
|
|
466
|
-
x.fillStyle = g;
|
|
467
|
-
x.fillRect(0,0,w,h);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// draw LittleJS logo...
|
|
471
|
-
const rect = (X, Y, W, H, C)=>
|
|
472
|
-
{
|
|
473
|
-
x.beginPath();
|
|
474
|
-
x.rect(X,Y,W,C?H*p:H);
|
|
475
|
-
x.fillStyle = C;
|
|
476
|
-
C ? x.fill() : x.stroke();
|
|
477
|
-
};
|
|
478
|
-
const line = (X, Y, Z, W)=>
|
|
479
|
-
{
|
|
480
|
-
x.beginPath();
|
|
481
|
-
x.lineTo(X,Y);
|
|
482
|
-
x.lineTo(Z,W);
|
|
483
|
-
x.stroke();
|
|
484
|
-
};
|
|
485
|
-
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
486
|
-
{
|
|
487
|
-
const D = (A+B)/2, E = p*(B-A)/2;
|
|
488
|
-
x.beginPath();
|
|
489
|
-
F && x.lineTo(X,Y);
|
|
490
|
-
x.arc(X,Y,R,D-E,D+E);
|
|
491
|
-
x.fillStyle = C;
|
|
492
|
-
C ? x.fill() : x.stroke();
|
|
493
|
-
};
|
|
494
|
-
const color = (c=0, l=0) =>
|
|
495
|
-
hsl([.98,.3,.57,.14][c%4],.8,[0,.3,.5,.8,.9][l]).toString();
|
|
496
|
-
const alpha = wave(1,1,t);
|
|
497
|
-
const p = percent(alpha, .1, .5);
|
|
498
|
-
|
|
499
|
-
// setup
|
|
500
|
-
x.translate(w/2,h/2);
|
|
501
|
-
const size = min(6, min(w,h)/99); // fit to screen
|
|
502
|
-
x.scale(size,size);
|
|
503
|
-
x.translate(-40,-35);
|
|
504
|
-
x.lineJoin = x.lineCap = 'round';
|
|
505
|
-
x.lineWidth = .1 + p*1.9;
|
|
506
|
-
|
|
507
|
-
// drawing effect
|
|
508
|
-
const p2 = percent(alpha,.1,1);
|
|
509
|
-
x.setLineDash([99*p2,99]);
|
|
510
|
-
|
|
511
|
-
// cab top
|
|
512
|
-
rect(7,16,18,-8,color(2,2));
|
|
513
|
-
rect(7,8,18,4,color(2,3));
|
|
514
|
-
rect(25,8,8,8,color(2,1));
|
|
515
|
-
rect(25,8,-18,8);
|
|
516
|
-
rect(25,8,8,8);
|
|
517
|
-
|
|
518
|
-
// cab
|
|
519
|
-
rect(25,16,7,23,color());
|
|
520
|
-
rect(11,39,14,-23,color(1,1));
|
|
521
|
-
rect(11,16,14,18,color(1,2));
|
|
522
|
-
rect(11,16,14,8,color(1,3));
|
|
523
|
-
rect(25,16,-14,24);
|
|
524
|
-
|
|
525
|
-
// cab window
|
|
526
|
-
rect(15,29,6,-9,color(2,2));
|
|
527
|
-
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
528
|
-
rect(21,21,-6,9);
|
|
529
|
-
|
|
530
|
-
// little stack
|
|
531
|
-
rect(37,14,9,6,color(3,2));
|
|
532
|
-
rect(37,14,4.5,6,color(3,3));
|
|
533
|
-
rect(37,14,9,6);
|
|
534
|
-
|
|
535
|
-
// big stack
|
|
536
|
-
rect(50,20,10,-8,color(0,1));
|
|
537
|
-
rect(50,20,6.5,-8,color(0,2));
|
|
538
|
-
rect(50,20,3.5,-8,color(0,3));
|
|
539
|
-
rect(50,20,10,-8);
|
|
540
|
-
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
541
|
-
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
542
|
-
circle(55,2,11.4,.5,PI-.5);
|
|
543
|
-
rect(45,7,20,-7,color(0,2));
|
|
544
|
-
rect(45,-1,20,4,color(0,3));
|
|
545
|
-
rect(45,-1,20,8);
|
|
546
|
-
|
|
547
|
-
// engine
|
|
548
|
-
for (let i=5; i--;)
|
|
549
|
-
{
|
|
550
|
-
// stagger radius to fix slight seam
|
|
551
|
-
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
552
|
-
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
553
|
-
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// engine outline
|
|
557
|
-
circle(36,30,10,PI/2,PI*3/2);
|
|
558
|
-
circle(48,30,10,PI/2,PI*3/2);
|
|
559
|
-
circle(60,30,10);
|
|
560
|
-
line(36,20,60,20);
|
|
561
|
-
|
|
562
|
-
// engine front light
|
|
563
|
-
circle(60,30,4,PI,3*PI,color(3,2));
|
|
564
|
-
circle(60,30,4,PI,2*PI,color(3,3));
|
|
565
|
-
circle(60,30,4,PI,3*PI);
|
|
566
|
-
|
|
567
|
-
// front brush
|
|
568
|
-
for (let i=6; i--;)
|
|
569
|
-
{
|
|
570
|
-
x.beginPath();
|
|
571
|
-
x.lineTo(53,54);
|
|
572
|
-
x.lineTo(53,40);
|
|
573
|
-
x.lineTo(53+(1+i*2.9)*p,40);
|
|
574
|
-
x.lineTo(53+(4+i*3.5)*p,54);
|
|
575
|
-
x.fillStyle = color(0,i%2+2);
|
|
576
|
-
x.fill();
|
|
577
|
-
i%2 && x.stroke();
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
// wheels
|
|
581
|
-
rect(6,40,5,5);
|
|
582
|
-
rect(6,40,5,5,color());
|
|
583
|
-
rect(15,54,38,-14,color());
|
|
584
|
-
for (let i=3; i--;)
|
|
585
|
-
for (let j=2; j--;)
|
|
586
|
-
{
|
|
587
|
-
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
588
|
-
x.stroke();
|
|
589
|
-
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
590
|
-
x.stroke();
|
|
591
|
-
}
|
|
592
|
-
line(6,40,68,40); // center
|
|
593
|
-
line(77,54,4,54); // bottom
|
|
594
|
-
|
|
595
|
-
// draw engine name
|
|
596
|
-
const s = engineName;
|
|
597
|
-
x.font = '900 16px arial';
|
|
598
|
-
x.textAlign = 'center';
|
|
599
|
-
x.textBaseline = 'top';
|
|
600
|
-
x.lineWidth = .1+p*3.9;
|
|
601
|
-
let w2 = 0;
|
|
602
|
-
for (let i=0; i<s.length; ++i)
|
|
603
|
-
w2 += x.measureText(s[i]).width;
|
|
604
|
-
for (let j=2; j--;)
|
|
605
|
-
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
606
|
-
{
|
|
607
|
-
x.fillStyle = color(i,2);
|
|
608
|
-
const w = x.measureText(s[i]).width;
|
|
609
|
-
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
610
|
-
X += w;
|
|
611
|
-
}
|
|
612
|
-
x.restore();
|
|
613
|
-
}
|
|
614
|
-
///////////////////////////////////////////////////////////////////////////
|
|
615
453
|
}
|
|
616
454
|
|
|
617
455
|
/** Update each engine object, remove destroyed objects, and update time
|
|
@@ -625,8 +463,7 @@ function engineObjectsUpdate()
|
|
|
625
463
|
// recursive object update
|
|
626
464
|
function updateObject(o)
|
|
627
465
|
{
|
|
628
|
-
if (o.destroyed)
|
|
629
|
-
return;
|
|
466
|
+
if (o.destroyed) return;
|
|
630
467
|
|
|
631
468
|
o.update();
|
|
632
469
|
for (const child of o.children)
|
|
@@ -634,8 +471,7 @@ function engineObjectsUpdate()
|
|
|
634
471
|
}
|
|
635
472
|
for (const o of engineObjects)
|
|
636
473
|
{
|
|
637
|
-
if (o.parent)
|
|
638
|
-
continue;
|
|
474
|
+
if (o.parent || o.destroyed) continue;
|
|
639
475
|
|
|
640
476
|
// update top level objects
|
|
641
477
|
o.update();
|
|
@@ -724,4 +560,169 @@ function engineObjectsRaycast(start, end, objects=engineObjects)
|
|
|
724
560
|
|
|
725
561
|
debugRaycast && debugLine(start, end, hitObjects.length ? '#f00' : '#00f', .02);
|
|
726
562
|
return hitObjects;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
566
|
+
function drawEngineLogo(t)
|
|
567
|
+
{
|
|
568
|
+
// LittleJS Logo and Splash Screen
|
|
569
|
+
const x = mainContext;
|
|
570
|
+
const w = mainCanvas.width = innerWidth;
|
|
571
|
+
const h = mainCanvas.height = innerHeight;
|
|
572
|
+
|
|
573
|
+
{
|
|
574
|
+
// background
|
|
575
|
+
const p3 = percent(t, 1, .8);
|
|
576
|
+
const p4 = percent(t, 0, .5);
|
|
577
|
+
const g = x.createRadialGradient(w/2,h/2,0,w/2,h/2,hypot(w,h)*.7);
|
|
578
|
+
g.addColorStop(0,hsl(0,0,lerp(0,p3/2,p4),p3).toString());
|
|
579
|
+
g.addColorStop(1,hsl(0,0,0,p3).toString());
|
|
580
|
+
x.save();
|
|
581
|
+
x.fillStyle = g;
|
|
582
|
+
x.fillRect(0,0,w,h);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// draw LittleJS logo...
|
|
586
|
+
const rect = (X, Y, W, H, C)=>
|
|
587
|
+
{
|
|
588
|
+
x.beginPath();
|
|
589
|
+
x.rect(X,Y,W,C?H*p:H);
|
|
590
|
+
x.fillStyle = C;
|
|
591
|
+
C ? x.fill() : x.stroke();
|
|
592
|
+
};
|
|
593
|
+
const line = (X, Y, Z, W)=>
|
|
594
|
+
{
|
|
595
|
+
x.beginPath();
|
|
596
|
+
x.lineTo(X,Y);
|
|
597
|
+
x.lineTo(Z,W);
|
|
598
|
+
x.stroke();
|
|
599
|
+
};
|
|
600
|
+
const circle = (X, Y, R, A=0, B=2*PI, C, F)=>
|
|
601
|
+
{
|
|
602
|
+
const D = (A+B)/2, E = p*(B-A)/2;
|
|
603
|
+
x.beginPath();
|
|
604
|
+
F && x.lineTo(X,Y);
|
|
605
|
+
x.arc(X,Y,R,D-E,D+E);
|
|
606
|
+
x.fillStyle = C;
|
|
607
|
+
C ? x.fill() : x.stroke();
|
|
608
|
+
};
|
|
609
|
+
const color = (c=0, l=0)=>
|
|
610
|
+
hsl([.98,.3,.57,.14][c%4],.9,[0,.3,.5,.8,.9][l]).toString();
|
|
611
|
+
const alpha = wave(1,1,t);
|
|
612
|
+
const p = percent(alpha, .1, .5);
|
|
613
|
+
|
|
614
|
+
// setup
|
|
615
|
+
x.translate(w/2,h/2);
|
|
616
|
+
const size = min(6, min(w,h)/99); // fit to screen
|
|
617
|
+
x.scale(size,size);
|
|
618
|
+
x.translate(-40,-35);
|
|
619
|
+
x.lineJoin = x.lineCap = 'round';
|
|
620
|
+
x.lineWidth = .1 + p*1.9;
|
|
621
|
+
|
|
622
|
+
// drawing effect
|
|
623
|
+
const p2 = percent(alpha,.1,1);
|
|
624
|
+
x.setLineDash([99*p2,99]);
|
|
625
|
+
|
|
626
|
+
// cab top
|
|
627
|
+
rect(7,16,18,-8,color(2,2));
|
|
628
|
+
rect(7,8,18,4,color(2,3));
|
|
629
|
+
rect(25,8,8,8,color(2,1));
|
|
630
|
+
rect(25,8,-18,8);
|
|
631
|
+
rect(25,8,8,8);
|
|
632
|
+
|
|
633
|
+
// cab
|
|
634
|
+
rect(25,16,7,24,color());
|
|
635
|
+
rect(11,39,14,-23,color(1,1));
|
|
636
|
+
rect(11,16,14,18,color(1,2));
|
|
637
|
+
rect(11,16,14,8,color(1,3));
|
|
638
|
+
rect(25,16,-14,24);
|
|
639
|
+
|
|
640
|
+
// cab window
|
|
641
|
+
rect(15,29,6,-9,color(2,2));
|
|
642
|
+
circle(15,21,5,0,PI/2,color(2,4),1);
|
|
643
|
+
rect(21,21,-6,9);
|
|
644
|
+
|
|
645
|
+
// little stack
|
|
646
|
+
rect(37,14,9,6,color(3,2));
|
|
647
|
+
rect(37,14,4.5,6,color(3,3));
|
|
648
|
+
rect(37,14,9,6);
|
|
649
|
+
|
|
650
|
+
// big stack
|
|
651
|
+
rect(50,20,10,-8,color(0,1));
|
|
652
|
+
rect(50,20,6.5,-8,color(0,2));
|
|
653
|
+
rect(50,20,3.5,-8,color(0,3));
|
|
654
|
+
rect(50,20,10,-8);
|
|
655
|
+
circle(55,2,11.4,.5,PI-.5,color(3,3));
|
|
656
|
+
circle(55,2,11.4,.5,PI/2,color(3,2),1);
|
|
657
|
+
circle(55,2,11.4,.5,PI-.5);
|
|
658
|
+
rect(45,7,20,-7,color(0,2));
|
|
659
|
+
rect(45,-1,20,4,color(0,3));
|
|
660
|
+
rect(45,-1,20,8);
|
|
661
|
+
|
|
662
|
+
// engine
|
|
663
|
+
for (let i=5; i--;)
|
|
664
|
+
{
|
|
665
|
+
// stagger radius to fix slight seam
|
|
666
|
+
circle(60-i*6,30, 9.9,0,2*PI,color(i+2,3));
|
|
667
|
+
circle(60-i*6,30,10.0,-.5,PI+.5,color(i+2,2));
|
|
668
|
+
circle(60-i*6,30,10.1,.5,PI-.5,color(i+2,1));
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// engine outline
|
|
672
|
+
circle(36,30,10,PI/2,PI*3/2);
|
|
673
|
+
circle(48,30,10,PI/2,PI*3/2);
|
|
674
|
+
circle(60,30,10);
|
|
675
|
+
line(36,20,60,20);
|
|
676
|
+
|
|
677
|
+
// engine front light
|
|
678
|
+
circle(60,30,4,PI,3*PI,color(3,2));
|
|
679
|
+
circle(60,30,4,PI,2*PI,color(3,3));
|
|
680
|
+
circle(60,30,4,PI,3*PI);
|
|
681
|
+
|
|
682
|
+
// front brush
|
|
683
|
+
for (let i=6; i--;)
|
|
684
|
+
{
|
|
685
|
+
x.beginPath();
|
|
686
|
+
x.lineTo(53,54);
|
|
687
|
+
x.lineTo(53,40);
|
|
688
|
+
x.lineTo(53+(1+i*2.9)*p,40);
|
|
689
|
+
x.lineTo(53+(4+i*3.5)*p,54);
|
|
690
|
+
x.fillStyle = color(0,i%2+2);
|
|
691
|
+
x.fill();
|
|
692
|
+
i%2 && x.stroke();
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// wheels
|
|
696
|
+
rect(6,40,5,5);
|
|
697
|
+
rect(6,40,5,5,color());
|
|
698
|
+
rect(15,54,38,-14,color());
|
|
699
|
+
for (let i=3; i--;)
|
|
700
|
+
for (let j=2; j--;)
|
|
701
|
+
{
|
|
702
|
+
circle(15*i+15,47,j?7:1,PI,3*PI,color(i,3));
|
|
703
|
+
x.stroke();
|
|
704
|
+
circle(15*i+15,47,j?7:1,0,PI,color(i,2));
|
|
705
|
+
x.stroke();
|
|
706
|
+
}
|
|
707
|
+
line(6,40,68,40); // center
|
|
708
|
+
line(77,54,4,54); // bottom
|
|
709
|
+
|
|
710
|
+
// draw engine name
|
|
711
|
+
const s = engineName;
|
|
712
|
+
x.font = '900 16px arial';
|
|
713
|
+
x.textAlign = 'center';
|
|
714
|
+
x.textBaseline = 'top';
|
|
715
|
+
x.lineWidth = .1+p*3.9;
|
|
716
|
+
let w2 = 0;
|
|
717
|
+
for (let i=0; i<s.length; ++i)
|
|
718
|
+
w2 += x.measureText(s[i]).width;
|
|
719
|
+
for (let j=2; j--;)
|
|
720
|
+
for (let i=0, X=41-w2/2; i<s.length; ++i)
|
|
721
|
+
{
|
|
722
|
+
x.fillStyle = color(i,2);
|
|
723
|
+
const w = x.measureText(s[i]).width;
|
|
724
|
+
x[j?'strokeText':'fillText'](s[i],X+w/2,55.5,17*p);
|
|
725
|
+
X += w;
|
|
726
|
+
}
|
|
727
|
+
x.restore();
|
|
727
728
|
}
|