littlejsengine 1.14.11 → 1.14.19

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 (80) hide show
  1. package/dist/littlejs.d.ts +303 -137
  2. package/dist/littlejs.esm.js +936 -463
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +934 -462
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +859 -417
  7. package/examples/box2d/game.js +3 -2
  8. package/examples/box2d/gameObjects.js +2 -2
  9. package/examples/box2d/tiles.png +0 -0
  10. package/examples/breakout/game.js +5 -5
  11. package/examples/breakout/gameObjects.js +2 -2
  12. package/examples/breakoutTutorial/README.md +32 -32
  13. package/examples/breakoutTutorial/game.js +1 -1
  14. package/examples/electron/game.js +4 -4
  15. package/examples/electron/index.html +2 -2
  16. package/examples/electron/package.json +1 -8
  17. package/examples/index.html +67 -56
  18. package/examples/module/game.js +4 -4
  19. package/examples/platformer/gameEffects.js +5 -5
  20. package/examples/platformer/gameLevel.js +1 -1
  21. package/examples/platformer/gameObjects.js +4 -4
  22. package/examples/puzzle/game.js +1 -1
  23. package/examples/shorts/animation.js +1 -1
  24. package/examples/shorts/base.html +1 -1
  25. package/examples/shorts/blending.js +8 -14
  26. package/examples/shorts/box2d.js +7 -3
  27. package/examples/shorts/box2dCar.js +2 -1
  28. package/examples/shorts/empty.js +30 -0
  29. package/examples/shorts/helloWorld.js +1 -1
  30. package/examples/shorts/hillGlideGame.js +10 -4
  31. package/examples/shorts/landerGame.js +11 -8
  32. package/examples/shorts/medals.js +4 -4
  33. package/examples/shorts/music.js +29 -56
  34. package/examples/shorts/musicPlayer.js +135 -0
  35. package/examples/shorts/nineSlice.js +34 -15
  36. package/examples/shorts/parallax.js +5 -4
  37. package/examples/shorts/particles.js +15 -15
  38. package/examples/shorts/piano.js +15 -21
  39. package/examples/shorts/pongGame.js +6 -4
  40. package/examples/shorts/raycasting.js +9 -4
  41. package/examples/shorts/sequencer.js +122 -0
  42. package/examples/shorts/shader.js +29 -0
  43. package/examples/shorts/shapes.js +7 -4
  44. package/examples/shorts/slidingPuzzle.js +4 -2
  45. package/examples/shorts/sound.js +18 -9
  46. package/examples/shorts/spaceGame.js +12 -8
  47. package/examples/shorts/spriteAtlas.js +7 -7
  48. package/examples/shorts/starfield.js +1 -1
  49. package/examples/shorts/systemFont.js +2 -2
  50. package/examples/shorts/texture.js +2 -2
  51. package/examples/shorts/tileLayer.js +10 -10
  52. package/examples/shorts/tiltedView.js +24 -5
  53. package/examples/shorts/timers.js +10 -0
  54. package/examples/shorts/topDown.js +4 -1
  55. package/examples/shorts/uiSystem.js +8 -5
  56. package/examples/starter/game.js +4 -4
  57. package/examples/starter/index.html +2 -2
  58. package/examples/style.css +1 -0
  59. package/examples/typescript/game.js +4 -4
  60. package/examples/typescript/game.ts +4 -4
  61. package/examples/uiSystem/game.js +4 -3
  62. package/package.json +4 -2
  63. package/plugins/box2d.js +17 -2
  64. package/plugins/newgrounds.js +7 -5
  65. package/plugins/postProcess.js +76 -53
  66. package/plugins/uiSystem.js +192 -53
  67. package/plugins/zzfxm.js +5 -1
  68. package/reference.md +1 -1
  69. package/src/engine.js +60 -23
  70. package/src/engineAudio.js +44 -17
  71. package/src/engineDebug.js +75 -45
  72. package/src/engineDraw.js +94 -59
  73. package/src/engineExport.js +2 -1
  74. package/src/engineMedals.js +10 -2
  75. package/src/engineObject.js +14 -10
  76. package/src/engineParticles.js +60 -47
  77. package/src/engineSettings.js +7 -7
  78. package/src/engineTileLayer.js +73 -56
  79. package/src/engineUtilities.js +67 -20
  80. package/src/engineWebGL.js +134 -63
@@ -56,10 +56,9 @@ async function gameInit()
56
56
  // start up LittleJS Box2D plugin
57
57
  await LJS.box2dInit();
58
58
  //LJS.box2dSetDebug(true); // enable box2d debug draw
59
- LJS.setCanvasClearColor(hsl(0,0,.8));
60
59
 
61
60
  // create a table of all sprites
62
- const gameTile = (i)=> LJS.tile(i, 496, 0, 8);
61
+ const gameTile = (i)=> LJS.tile(i, 124, 0, 2);
63
62
  spriteAtlas =
64
63
  {
65
64
  circle: gameTile(0),
@@ -71,6 +70,8 @@ async function gameInit()
71
70
  squareOutline2: gameTile(6),
72
71
  };
73
72
 
73
+ // startup the demo
74
+ LJS.setCanvasClearColor(hsl(0,0,.8));
74
75
  setScene(startScene);
75
76
  }
76
77
 
@@ -471,7 +471,7 @@ export function explosion(pos, radius=3, strength=300)
471
471
  // smoke
472
472
  new LJS.ParticleEmitter(
473
473
  pos, 0, // pos, angle
474
- radius/2, .2, 50*radius, 3.14,// emitSize, emitTime, emitRate, emitCone
474
+ radius/2, .2, 50*radius, 3.14,// emitSize, emitTime, rate, cone
475
475
  Game.spriteAtlas.dot, // tileInfo
476
476
  hsl(0,0,0), hsl(0,0,0), // colorStartA, colorStartB
477
477
  hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
@@ -483,7 +483,7 @@ export function explosion(pos, radius=3, strength=300)
483
483
  // fire
484
484
  new LJS.ParticleEmitter(
485
485
  pos, 0, // pos, angle
486
- radius, .1, 100*radius, 3.14, // emitSize, emitTime, emitRate, emitCone
486
+ radius, .1, 100*radius, 3.14, // emitSize, emitTime, rate, cone
487
487
  Game.spriteAtlas.dot, // tileInfo
488
488
  hsl(0,1,.5), hsl(.15,1,.5), // colorStartA, colorStartB
489
489
  hsl(0,1,.5,0), hsl(.1, 1,.5,0), // colorEndA, colorEndB
Binary file
@@ -104,11 +104,12 @@ function gameRenderPost()
104
104
  {
105
105
  // use built in image font for text
106
106
  const font = new LJS.FontImage;
107
- font.drawText('Score: ' + score, LJS.cameraPos.add(vec2(0,9.7)), .15, true);
107
+
108
+ font.drawTextOverlay('Score: ' + score, LJS.cameraPos.add(vec2(0,9.7)), .15, true);
108
109
  if (!brickCount)
109
- font.drawText('You Win!', LJS.cameraPos.add(vec2(0,-5)), .2, true);
110
+ font.drawTextOverlay('You Win!', LJS.cameraPos.add(vec2(0,-5)), .2, true);
110
111
  else if (!ball)
111
- font.drawText('Click to Play', LJS.cameraPos.add(vec2(0,-5)), .2, true);
112
+ font.drawTextOverlay('Click to Play', LJS.cameraPos.add(vec2(0,-5)), .2, true);
112
113
  }
113
114
 
114
115
  ///////////////////////////////////////////////////////////////////////////////
@@ -167,8 +168,7 @@ function setupPostProcess()
167
168
  c *= 1.-pow((dx*dx + dy*dy)/vignette, vignettePow);
168
169
  }`;
169
170
 
170
- const includeOverlay = true;
171
- new LJS.PostProcessPlugin(televisionShader, includeOverlay);
171
+ new LJS.PostProcessPlugin(televisionShader);
172
172
  }
173
173
 
174
174
  ///////////////////////////////////////////////////////////////////////////////
@@ -74,7 +74,7 @@ export class Brick extends PhysicsObject
74
74
  const color2 = color1.lerp(hsl(), .5);
75
75
  new LJS.ParticleEmitter(
76
76
  this.pos, 0, // pos, angle
77
- this.size, .1, 200, 3.14, // emitSize, emitTime, emitRate, emitCone
77
+ this.size, .1, 200, 3.14, // emitSize, emitTime, rate, cone
78
78
  tile(0, 16), // tileIndex, tileSize
79
79
  color1, color2, // colorStartA, colorStartB
80
80
  color1.scale(1,0), color2.scale(1,0), // colorEndA, colorEndB
@@ -110,7 +110,7 @@ export class Ball extends PhysicsObject
110
110
  const color = hsl(0,0,.2);
111
111
  this.trailEffect = new LJS.ParticleEmitter(
112
112
  this.pos, 0, // pos, angle
113
- this.size, 0, 80, 3.14, // emitSize, emitTime, emitRate, emitCone
113
+ this.size, 0, 80, 3.14, // emitSize, emitTime, rate, cone
114
114
  tile(0, 16), // tileIndex, tileSize
115
115
  color, color, // colorStartA, colorStartB
116
116
  color.scale(0), color.scale(0), // colorEndA, colorEndB
@@ -33,11 +33,11 @@ That will make a square of 20×20 engine objects, we can adjust it as we go. It
33
33
 
34
34
  ![LittleJS Screenshot](images/1.png)
35
35
 
36
- It is completely off center and may not look like much yet, but it’s a start!
36
+ It is completely off center and may not look like much yet, but it's a start!
37
37
 
38
38
  ## Improve the Bricks
39
39
 
40
- Let’s make the bricks more rectangular by passing vec2(2,1) to the size parameter of Engine Object. We will need to adjust the loop to add 2 for each x iteration. Also we can use a Vector2 to describe the size of the level.
40
+ Let's make the bricks more rectangular by passing vec2(2,1) to the size parameter of Engine Object. We will need to adjust the loop to add 2 for each x iteration. Also we can use a Vector2 to describe the size of the level.
41
41
 
42
42
  ```javascript
43
43
  // create bricks
@@ -52,7 +52,7 @@ for (let y=0; y<=levelSize.y; y+=1)
52
52
 
53
53
  ## Move the Camera
54
54
 
55
- Let’s move the camera to the center of our level by setting the cameraPos variable to half the level size.
55
+ Let's move the camera to the center of our level by setting the cameraPos variable to half the level size.
56
56
  You can also change the camera zoom by using camaraScale which controls the number of pixels for each world space unit.
57
57
  For this example the camera will just remain stationary, but in other types of games it is very useful!
58
58
 
@@ -60,7 +60,7 @@ For this example the camera will just remain stationary, but in other types of g
60
60
  LJS.setCameraPos(levelSize.scale(.5)); // center camera in level
61
61
  ```
62
62
 
63
- We’ll also start with a blank slate by commenting out the default “Hello World!” text in gameRenderPost.
63
+ We'll also start with a blank slate by commenting out the default “Hello World!” text in gameRenderPost.
64
64
 
65
65
  ```javascript
66
66
  // LJS.drawTextScreen('Hello World!', LJS.mainCanvasSize.scale(.5), 80);
@@ -68,11 +68,11 @@ We’ll also start with a blank slate by commenting out the default “Hello Wor
68
68
 
69
69
  ![LittleJS Screenshot](images/2.png)
70
70
 
71
- It’s getting closer! The camera is now centered and blocks are rectangular.
71
+ It is getting closer! The camera is now centered and blocks are rectangular.
72
72
 
73
73
  ## Modify the Level Size
74
74
 
75
- For this example we will use a fixed size canvas. This will let the drawing canvas always be the same resolution, even if the window size changes. Black space is added on the sides to compensate. It’s a great option for any game that uses a single screen like this.
75
+ For this example we will use a fixed size canvas. This will let the drawing canvas always be the same resolution, even if the window size changes. Black space is added on the sides to compensate. It is a great option for any game that uses a single screen like this.
76
76
 
77
77
  To enable it we will set canvasFixedSize in gameInit to use 720p resolution.
78
78
 
@@ -80,7 +80,7 @@ To enable it we will set canvasFixedSize in gameInit to use 720p resolution.
80
80
  LJS.setCanvasFixedSize(vec2(1280, 720)); // use a 720p fixed size canvas
81
81
  ```
82
82
 
83
- Before we tweak the level size, let’s add this bit of code to gameRender to show the size of the level. This will cause some rects to be drawn each frame before the engine objects. To create color objects we pass in RGB values between 0 and 1.
83
+ Before we tweak the level size, let's add this bit of code to gameRender to show the size of the level. This will cause some rects to be drawn each frame before the engine objects. To create color objects we pass in RGB values between 0 and 1.
84
84
 
85
85
  ```javascript
86
86
  LJS.drawRect(LJS.cameraPos, vec2(100), rgb(.5,.5,.5)); // draw background
@@ -112,7 +112,7 @@ Now that looks almost like a breakout game!
112
112
 
113
113
  ## Create the Player Paddle
114
114
 
115
- For the player’s paddle, let’s create a new class of object that extends the built in EngineObject. This will allow us to control movement with the mouse. In this Paddle class we will just add some new code to the update function which is automatically called each frame by the engine.
115
+ For the player's paddle, let's create a new class of object that extends the built in EngineObject. This will allow us to control movement with the mouse. In this Paddle class we will just add some new code to the update function which is automatically called each frame by the engine.
116
116
 
117
117
  ```javascript
118
118
  class Paddle extends LJS.EngineObject
@@ -132,7 +132,7 @@ new Paddle; // create player's paddle
132
132
 
133
133
  There is now a square paddle that moves along the bottom of the screen.
134
134
 
135
- Let’s make the paddle wider and move it up a smidge by adding a constructor to the Paddle class.
135
+ Let's make the paddle wider and move it up a smidge by adding a constructor to the Paddle class.
136
136
 
137
137
  ```javascript
138
138
  constructor()
@@ -153,7 +153,7 @@ this.pos.x = LJS.clamp(this.pos.x, this.size.x/2, levelSize.x - this.size.x/2);
153
153
  ```
154
154
 
155
155
  ## Create the Ball
156
- The final missing piece is of course that ball that moves around and bounces off of stuff. We will leverage the built in physics solver to handle all the ball’s physics and collision math.
156
+ The final missing piece is of course that ball that moves around and bounces off of stuff. We will leverage the built in physics solver to handle all the ball's physics and collision math.
157
157
 
158
158
  We can create a Ball class the same way we created the paddle. In this case we will also apply some velocity to ball so it starts moving right away.
159
159
 
@@ -169,7 +169,7 @@ class Ball extends LJS.EngineObject
169
169
  }
170
170
  ```
171
171
 
172
- Then we just need to create the ball in gameInit, passing in the camera position for it’s location.
172
+ Then we just need to create the ball in gameInit, passing in the camera position for its location.
173
173
 
174
174
  ```javascript
175
175
  {
@@ -186,13 +186,13 @@ You may have noticed that the ball goes right through the paddle and bricks. To
186
186
  this.setCollision(); // make object collide
187
187
  ```
188
188
 
189
- Now the ball collides but unfortunately also pushes the paddle away, not quite what we want. To prevent the paddle from being moved by physics we will make it use static physics by setting it’s mass to 0 in the paddle constructor.
189
+ Now the ball collides but unfortunately also pushes the paddle away, not quite what we want. To prevent the paddle from being moved by physics we will make it use static physics by setting its mass to 0 in the paddle constructor.
190
190
 
191
191
  ```javascript
192
192
  this.mass = 0; // make object have static physics
193
193
  ```
194
194
 
195
- Getting closer, the paddle is not pushed away but the ball doesn’t bounce either. To make it bounce, we need to set the ball’s restitution which controls how much it will bounce.
195
+ Getting closer, the paddle is not pushed away but the ball doesn't bounce either. To make it bounce, we need to set the ball's restitution which controls how much it will bounce.
196
196
 
197
197
  ```javascript
198
198
  this.restitution = 1; // make object bounce
@@ -202,7 +202,7 @@ this.restitution = 1; // make object bounce
202
202
 
203
203
  It works! The ball now bounces off the paddle, still not the walls or blocks yet though.
204
204
 
205
- Before we continue, let’s make the ball a bit smaller by changing the super call in it’s constructor.
205
+ Before we continue, let's make the ball a bit smaller by changing the super call in its constructor.
206
206
 
207
207
  ```javascript
208
208
  super(pos, vec2(.5)); // set object position and size
@@ -210,7 +210,7 @@ super(pos, vec2(.5)); // set object position and size
210
210
 
211
211
  ## Make the Ball Bounce Off Walls
212
212
 
213
- Next, let’s make the ball bounce when it hits the top or sides of the screen. To do this, we will make a Wall object class that works similar to the paddle but without an update function.
213
+ Next, let's make the ball bounce when it hits the top or sides of the screen. To do this, we will make a Wall object class that works similar to the paddle but without an update function.
214
214
 
215
215
  ```javascript
216
216
  class Wall extends LJS.EngineObject
@@ -275,11 +275,11 @@ if (!ball || ball.pos.y < -1)
275
275
  }
276
276
  ```
277
277
 
278
- Don’t forget to destroy the old ball before creating the new one! Though it doesn’t really matter for this simple example, it would cause the old balls to continue existing while being updated and rendered. That could really slow things in bigger games with thousands of objects.
278
+ Don't forget to destroy the old ball before creating the new one! Though it doesn't really matter for this simple example, it would cause the old balls to continue existing while being updated and rendered. That could really slow things in bigger games with thousands of objects.
279
279
 
280
280
  ## Break the Bricks
281
281
 
282
- Still, the ball doesn’t collide with the bricks much less breaks them and it wouldn’t be breakout without that. So we need to make the brick collide with the ball, just like the walls, so let’s start with Brick class that is the same as Wall.
282
+ Still, the ball doesn't collide with the bricks much less breaks them and it wouldn't be breakout without that. So we need to make the brick collide with the ball, just like the walls, so let's start with Brick class that is the same as Wall.
283
283
 
284
284
  ```javascript
285
285
  class Brick extends LJS.EngineObject
@@ -300,7 +300,7 @@ And we will just change the for loop in gameInit to use the Brick class.
300
300
  const brick = new Brick(vec2(x,y), vec2(2,1)); // create a brick
301
301
  ```
302
302
 
303
- Now the ball will bounce off the bricks, but we want it to break the bricks. To do this we can override the brick’s collideWithObject function which is called by the engine when the ball hits it. Instead of the default collide behavior, we just destroy the brick.
303
+ Now the ball will bounce off the bricks, but we want it to break the bricks. To do this we can override the brick's collideWithObject function which is called by the engine when the ball hits it. Instead of the default collide behavior, we just destroy the brick.
304
304
 
305
305
  ```javascript
306
306
  collideWithObject(o)
@@ -319,7 +319,7 @@ return true; // allow object to collide
319
319
 
320
320
  Success, we have breakout! All the core components are here though there are a few more changes we can make that will greatly increase the playability.
321
321
 
322
- ## Track the Player’s Score
322
+ ## Track the Player's Score
323
323
 
324
324
  We can easily add a score tracker to the game by making a global score variable called score.
325
325
 
@@ -343,7 +343,7 @@ LJS.drawTextScreen('Score ' + score, vec2(LJS.mainCanvasSize.x/2, 70), 50); // s
343
343
 
344
344
  ## Add Sound Effects
345
345
 
346
- So far the game is completely silent. Let’s add sounds using ZzFX. This is a tiny sound effect generator that works in conjunction with LittleJS and can be used on it’s own too.
346
+ So far the game is completely silent. Let's add sounds using ZzFX. This is a tiny sound effect generator that works in conjunction with LittleJS and can be used on its own too.
347
347
 
348
348
  We will start with the ball bounce sound. [You can use the official ZzFX sound designer to create your own sound.](https://killedbyapixel.github.io/ZzFX/) I recommend using the Blip preset for this one.
349
349
 
@@ -364,7 +364,7 @@ collideWithObject(o)
364
364
  return true; // allow object to collide
365
365
  }
366
366
  ```
367
- Let’s also make a brick break sound. You can use the ZzFX sound designer again to create your own sound, for this one try the Hit preset.
367
+ Let's also make a brick break sound. You can use the ZzFX sound designer again to create your own sound, for this one try the Hit preset.
368
368
 
369
369
  Create a global object for this sound the same way we did for the bounce sound. You can replace it with your own generated sound or use mine.
370
370
 
@@ -372,17 +372,17 @@ Create a global object for this sound the same way we did for the bounce sound.
372
372
  const sound_break = new LJS.Sound([,,90,,.01,.03,4,,,,,,,9,50,.2,,.2,.01], 0);
373
373
  ```
374
374
 
375
- Then in the Brick’s collideWithObject function just add some code to play that sound.
375
+ Then in the Brick's collideWithObject function just add some code to play that sound.
376
376
 
377
377
  ```javascript
378
378
  sound_break.play(); // play brick break sound
379
379
  ```
380
380
 
381
- Now you should hear sound as the ball bounces around and breaks bricks! Of course it’s easy to swap out these sounds with other ones, feel free to experiment.
381
+ Now you should hear sound as the ball bounces around and breaks bricks! Of course it is easy to swap out these sounds with other ones, feel free to experiment.
382
382
 
383
383
  ## Add Click To Start Interaction
384
384
 
385
- Let’s improve the gameplay by letting the player click to start, instead of creating the ball right away. This will also ensure that the player has clicked which is requirement for audio to play in most browsers.
385
+ Let's improve the gameplay by letting the player click to start, instead of creating the ball right away. This will also ensure that the player has clicked which is requirement for audio to play in most browsers.
386
386
 
387
387
  We can also add a special sound that plays on startup. Try making an interesting start from the ZzFX sound designer, maybe using the Powerup preset.
388
388
 
@@ -408,9 +408,9 @@ if (!ball && LJS.mouseWasPressed(0)) // if there is no ball and left mouse is pr
408
408
 
409
409
  ## Add Particle Effects
410
410
 
411
- Let’s add some effects when the bricks break. You can use the LittleJS particle system designer to create an explosion effect.
411
+ Let's add some effects when the bricks break. You can use the LittleJS particle system designer to create an explosion effect.
412
412
 
413
- This will be a one shot type of effect, not a continuous emitter so change emitTime to something small like .1 for this example. Now before you play with it too much, let’s test it by pasting in the code it generated into the Brick’s collideWithObject function.
413
+ This will be a one shot type of effect, not a continuous emitter so change emitTime to something small like .1 for this example. Now before you play with it too much, let's test it by pasting in the code it generated into the Brick's collideWithObject function.
414
414
 
415
415
  Make sure you replace the first parameter, vec2(), with this.pos so the effect appears wherever the brick is, otherwise it will spawn at the world origin.
416
416
 
@@ -419,14 +419,14 @@ Make sure you replace the first parameter, vec2(), with this.pos so the effect a
419
419
  new LJS.ParticleEmitter(this.pos, 0, 0, 0.1, 100, 3.14, 0, rgb(1, 1, 1, 1), rgb(1, 1, 1, 1), rgb(1, 1, 1, 0), rgb(1, 1, 1, 0), 0.5, 0.1, 1, 0.1, 0.05, 1, 1, 0, 3.14, 0.1, 0.2, 0, 0, 1);
420
420
  ```
421
421
 
422
- Now you should see this simple particle effect play wherever a brick breaks. You can continue tweaking the parameters to make your own effect or use the one I made which also uses this.color to change the particle’s color so it matches the brick.
422
+ Now you should see this simple particle effect play wherever a brick breaks. You can continue tweaking the parameters to make your own effect or use the one I made which also uses this.color to change the particle's color so it matches the brick.
423
423
 
424
424
  ```javascript
425
425
  // create explosion effect
426
426
  const color = this.color;
427
427
  new LJS.ParticleEmitter(
428
428
  this.pos, 0, // pos, angle
429
- this.size, .1, 200, 3.14,// emitSize, emitTime, emitRate, emitCone
429
+ this.size, .1, 200, 3.14,// emitSize, emitTime, rate, cone
430
430
  undefined, // tileInfo
431
431
  color, color, // colorStartA, colorStartB
432
432
  color.scale(1,0), color.scale(1,0), // colorEndA, colorEndB
@@ -440,7 +440,7 @@ new LJS.ParticleEmitter(
440
440
 
441
441
  ## Improve the Gameplay
442
442
 
443
- Last but certainly not least, let’s make a few small improvements to the gameplay. It would help to give the player some more control of where the ball goes when it hits the paddle. We can do this by adding some code to the ball’s collideWithObject function.
443
+ Last but certainly not least, let's make a few small improvements to the gameplay. It would help to give the player some more control of where the ball goes when it hits the paddle. We can do this by adding some code to the ball's collideWithObject function.
444
444
 
445
445
  This code will use the rotate function to modify the bounce angle based on the distance from the center of the paddle.
446
446
 
@@ -484,7 +484,7 @@ And assign that paddle object when it is created in gameInit.
484
484
  paddle = new Paddle; // create player's paddle
485
485
  ```
486
486
 
487
- While we are working in the ball’s collideWithObject function, let’s also make it speed up a little bit each time it bounces.
487
+ While we are working in the ball's collideWithObject function, let's also make it speed up a little bit each time it bounces.
488
488
 
489
489
  ```javascript
490
490
  // speed up the ball
@@ -494,13 +494,13 @@ this.velocity = this.velocity.normalize(speed);
494
494
 
495
495
  ## Improve the Sound
496
496
 
497
- Lets make the sound a bit more interesting by using positional audio. This lets the volume and panning be adjusted based on the sound’s on screen location. To enable this, just pass a position to the sound’s play function.
497
+ Let's make the sound a bit more interesting by using positional audio. This lets the volume and panning be adjusted based on the sound's on screen location. To enable this, just pass a position to the sound's play function.
498
498
 
499
499
  ```javascript
500
500
  sound_break.play(this.pos); // play brick break sound
501
501
  ```
502
502
 
503
- For the ball bounce sound, let’s also tweak the pitch by how fast the ball is moving. For the sound play function, the second parameter takes a scalar for volume and the third parameter takes a scalar for pitch.
503
+ For the ball bounce sound, let's also tweak the pitch by how fast the ball is moving. For the sound play function, the second parameter takes a scalar for volume and the third parameter takes a scalar for pitch.
504
504
 
505
505
  ```javascript
506
506
  sound_bounce.play(this.pos, 1, speed); // play bounce sound with pitch scaled by speed
@@ -117,7 +117,7 @@ class Brick extends LJS.EngineObject
117
117
  const color = this.color;
118
118
  new LJS.ParticleEmitter(
119
119
  this.pos, 0, // pos, angle
120
- this.size, .1, 200, 3.14,// emitSize, emitTime, emitRate, emitCone
120
+ this.size, .1, 200, 3.14,// emitSize, emitTime, rate, cone
121
121
  undefined, // tileInfo
122
122
  color, color, // colorStartA, colorStartB
123
123
  color.scale(1,0), color.scale(1,0), // colorEndA, colorEndB
@@ -65,16 +65,16 @@ function gameInit()
65
65
  // create particle emitter
66
66
  particleEmitter = new ParticleEmitter(
67
67
  vec2(16,9), 0, // emitPos, emitAngle
68
- 0, 0, 500, PI, // emitSize, emitTime, emitRate, emitCone
68
+ 0, 0, 500, PI, // emitSize, emitTime, rate, cone
69
69
  tile(0, 16), // tileIndex, tileSize
70
70
  hsl(1,1,1), hsl(0,0,0), // colorStartA, colorStartB
71
71
  hsl(0,0,0,0), hsl(0,0,0,0), // colorEndA, colorEndB
72
- 2, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
72
+ 1, .2, .2, .1, .05, // time, sizeStart, sizeEnd, speed, angleSpeed
73
73
  .99, 1, 1, PI, // damping, angleDamping, gravityScale, cone
74
74
  .05, .5, true, true // fadeRate, randomness, collide, additive
75
75
  );
76
76
  particleEmitter.restitution = .3; // bounce when it collides
77
- particleEmitter.trailScale = 2; // stretch in direction of motion
77
+ particleEmitter.trailScale = 2; // stretch as it moves
78
78
  }
79
79
 
80
80
  ///////////////////////////////////////////////////////////////////////////////
@@ -86,7 +86,7 @@ function gameUpdate()
86
86
  sound_click.play(mousePos);
87
87
 
88
88
  // change particle color and set to fade out
89
- particleEmitter.colorStartA = hsl();
89
+ particleEmitter.colorStartA = randColor();
90
90
  particleEmitter.colorStartB = randColor();
91
91
  particleEmitter.colorEndA = particleEmitter.colorStartA.scale(1,0);
92
92
  particleEmitter.colorEndB = particleEmitter.colorStartB.scale(1,0);
@@ -7,7 +7,7 @@
7
7
  </head><body>
8
8
 
9
9
  <!-- LittleJS Engine -->
10
- <script src=../../dist/littlejs.js?1136></script>
10
+ <script src=../../dist/littlejs.js?11417></script>
11
11
 
12
12
  <!-- Add your game scripts here -->
13
- <script src=game.js?1136></script>
13
+ <script src=game.js?11417></script>
@@ -1,12 +1,5 @@
1
1
  {
2
2
  "name": "littlejsgame",
3
3
  "version": "1.0.0",
4
- "main": "electron.js",
5
- "scripts": {
6
- "build": "node build.js"
7
- },
8
- "devDependencies": {
9
- "electron": "^38.2.0",
10
- "electron-packager": "^17.1.2"
11
- }
4
+ "main": "electron.js"
12
5
  }
@@ -4,6 +4,17 @@
4
4
  <link rel=stylesheet href=style.css?9>
5
5
  <meta charset=utf-8>
6
6
  <meta name='viewport' content='width=device-width, initial-scale=1.0'>
7
+ <!-- meta tags for social media -->
8
+ <meta name='twitter:title' content='LittleJS Example Browser'>
9
+ <meta property='og:title' content='LittleJS Example Browser'>
10
+ <meta name='description' content='Tiny fast HTML5 game engine!'>
11
+ <meta name='twitter:description' content='Tiny fast HTML5 game engine!'>
12
+ <meta property='og:description' content='Tiny fast HTML5 game engine!'>
13
+ <meta name='twitter:image' content='https://3d2k.com/images/LittleJSExamples.png'>
14
+ <meta property='og:image' content='https://3d2k.com/images/LittleJSExamples.png'>
15
+ <meta name='keywords' content='JavaScript, HTML5, Game, Engine'>
16
+ <meta name='twitter:creator' content='@KilledByAPixel'>
17
+ <meta name='twitter:card' content='summary_large_image'>
7
18
  </head><body>
8
19
  <div id=container1>
9
20
  <div id=container3>
@@ -21,8 +32,7 @@ Theme:
21
32
  <option value=20px>L</option>
22
33
  <option value=24px>XL</option>
23
34
  </select>
24
- <span class=nowrap><input type=checkbox id=checkboxLiveEdit checked>Live Edit</span>
25
- <span class=nowrap><input type=checkbox id=checkboxJumpToErrors>Jump to Errors</span>
35
+ <span class=nowrap><input type=checkbox id=checkboxLiveEdit onchange=writeSaveData() checked>Live Edit</span>
26
36
  </div>
27
37
  <div id=divTextareas>
28
38
  <textarea id=textareaCode oninput=codeInput() spellcheck=false></textarea>
@@ -75,7 +85,8 @@ class ExampleInfo
75
85
  const exampleList =
76
86
  [
77
87
  new ExampleInfo('--- SHORT EXAMPLES ---'),
78
- new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example', false, 'beginner, gradient, text, tiles'),
88
+ new ExampleInfo('Hello World', 'helloWorld.js', 'Simple starter example', false, 'beginner, gradient, text, tiles'),
89
+ new ExampleInfo('Empty', 'empty.js', 'Empty example template', false, 'beginner, text'),
79
90
  new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation', false, 'sprites, loading, tiles'),
80
91
  new ExampleInfo('Animation', 'animation.js', 'Sprite animation system', false, 'frames, loop, tiles, sprites'),
81
92
  new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes and primitives', false, 'circle, ellipses, rectangles, polygon, lines'),
@@ -87,13 +98,16 @@ const exampleList =
87
98
  new ExampleInfo('Particles', 'particles.js', 'Particle system', false, 'effects, emitter, physics, fire, smoke'),
88
99
  new ExampleInfo('Timers', 'timers.js', 'Timer objects and UI', false, 'delay, interval, slider'),
89
100
  new ExampleInfo('Sound Effects', 'sound.js', 'ZzFX sound effect generator', false, 'audio, volume, ui'),
90
- new ExampleInfo('Music', 'music.js', 'Music playback and controls', false, 'audio, streaming, volume, ui'),
101
+ new ExampleInfo('Music', 'music.js', 'Basic music load, play, pause, and stop', false, 'music, sound, audio, streaming, volume, ui'),
91
102
  new ExampleInfo('Font Image', 'systemFont.js', 'Bitmap font system with built-in system font', false, 'text, characters'),
92
103
  new ExampleInfo('Parallax', 'parallax.js', 'Parallax scrolling mountains', false, 'generative, canvas, background'),
93
104
  new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation', false, 'generative, level, tiles, map, grid'),
94
105
  new ExampleInfo('Medals', 'medals.js', 'Achievement system', false, 'unlock, progress, newgrounds'),
95
- new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard', false, 'music, notes, ui, instrument'),
96
106
  new ExampleInfo('Clock', 'clock.js', 'Animated analog clock', false, 'time, rotation, lines, rectangles'),
107
+ new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard', false, 'music, sound, audio, notes, ui, instrument'),
108
+ new ExampleInfo('Step Sequencer', 'sequencer.js', 'Simple music loop creation tool', false, 'music, sound, audio, notes, ui, instrument'),
109
+ new ExampleInfo('Music Player', 'musicPlayer.js', 'Advanced music player with audio seeking and drag and drop', false, 'music, sound, audio, streaming, volume, ui'),
110
+ new ExampleInfo('WebGL Shader', 'shader.js', 'Full canvas webgl shader', false, 'webgl, visual, effect'),
97
111
  new ExampleInfo('--- MINI GAMES ---'),
98
112
  new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing', false, 'objects, collision'),
99
113
  new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view', false, 'objects, gravity, level, tiles, camera'),
@@ -138,7 +152,7 @@ function initExampleBrowser()
138
152
  // set the selected example from the URL parameters
139
153
  const urlParams = new URLSearchParams(window.location.search);
140
154
  const selectedExample = urlParams.get('example') || '';
141
- let exampleIndex = exampleList.findIndex((example) => example.filename === selectedExample);
155
+ let exampleIndex = exampleList.findIndex((example) => example.name === selectedExample);
142
156
  if (exampleIndex < 0)
143
157
  exampleIndex = 1;
144
158
  selectExample.selectedIndex = exampleIndex;
@@ -233,11 +247,11 @@ function setExample()
233
247
  exampleInfo.innerText = example.text;
234
248
  const filename = 'examples/' + example.filename;
235
249
  exampleLink.href = 'https://github.com/KilledByAPixel/LittleJS/tree/main/' + filename;
236
- exampleLink.innerText = 'View code on GitHub';
250
+ exampleLink.innerText = 'View on GitHub: ' + example.filename;
237
251
 
238
252
  // update URL parameter
239
253
  const url = new URL(window.location);
240
- url.searchParams.set('example', example.filename);
254
+ url.searchParams.set('example', example.name);
241
255
  window.history.replaceState({}, '', url);
242
256
 
243
257
  loadFile(example.filename, example.largeExample);
@@ -522,13 +536,13 @@ function setCode(code, filename)
522
536
  iframeContent.engineInit(iframeContent.gameInit, iframeContent.gameUpdate, iframeContent.gameUpdatePost, iframeContent.gameRender, iframeContent.gameRenderPost, ['tiles.png?'+Date.now()])
523
537
  .catch(error =>
524
538
  {
525
- const errorLine = getErrorLine(error.stack);
539
+ const errorLine = error ? getErrorLine(error.stack) : -1;
526
540
  if (errorLine >= 0)
527
541
  setErrorLine(errorLine);
528
542
  let message = error;
529
- if (error.message)
543
+ if (error && error.message)
530
544
  message = error.message;
531
- if (error.stack)
545
+ if (error && error.stack)
532
546
  message += '\n' + error.stack;
533
547
  setErrorMessage(message);
534
548
  throw error;
@@ -613,35 +627,6 @@ function setErrorLine(lineNumber)
613
627
  clearErrorLine();
614
628
  --lineNumber; // codeMirror uses 0-based line numbers
615
629
  errorLineMarker = codeMirror.getDoc().addLineClass(lineNumber, 'background', 'error-line');
616
- if (checkboxJumpToErrors.checked)
617
- {
618
- codeMirror.scrollIntoView({line: lineNumber, ch: 0});
619
- codeMirror.focus();
620
- }
621
- }
622
- else if (textareaCode && checkboxJumpToErrors.checked)
623
- {
624
- // for textarea, calculate character position and set cursor
625
- const lines = textareaCode.value.split('\n');
626
- let charPos = 0;
627
- for (let i = 0; i < lineNumber - 1 && i < lines.length; i++)
628
- {
629
- charPos += lines[i].length + 1; // +1 for newline character
630
- }
631
- textareaCode.setSelectionRange(charPos, charPos);
632
- if (checkboxJumpToErrors.checked)
633
- {
634
- const line = textareaCode.value.split('\n');
635
- lineNumber = Math.min(Math.max(0, lineNumber-1), lines.length-1);
636
-
637
- // select error line in textarea
638
- let start = 0;
639
- for (let i = 0; i < lineNumber; i++)
640
- start += lines[i].length + 1;
641
- textareaCode.selectionStart = start;
642
- textareaCode.selectionEnd = start + lines[lineNumber].length;
643
- textareaCode.focus();
644
- }
645
630
  }
646
631
  }
647
632
 
@@ -666,13 +651,41 @@ textareaConsole.addEventListener('scroll', onScrollConsole);
666
651
 
667
652
  ///////////////////////////////////////////////////////////////////////////////
668
653
  // load saved preferences from localStorage
669
- const defaultTheme = 'littlejs';
670
- const defaultFontSize = '16px';
671
- const savePrefix = 'LittleJSExamples_';
672
- const savedTheme = localStorage.getItem(savePrefix+'theme') || defaultTheme;
673
- const savedFontSize = localStorage.getItem(savePrefix+'fontSize') || defaultFontSize;
674
- selectFontSize.value = savedFontSize;
675
- textareaCode.style.fontSize = savedFontSize;
654
+ const saveName = 'LittleJSExamples';
655
+ let savedTheme;
656
+
657
+ function readSaveData()
658
+ {
659
+ // load saved preferences
660
+ const defaultTheme = 'littlejs';
661
+ const defaultFontSize = '16px';
662
+ const saveDataJSON = localStorage.getItem(saveName);
663
+ const saveData = saveDataJSON ? JSON.parse(saveDataJSON) : {};
664
+ selectTheme.value = savedTheme = saveData.theme ?? defaultTheme;
665
+ selectFontSize.value = saveData.fontSize ?? defaultFontSize;
666
+ }
667
+ readSaveData();
668
+
669
+ function writeSaveData()
670
+ {
671
+ // Save preferences to localStorage
672
+ const theme = selectTheme.value;
673
+ const fontSize = selectFontSize.value;
674
+ const saveData =
675
+ {
676
+ theme,
677
+ fontSize
678
+ };
679
+ const saveDataJSON = JSON.stringify(saveData);
680
+ localStorage.setItem(saveName, saveDataJSON);
681
+ }
682
+
683
+ function resetDefaults()
684
+ {
685
+ localStorage.removeItem(saveName);
686
+ readSaveData();
687
+ loadTheme();
688
+ }
676
689
 
677
690
  ///////////////////////////////////////////////////////////////////////////////
678
691
  // setup code mirror
@@ -750,20 +763,18 @@ function addCodeMirrorElement(filename, type, rel)
750
763
 
751
764
  function loadTheme()
752
765
  {
753
- const theme = selectTheme.value;
754
- const size = selectFontSize.value;
755
-
756
- // Save preferences to localStorage
757
- localStorage.setItem(savePrefix+'theme', theme);
758
- localStorage.setItem(savePrefix+'fontSize', size);
759
-
760
766
  // Apply the theme and font size
761
- textareaCode.style.fontSize = size;
767
+ const theme = selectTheme.value;
768
+ const fontSize = selectFontSize.value;
769
+ textareaCode.style.fontSize = fontSize;
762
770
  if (codeMirror)
763
771
  {
764
- codeMirror.getWrapperElement().style.fontSize = size;
772
+ codeMirror.getWrapperElement().style.fontSize = fontSize;
765
773
  codeMirror.setOption('theme', theme);
766
774
  }
775
+
776
+ // Save preferences to localStorage
777
+ writeSaveData();
767
778
  }
768
779
 
769
780
  ///////////////////////////////////////////////////////////////////////////////