littlejsengine 1.14.10 → 1.14.16

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.
Files changed (76) hide show
  1. package/dist/littlejs.d.ts +264 -120
  2. package/dist/littlejs.esm.js +603 -288
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +600 -287
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +521 -232
  7. package/examples/box2d/gameObjects.js +2 -2
  8. package/examples/breakout/gameObjects.js +2 -2
  9. package/examples/breakoutTutorial/README.md +32 -32
  10. package/examples/breakoutTutorial/game.js +1 -1
  11. package/examples/electron/game.js +3 -3
  12. package/examples/electron/index.html +2 -2
  13. package/examples/electron/package.json +1 -8
  14. package/examples/index.html +59 -51
  15. package/examples/module/game.js +3 -3
  16. package/examples/platformer/gameCharacter.js +6 -2
  17. package/examples/platformer/gameEffects.js +4 -4
  18. package/examples/platformer/gameLevel.js +1 -1
  19. package/examples/platformer/gameObjects.js +11 -9
  20. package/examples/puzzle/game.js +1 -1
  21. package/examples/shorts/animation.js +1 -1
  22. package/examples/shorts/base.html +1 -1
  23. package/examples/shorts/blending.js +8 -14
  24. package/examples/shorts/box2d.js +7 -3
  25. package/examples/shorts/box2dCar.js +2 -1
  26. package/examples/shorts/empty.js +30 -0
  27. package/examples/shorts/helloWorld.js +1 -1
  28. package/examples/shorts/hillGlideGame.js +10 -4
  29. package/examples/shorts/landerGame.js +30 -3
  30. package/examples/shorts/medals.js +4 -4
  31. package/examples/shorts/music.js +29 -55
  32. package/examples/shorts/musicPlayer.js +134 -0
  33. package/examples/shorts/nineSlice.js +34 -15
  34. package/examples/shorts/parallax.js +4 -3
  35. package/examples/shorts/particles.js +16 -16
  36. package/examples/shorts/piano.js +15 -21
  37. package/examples/shorts/pongGame.js +6 -4
  38. package/examples/shorts/raycasting.js +13 -5
  39. package/examples/shorts/sequencer.js +122 -0
  40. package/examples/shorts/shapes.js +7 -4
  41. package/examples/shorts/slidingPuzzle.js +16 -6
  42. package/examples/shorts/sound.js +18 -9
  43. package/examples/shorts/spaceGame.js +12 -8
  44. package/examples/shorts/spriteAtlas.js +7 -7
  45. package/examples/shorts/starfield.js +1 -1
  46. package/examples/shorts/systemFont.js +2 -2
  47. package/examples/shorts/texture.js +2 -2
  48. package/examples/shorts/tileLayer.js +10 -10
  49. package/examples/shorts/tiltedView.js +14 -3
  50. package/examples/shorts/timers.js +10 -0
  51. package/examples/shorts/topDown.js +4 -1
  52. package/examples/shorts/uiSystem.js +8 -5
  53. package/examples/starter/game.js +3 -3
  54. package/examples/starter/index.html +2 -2
  55. package/examples/typescript/game.js +3 -3
  56. package/examples/typescript/game.ts +3 -3
  57. package/examples/uiSystem/game.js +6 -6
  58. package/package.json +4 -2
  59. package/plugins/box2d.js +18 -3
  60. package/plugins/newgrounds.js +11 -9
  61. package/plugins/postProcess.js +5 -2
  62. package/plugins/uiSystem.js +120 -34
  63. package/plugins/zzfxm.js +5 -1
  64. package/reference.md +1 -3
  65. package/src/engine.js +52 -21
  66. package/src/engineAudio.js +31 -14
  67. package/src/engineDebug.js +80 -55
  68. package/src/engineDraw.js +60 -31
  69. package/src/engineExport.js +3 -1
  70. package/src/engineMedals.js +10 -2
  71. package/src/engineObject.js +14 -10
  72. package/src/engineParticles.js +59 -46
  73. package/src/engineRelease.js +1 -0
  74. package/src/engineSettings.js +7 -7
  75. package/src/engineTileLayer.js +49 -18
  76. package/src/engineUtilities.js +79 -34
@@ -50,7 +50,7 @@ function loadLevelData()
50
50
  {
51
51
  // load the level data
52
52
  const tileMapData = Game.gameLevelData;
53
- tileLayers = LJS.tileCollisionLoad(tileMapData, tile(0,16,1));
53
+ tileLayers = LJS.tileLayersLoad(tileMapData, tile(0,16,1), 0, 1);
54
54
  levelSize = tileLayers[0].size;
55
55
  playerStartPos = vec2(0, levelSize.y); // default player start
56
56
  foregroundTileLayer = tileLayers[0];
@@ -101,10 +101,14 @@ export class Coin extends LJS.EngineObject
101
101
 
102
102
  render()
103
103
  {
104
+ // side of coin
105
+ LJS.drawTile(this.pos, vec2(.1, .6), 0, this.color);
106
+
104
107
  // make it appear to spin
105
108
  const t = LJS.time+this.pos.x/4+this.pos.y/4;
106
- LJS.drawTile(this.pos, vec2(.1, .6), 0, this.color); // edge of coin
107
- LJS.drawTile(this.pos, vec2(.5+.5*Math.sin(t*2*LJS.PI), 1), this.tileInfo, this.color);
109
+ const spinSize = vec2(.5+.5*Math.sin(t*2*LJS.PI), 1);
110
+ if (spinSize.x > .1)
111
+ LJS.drawTile(this.pos, spinSize, this.tileInfo, this.color);
108
112
  }
109
113
 
110
114
  update()
@@ -227,7 +231,7 @@ export class Grenade extends GameObject
227
231
 
228
232
  export class Weapon extends LJS.EngineObject
229
233
  {
230
- constructor(pos, parent)
234
+ constructor(pos)
231
235
  {
232
236
  super(pos, vec2(.6), Game.spriteAtlas.gun);
233
237
 
@@ -238,20 +242,18 @@ export class Weapon extends LJS.EngineObject
238
242
  this.damage = 1;
239
243
 
240
244
  // prepare to fire
241
- this.renderOrder = parent.renderOrder + 1;
242
245
  this.fireTimeBuffer = this.localAngle = 0;
243
246
  this.recoilTimer = new Timer;
244
- parent.addChild(this, vec2(.6,0));
245
247
 
246
248
  // shell effect
247
249
  this.addChild(this.shellEmitter = new LJS.ParticleEmitter(
248
- vec2(), 0, 0, 0, 0, .1, // pos, angle, emitSize, emitTime, emitRate, emitCone
250
+ vec2(), 0, 0, 0, 0, .1, // pos, angle, size, time, rate, cone
249
251
  0, // tileInfo
250
252
  rgb(1,.8,.5), rgb(.9,.7,.5), // colorStartA, colorStartB
251
253
  rgb(1,.8,.5), rgb(.9,.7,.5), // colorEndA, colorEndB
252
254
  3, .1, .1, .15, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
253
255
  1, .95, 1, 0, 0, // damp, angleDamp, gravity, particleCone, fade
254
- .1, 1 // randomness, collide, additive, colorLinear, renderOrder
256
+ .1, 1 // randomness, collide
255
257
  ), vec2(.1,0), -.8);
256
258
  this.shellEmitter.restitution = .5;
257
259
  this.shellEmitter.particleDestroyCallback = GameEffects.persistentParticleDestroyCallback;
@@ -366,13 +368,13 @@ export class Bullet extends LJS.EngineObject
366
368
 
367
369
  // spark effects
368
370
  const emitter = new LJS.ParticleEmitter(
369
- this.pos, 0, 0, .1, 100, .5, // pos, angle, emitSize, emitTime, emitRate, emitCone
371
+ this.pos, 0, 0, .1, 100, .5, // pos, angle, size, time, rate, cone
370
372
  0, // tileInfo
371
373
  rgb(1,1,0), rgb(1,0,0), // colorStartA, colorStartB
372
374
  rgb(1,1,0), rgb(1,0,0), // colorEndA, colorEndB
373
375
  .2, .2, 0, .1, .1, // time, sizeStart, sizeEnd, speed, angleSpeed
374
376
  1, 1, .5, 3.14, .1, // damp, angleDamp, gravityScale, particleCone, fade,
375
- .5, 1, 1 // randomness, collide, additive, colorLinear, renderOrder
377
+ .5, 1, 1 // randomness, collide, additive
376
378
  );
377
379
  emitter.trailScale = 1;
378
380
  emitter.restitution = .3;
@@ -309,7 +309,7 @@ function clearMatches()
309
309
  const color2 = color1.lerp(hsl(), .5);
310
310
  new LJS.ParticleEmitter(
311
311
  pos.add(vec2(.5)), 0, // pos, angle
312
- .5, .1, 200, 3.14, // emitSize, emitTime, emitRate, emitCone
312
+ .5, .1, 200, 3.14, // emitSize, emitTime, rate, cone
313
313
  0, // tileInfo
314
314
  color1, color2, // colorStartA, colorStartB
315
315
  color1.scale(1,0), color2.scale(1,0),// colorEndA, colorEndB
@@ -3,7 +3,7 @@ canvasClearColor = GRAY;
3
3
  function gameRender()
4
4
  {
5
5
  {
6
- // animate using multiple frames
6
+ // animate by changing frames
7
7
  const pos = vec2(-5, 2*abs(Math.sin(time*2*PI)));
8
8
  const frame = (time*4)%2|0;
9
9
  drawTile(pos, vec2(7), tile(3).frame(frame), RED);
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html><meta charset=utf-8><body>
2
- <script src=../../dist/littlejs.js?1134></script>
2
+ <script src=../../dist/littlejs.js?11416></script>
3
3
  <script src=../../dist/box2d.wasm.js></script>
4
4
 
5
5
  <!-- LittleJS Engine Source
@@ -1,20 +1,14 @@
1
- // use smooth blending
2
- setCanvasPixelated(false);
3
- setTilesPixelated(false);
4
-
5
1
  function gameRender()
6
2
  {
7
- const circleTile = tile(2, 128); // get the circle tile
8
-
9
- // additive blend
3
+ // additive blending
10
4
  setBlendMode(1);
11
- drawTile(vec2(-8,-2), vec2(7), circleTile, rgb(0,0,.5), 0, false, rgb(0,0,.5));
12
- drawTile(vec2(-6, 2), vec2(7), circleTile, rgb(0,1,0));
13
- drawTile(vec2(-4,-2), vec2(7), circleTile, rgb(1,0,0));
5
+ drawCircle(vec2(-8,-2), 7, rgb(0,0,.5));
6
+ drawCircle(vec2(-6, 2), 7, rgb(0,1,0));
7
+ drawCircle(vec2(-4,-2), 7, rgb(1,0,0));
14
8
 
15
- // alpha blend
9
+ // alpha blending
16
10
  setBlendMode(0);
17
- drawTile(vec2(4,-2), vec2(7), circleTile, hsl(time/9 ,1,.5), 0, false, hsl(1,1,.5));
18
- drawTile(vec2(8,-2), vec2(7), circleTile, hsl(time/9+1/3,1,.5,.5));
19
- drawTile(vec2(6, 2), vec2(7), circleTile, hsl(time/9+2/3,1,.5,.5));
11
+ drawCircle(vec2(4,-2), 7, hsl(time/9 ,1,.5));
12
+ drawCircle(vec2(8,-2), 7, hsl(time/9+1/3,1,.5,.5));
13
+ drawCircle(vec2(6, 2), 7, hsl(time/9+2/3,1,.5,.5));
20
14
  }
@@ -7,13 +7,16 @@ async function gameInit()
7
7
  canvasClearColor = hsl(0,0,.9);
8
8
 
9
9
  // create ground object
10
- groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0, GRAY, box2d.bodyTypeStatic);
10
+ groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0,
11
+ GRAY, box2d.bodyTypeStatic);
11
12
  groundObject.addBox(vec2(100,2));
12
13
 
13
14
  // add some random objects
14
15
  for (let i=50; i--;)
15
16
  {
16
- const o = new Box2dObject(randInCircle(5), vec2(), 0, 0, randColor());
17
+ const pos = randInCircle(5);
18
+ const color = randColor();
19
+ const o = new Box2dObject(pos, vec2(), 0, 0, color);
17
20
  randInt(2) ? o.addCircle(rand(1,2)) : o.addRandomPoly(rand(1,2));
18
21
  }
19
22
  }
@@ -36,7 +39,8 @@ function gameUpdate()
36
39
  // grab object
37
40
  const object = box2d.pointCast(mousePos);
38
41
  if (object)
39
- mouseJoint = new Box2dTargetJoint(object, groundObject, mousePos);
42
+ mouseJoint = new Box2dTargetJoint(object,
43
+ groundObject, mousePos);
40
44
  }
41
45
  }
42
46
 
@@ -3,7 +3,8 @@ async function gameInit()
3
3
  // setup box2d and create the objects
4
4
  await box2dInit();
5
5
  gravity.y = -20;
6
- groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0, GRAY, box2d.bodyTypeStatic);
6
+ groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0,
7
+ GRAY, box2d.bodyTypeStatic);
7
8
  groundObject.addBox(vec2(100,6));
8
9
  new CarObject(vec2(0,-2));
9
10
  canvasClearColor = hsl(0,0,.9);
@@ -0,0 +1,30 @@
1
+ function gameInit()
2
+ {
3
+ // called once after the engine starts up
4
+ // setup the game
5
+ }
6
+
7
+ function gameUpdate()
8
+ {
9
+ // called every frame at 60 frames per second
10
+ // handle input and update the game state
11
+ }
12
+
13
+ function gameUpdatePost()
14
+ {
15
+ // called after physics and objects are updated
16
+ // setup camera and prepare for render
17
+ }
18
+
19
+ function gameRender()
20
+ {
21
+ // called before objects are rendered
22
+ // draw any background effects that appear behind objects
23
+ }
24
+
25
+ function gameRenderPost()
26
+ {
27
+ // called after objects are rendered
28
+ // draw effects or hud that appear above all objects
29
+ drawTextScreen('LittleJS Engine', mainCanvasSize.scale(.5), 80);
30
+ }
@@ -4,7 +4,7 @@ function gameRender()
4
4
  drawRectGradient(cameraPos, getCameraSize(), BLACK, WHITE);
5
5
 
6
6
  // draw text to the overlay canvas
7
- drawTextOverlay('Hello World', vec2(0,3), 3);
7
+ drawTextOverlay('LittleJS Engine', vec2(0,3), 3);
8
8
 
9
9
  // draw a tile
10
10
  drawTile(vec2(Math.sin(time)*3,-2), vec2(7), tile(3,128));
@@ -2,7 +2,7 @@ class Player extends EngineObject
2
2
  {
3
3
  constructor(pos)
4
4
  {
5
- super(pos, vec2(2), tile(9), 0, CYAN);
5
+ super(pos, vec2(2), tile(9), 0, RED);
6
6
  this.damping = 1; // disable damping
7
7
  this.clampSpeed = false; // disable speed clamping
8
8
  }
@@ -11,12 +11,14 @@ class Player extends EngineObject
11
11
  {
12
12
  super.update();
13
13
 
14
+ // check ground height
14
15
  const h = getGroundHeight(this.pos.x);
15
16
  if (this.pos.y < h + this.size.y/2)
16
17
  {
17
18
  // clamp to ground and reflect velocity
18
19
  this.pos.y = h + this.size.y/2;
19
- const n = vec2((h-getGroundHeight(this.pos.x+.1))/.1, 1).normalize();
20
+ const h2 = getGroundHeight(this.pos.x+.1);
21
+ const n = vec2((h-h2)/.1, 1).normalize();
20
22
  this.velocity = this.velocity.reflect(n,0);
21
23
  }
22
24
 
@@ -40,13 +42,17 @@ function gameInit()
40
42
  {
41
43
  gravity.y = -.01;
42
44
  new Player(vec2(0,5));
43
- canvasClearColor = hsl(.7,1,.8);
45
+ canvasClearColor = hsl(.6,1,.8);
44
46
  }
45
47
 
46
48
  function gameRender()
47
49
  {
50
+ // draw ground as a series of thin rectangles
48
51
  const h = 100, w = 20;
49
- const pos = vec2(), sizeTop = vec2(.4), size = vec2(.2,h), color = WHITE;
52
+ const pos = vec2();
53
+ const sizeTop = vec2(.4);
54
+ const size = vec2(.2,h);
55
+ const color = rgb();
50
56
  for (let x=cameraPos.x-w; x<cameraPos.x+w; x+=.1)
51
57
  {
52
58
  pos.x = x;
@@ -4,14 +4,39 @@ class Player extends EngineObject
4
4
  {
5
5
  super(pos, vec2(1,2), tile(8), 0, RED);
6
6
  this.setCollision(); // make object collide
7
+ this.angleDamping = .95;
7
8
  }
8
9
 
9
10
  update()
10
11
  {
11
12
  super.update();
13
+
14
+ // space ship controls
15
+ const moveInput = keyDirection();
16
+ this.applyAngularAcceleration(moveInput.x * .002);
17
+ const accel = vec2(0, moveInput.y*.002).rotate(this.angle);
18
+ this.applyAcceleration(accel);
19
+ }
20
+
21
+ // explode when it collides
22
+ collideWithObject()
23
+ {
24
+ // don't explode if player is almost still and not tilted
25
+ if (abs(this.angle) < .2)
26
+ if (this.velocity.length() < .05)
27
+ return true;
12
28
 
13
- // apply movement controls
14
- this.applyAcceleration(keyDirection().scale(.002));
29
+ // explode!
30
+ this.destroy();
31
+ new ParticleEmitter(
32
+ this.pos, 0, // pos, angle
33
+ 1, .1, 300, 3.14, 0, // emitSize, emitTime, rate, cone, tile
34
+ rgb(1,.5,.1), rgb(1,.1,.1), // colorStartA, colorStartB
35
+ rgb(1,.5,.1,0), rgb(1,.1,.1,0), // colorEndA, colorEndB
36
+ .7, .8, .2, .2, .05, // time, start, end, speed, angleSpeed
37
+ .9, 1, -.2, 3.14, .05, // damp, angleDamp, gravity, cone, fade
38
+ 1, 0, 1 // randomness, collide, additive
39
+ );
15
40
  }
16
41
  }
17
42
 
@@ -25,7 +50,9 @@ function gameInit()
25
50
  for (let i=0, y=0; i<20; ++i)
26
51
  {
27
52
  y = (y + rand(1,-1)*4) * .5;
28
- const o = new EngineObject(vec2(i*2-20, y-54), vec2(2, 99), 0, 0, hsl(0,0,.5+i%.29));
53
+ const pos = vec2(i*2-20, y-54);
54
+ const color = hsl(0, 0, .5+i%.29);
55
+ const o = new EngineObject(pos, vec2(2, 99), 0, 0, color);
29
56
  o.setCollision(); // make object collide
30
57
  o.mass = 0; // make object have static physics
31
58
  }
@@ -1,8 +1,8 @@
1
1
  // create example medals
2
- const medal_openedExample = new Medal(0, 'Opened Example', 'You opened this example!');
3
- const medal_leftClick = new Medal(1, 'Left Clicked', 'You left clicked!', '🐁');
4
- const medal_rightClick = new Medal(2, 'Right Clicked', 'You right clicked!', '🐭');
5
- const medal_spacePressed = new Medal(3, 'Pressed Spacebar', 'You pressed spacebar!', '🚀');
2
+ const medal_openedExample = new Medal(0, 'Open', 'Opened this example!');
3
+ const medal_leftClick = new Medal(1, 'Lefty', 'Left clicked!', '🐁');
4
+ const medal_rightClick = new Medal(2, 'Righty', 'Right clicked!', '🐭');
5
+ const medal_spacePressed = new Medal(3, 'Space', 'Pressed spacebar!', '🚀');
6
6
 
7
7
  // setup medals
8
8
  const saveName = 'Medals Example';
@@ -1,7 +1,7 @@
1
- let playButton, stopButton, progressBar;
1
+ let musicPlayer, playButton, stopButton, infoText;
2
2
  let musicSound = new SoundWave('song.mp3');
3
3
  let musicVolume = 1;
4
- let musicInstance;
4
+ let music;
5
5
 
6
6
  function gameInit()
7
7
  {
@@ -10,27 +10,28 @@ function gameInit()
10
10
  uiSystem.defaultSoundPress = new Sound([.5,0,220]);
11
11
  uiSystem.defaultSoundClick = new Sound([.5,0,440]);
12
12
  uiSystem.defaultCornerRadius = 20;
13
- canvasClearColor = hsl(.9,.3,.2); // background color
13
+ canvasClearColor = hsl(.9,.3,.2);
14
14
 
15
15
  // setup music player UI
16
- const musicPlayer = new UIObject(mainCanvasSize.scale(.5), vec2(500, 300));
16
+ musicPlayer = new UIObject(mainCanvasSize.scale(.5), vec2(500, 220));
17
17
 
18
- // title
19
- const title = new UIText(vec2(0, -100), vec2(500, 40), 'LittleJS Music Player');
20
- musicPlayer.addChild(title);
18
+ // infomation text
19
+ infoText = new UIText(vec2(0, -70), vec2(400, 50));
20
+ musicPlayer.addChild(infoText);
21
21
 
22
22
  // volume slider
23
- const volumeSlider = new UIScrollbar(vec2(0, -40), vec2(400, 30), musicVolume, 'Music Volume');
23
+ const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
24
+ musicVolume, 'Music Volume');
24
25
  musicPlayer.addChild(volumeSlider);
25
26
  volumeSlider.onChange = () =>
26
27
  {
27
28
  musicVolume = volumeSlider.value;
28
- if (musicInstance)
29
- musicInstance.setVolume(musicVolume);
29
+ if (music)
30
+ music.setVolume(musicVolume);
30
31
  };
31
32
 
32
33
  // play button
33
- playButton = new UIButton(vec2(-90, 30), vec2(140, 50), 'Play');
34
+ playButton = new UIButton(vec2(-90, 50), vec2(140, 50), 'Play');
34
35
  musicPlayer.addChild(playButton);
35
36
  playButton.onClick = ()=>
36
37
  {
@@ -38,67 +39,40 @@ function gameInit()
38
39
  return;
39
40
 
40
41
  // handle play/pause toggle
41
- if (!musicInstance)
42
- musicInstance = musicSound.playMusic(musicVolume);
43
- else if (musicInstance.isPaused())
44
- musicInstance.resume();
42
+ if (!music)
43
+ music = musicSound.playMusic(musicVolume);
44
+ else if (music.isPaused())
45
+ music.resume();
45
46
  else
46
- musicInstance.pause();
47
+ music.pause();
47
48
  };
48
49
 
49
50
  // stop button
50
- stopButton = new UIButton(vec2(90, 30), vec2(140, 50), 'Stop');
51
+ stopButton = new UIButton(vec2(90, 50), vec2(140, 50), 'Stop');
52
+ stopButton.onClick = ()=> music && music.stop();
51
53
  musicPlayer.addChild(stopButton);
52
- stopButton.onClick = ()=>
53
- {
54
- if (!musicInstance)
55
- return;
56
-
57
- // stop sound
58
- musicInstance.stop();
59
- };
60
-
61
- // progress bar and scrollbar for seeking
62
- progressBar = new UIScrollbar(vec2(0, 100), vec2(400, 30), 0, 'Progress');
63
- musicPlayer.addChild(progressBar);
64
- progressBar.onChange = ()=>
65
- {
66
- // control music seek position
67
- const wasPlaying = musicInstance && musicInstance.isPlaying();
68
- if (!musicInstance)
69
- musicInstance = musicSound.playMusic(musicVolume, true, true);
70
- progressBar.value = min(progressBar.value, .999); // prevent looping around
71
- const seekTime = progressBar.value * musicSound.getDuration();
72
- musicInstance.start(seekTime);
73
- if (!wasPlaying)
74
- musicInstance.pause();
75
- };
76
54
  }
77
55
 
78
56
  function gameUpdate()
79
57
  {
80
- // disable buttons while loading or waiting for interaction
81
- const isDisabled = !musicSound.isLoaded() || !audioIsRunning();
82
- playButton.disabled = isDisabled
83
- stopButton.disabled = isDisabled
84
- progressBar.disabled = isDisabled
58
+ // disable buttons while loading
59
+ const isDisabled = !musicSound.isLoaded();
60
+ playButton.disabled = stopButton.disabled = isDisabled;
85
61
 
86
62
  // update ui
87
- if (!musicSound.isLoaded())
63
+ if (isDisabled)
88
64
  {
89
65
  // update loading progress
90
- const loadingPercent = musicSound.loadedPercent * 100;
91
- progressBar.text = `Loading: ${(loadingPercent).toFixed(2)}%`;
66
+ const loadingPercent = musicSound.loadedPercent * 100|0;
67
+ infoText.text = `Loading: ${loadingPercent}%`;
92
68
  }
93
69
  else
94
70
  {
95
71
  // update ui text
96
- playButton.text = !musicInstance || !musicInstance.isPlaying() ? 'Play' : 'Pause';
97
- const currentTime = musicInstance ? musicInstance.getCurrentTime() : 0;
72
+ const isPlaying = music && music.isPlaying();
73
+ playButton.text = isPlaying ? 'Pause' : 'Play';
74
+ const current = music ? music.getCurrentTime() : 0;
98
75
  const duration = musicSound.getDuration();
99
- if (!progressBar.mouseIsHeld)
100
- progressBar.value = currentTime / duration;
101
- const timeText = `${formatTime(currentTime)} / ${formatTime(duration)}`;
102
- progressBar.text = audioIsRunning() ? timeText : 'Click to Enable Audio';
76
+ infoText.text = formatTime(current) +' / '+ formatTime(duration);
103
77
  }
104
78
  }
@@ -0,0 +1,134 @@
1
+ let musicPlayer, playButton, stopButton, progressBar;
2
+ let musicSound, music;
3
+ let musicVolume = 1;
4
+
5
+ function gameInit()
6
+ {
7
+ // setup ui system plugin
8
+ new UISystemPlugin;
9
+ uiSystem.defaultSoundPress = new Sound([.5,0,220]);
10
+ uiSystem.defaultSoundClick = new Sound([.5,0,440]);
11
+ uiSystem.defaultCornerRadius = 20;
12
+ canvasClearColor = hsl(.9,.3,.2);
13
+
14
+ // setup music player UI
15
+ musicPlayer = new UIObject(mainCanvasSize.scale(.5), vec2(500, 300));
16
+ const title = new UIText(vec2(0, -100), vec2(500, 40),
17
+ 'LittleJS Music Player');
18
+ musicPlayer.addChild(title);
19
+
20
+ // drop zone text
21
+ const dropZoneText = new UIText(vec2(0, -60), vec2(450, 20),
22
+ 'Drag & Drop Audio Files Here!');
23
+ dropZoneText.textColor = hsl(.9, .3, .8, .7);
24
+ musicPlayer.addChild(dropZoneText);
25
+
26
+ // volume slider
27
+ const volumeSlider = new UIScrollbar(vec2(0, -20), vec2(400, 30),
28
+ musicVolume, 'Music Volume');
29
+ musicPlayer.addChild(volumeSlider);
30
+ volumeSlider.onChange = () =>
31
+ {
32
+ musicVolume = volumeSlider.value;
33
+ if (music)
34
+ music.setVolume(musicVolume);
35
+ };
36
+
37
+ // play button
38
+ playButton = new UIButton(vec2(-90, 50), vec2(140, 50), 'Play');
39
+ musicPlayer.addChild(playButton);
40
+ playButton.onClick = ()=>
41
+ {
42
+ if (!musicSound.isLoaded())
43
+ return;
44
+
45
+ // handle play/pause toggle
46
+ if (!music)
47
+ music = musicSound.playMusic(musicVolume);
48
+ else if (music.isPaused())
49
+ music.resume();
50
+ else
51
+ music.pause();
52
+ };
53
+
54
+ // stop button
55
+ stopButton = new UIButton(vec2(90, 50), vec2(140, 50), 'Stop');
56
+ stopButton.onClick = ()=> music && music.stop();
57
+ musicPlayer.addChild(stopButton);
58
+
59
+ // progress bar and scrollbar for seeking
60
+ progressBar = new UIScrollbar(vec2(0, 120), vec2(400, 30), 0);
61
+ progressBar.disabledColor = RED;
62
+ progressBar.onChange = ()=>
63
+ {
64
+ // control music seek position
65
+ const wasPlaying = music && music.isPlaying();
66
+ if (!music)
67
+ music = musicSound.playMusic(musicVolume, true, true);
68
+ progressBar.value = min(progressBar.value, .999); // prevent wrap
69
+ const seekTime = progressBar.value * musicSound.getDuration();
70
+ music.start(seekTime);
71
+ if (!wasPlaying)
72
+ music.pause();
73
+ };
74
+ musicPlayer.addChild(progressBar);
75
+
76
+ {
77
+ // setup drag and drop for audio files
78
+ function onDragEnter() { musicPlayer.color = RED; };
79
+ function onDragLeave() { musicPlayer.color = WHITE; };
80
+ function onDrop(e)
81
+ {
82
+ musicPlayer.color = WHITE
83
+
84
+ // get the dropped file
85
+ const file = e.dataTransfer.files[0];
86
+ if (!file || !file.type.startsWith('audio'))
87
+ return;
88
+
89
+ // create new sound from dropped file
90
+ const fileURL = URL.createObjectURL(file);
91
+ musicSound = new SoundWave(fileURL, musicVolume);
92
+ dropZoneText.text = file.name;
93
+
94
+ // reset UI
95
+ music && music.stop();
96
+ music = undefined;
97
+ progressBar.value = 0;
98
+ }
99
+ uiSystem.setupDragAndDrop(onDrop, onDragEnter, onDragLeave);
100
+ }
101
+ }
102
+
103
+ function gameUpdate()
104
+ {
105
+ // disable buttons while loading
106
+ const isDisabled = !musicSound || !musicSound.isLoaded();
107
+ playButton.disabled = isDisabled
108
+ stopButton.disabled = isDisabled
109
+ progressBar.disabled = isDisabled
110
+
111
+ // update ui
112
+ if (!musicSound)
113
+ {
114
+ // waiting for file
115
+ progressBar.text = 'No File Loaded';
116
+ }
117
+ else if (isDisabled)
118
+ {
119
+ // update loading progress
120
+ const loadingPercent = musicSound.loadedPercent * 100|0;
121
+ progressBar.text = `Loading: ${loadingPercent}%`;
122
+ }
123
+ else
124
+ {
125
+ // update ui text
126
+ const isPlaying = music && music.isPlaying();
127
+ playButton.text = isPlaying ? 'Pause' : 'Play';
128
+ const current = music ? music.getCurrentTime() : 0;
129
+ const duration = musicSound.getDuration();
130
+ progressBar.text = formatTime(current) +' / '+ formatTime(duration);
131
+ if (!progressBar.mouseIsHeld)
132
+ progressBar.value = current / duration;
133
+ }
134
+ }
@@ -3,19 +3,38 @@ function gameRender()
3
3
  const nineSliceTile = tile(16);
4
4
  const threeSliceTile = tile(19);
5
5
 
6
- // draw nine slice with thin border and color
7
- drawNineSlice(vec2(-7,4), vec2(11+wave(.5,2), 6), nineSliceTile, hsl(.1,.5,.9), .5);
8
- drawTextOverlay('Nine Slice\nThin Border', vec2(-7,4), 1, BLACK);
9
-
10
- // draw nine slice in screen space with thick border and rotation
11
- drawNineSliceScreen(vec2(700,150), vec2(250), nineSliceTile, 32, 2, time/2);
12
- drawTextScreen('Nine Slice\nScreen Space', vec2(700,150), 30, BLACK);
13
-
14
- // draw three slice with variable border and additive color
15
- drawThreeSlice(vec2(-7,-4), vec2(9, 7), threeSliceTile, WHITE, 2+wave(.2)*2, hsl(time/30,.5,.5));
16
- drawTextOverlay('Three Slice\nVariable\n Border', vec2(-7,-4), 1, BLACK);
17
-
18
- // draw three slice in screen space with changing size
19
- drawThreeSliceScreen(vec2(700,420), vec2(350-wave(.3,90),120+wave(.3,60)), threeSliceTile);
20
- drawTextScreen('Three Slice\nScreen Space', vec2(700,420), 30, BLACK);
6
+ {
7
+ // draw nine slice with thin border and color
8
+ const pos = vec2(-7,4);
9
+ const size = vec2(11+wave(.5,2), 6);
10
+ const color = hsl(.1,.5,.9);
11
+ const border = .5;
12
+ drawNineSlice(pos, size, nineSliceTile, color, border);
13
+ drawTextOverlay('Nine Slice\nThin Border', pos, 1, BLACK);
14
+ }
15
+ {
16
+ // draw nine slice in screen space with thick border and rotation
17
+ const pos = vec2(700,150);
18
+ const size = vec2(250);
19
+ const border = 32;
20
+ const angle = time/2;
21
+ drawNineSliceScreen(pos, size, nineSliceTile, border, 2, angle);
22
+ drawTextScreen('Nine Slice\nScreen Space', pos, 30, BLACK);
23
+ }
24
+ {
25
+ // draw three slice with variable border and additive color
26
+ const pos = vec2(-7,-4);
27
+ const size = vec2(9, 7);
28
+ const border = 2 + wave(.2)*2;
29
+ const additive = hsl(time/30,.5,.5);
30
+ drawThreeSlice(pos, size, threeSliceTile, WHITE, border, additive);
31
+ drawTextOverlay('Three Slice\nVariable\nBorder', pos, 1, BLACK);
32
+ }
33
+ {
34
+ // draw three slice in screen space with changing size
35
+ const pos = vec2(700,420);
36
+ const size = vec2(350-wave(.3,90),120+wave(.3,60));
37
+ drawThreeSliceScreen(pos, size, threeSliceTile);
38
+ drawTextScreen('Three Slice\nScreen Space', pos, 30, BLACK);
39
+ }
21
40
  }