littlejsengine 1.14.5 → 1.14.8

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 (45) hide show
  1. package/dist/littlejs.d.ts +53 -19
  2. package/dist/littlejs.esm.js +183 -106
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +179 -104
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +178 -103
  7. package/examples/box2d/game.js +1 -3
  8. package/examples/box2d/gameObjects.js +2 -2
  9. package/examples/htmlMenu/game.js +1 -1
  10. package/examples/index.html +92 -51
  11. package/examples/puzzle/game.js +1 -2
  12. package/examples/shorts/animation.js +2 -2
  13. package/examples/shorts/box2d.js +1 -0
  14. package/examples/shorts/box2dCar.js +1 -0
  15. package/examples/shorts/flappyGame.js +2 -1
  16. package/examples/shorts/helloWorld.js +2 -2
  17. package/examples/shorts/hillGlideGame.js +6 -3
  18. package/examples/shorts/maze.js +1 -0
  19. package/examples/shorts/medals.js +3 -0
  20. package/examples/shorts/music.js +3 -5
  21. package/examples/shorts/piano.js +24 -25
  22. package/examples/shorts/platformer.js +1 -0
  23. package/examples/shorts/shapes.js +6 -6
  24. package/examples/shorts/sound.js +21 -32
  25. package/examples/shorts/spriteAtlas.js +1 -2
  26. package/examples/shorts/tileLayer.js +1 -0
  27. package/examples/shorts/tiltedView.js +1 -1
  28. package/examples/shorts/timers.js +34 -31
  29. package/examples/shorts/topDown.js +2 -1
  30. package/examples/shorts/uiSystem.js +15 -10
  31. package/examples/stress/index.html +1 -1
  32. package/examples/uiSystem/game.js +1 -1
  33. package/package.json +1 -1
  34. package/plugins/uiSystem.js +83 -58
  35. package/reference.md +1 -0
  36. package/src/engine.js +23 -6
  37. package/src/engineDebug.js +1 -1
  38. package/src/engineDraw.js +19 -12
  39. package/src/engineExport.js +4 -2
  40. package/src/engineInput.js +7 -0
  41. package/src/engineObject.js +4 -4
  42. package/src/engineSettings.js +11 -1
  43. package/src/engineTileLayer.js +2 -2
  44. package/src/engineUtilities.js +9 -2
  45. package/src/engineWebGL.js +20 -18
@@ -56,6 +56,7 @@ 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));
59
60
 
60
61
  // create a table of all sprites
61
62
  const gameTile = (i)=> LJS.tile(i, 496, 0, 8);
@@ -136,9 +137,6 @@ function gameUpdatePost()
136
137
  ///////////////////////////////////////////////////////////////////////////////
137
138
  function gameRender()
138
139
  {
139
- // draw a grey square in the background
140
- LJS.drawRect(vec2(20,8), vec2(100), hsl(0,0,.8));
141
-
142
140
  if (Scenes.scene == 5)
143
141
  {
144
142
  // raycast test
@@ -18,7 +18,7 @@ const density = 1;
18
18
  const friction = .5;
19
19
  const restitution = .2;
20
20
 
21
- export function spawnBox(pos, size=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDynamic, applyTexture=true, angle=0)
21
+ export function spawnBox(pos, size=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDynamic, applyTexture=false, angle=0)
22
22
  {
23
23
  size = typeof size === 'number' ? vec2(size) : size; // square
24
24
  const o = new LJS.Box2dObject(pos, size, applyTexture && Game.spriteAtlas.squareOutline, angle, color, type);
@@ -27,7 +27,7 @@ export function spawnBox(pos, size=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDy
27
27
  return o;
28
28
  }
29
29
 
30
- export function spawnCircle(pos, diameter=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDynamic, applyTexture=true, angle=0)
30
+ export function spawnCircle(pos, diameter=1, color=LJS.WHITE, type=LJS.box2d.bodyTypeDynamic, applyTexture=false, angle=0)
31
31
  {
32
32
  const size = vec2(diameter);
33
33
  const o = new LJS.Box2dObject(pos, size, applyTexture && Game.spriteAtlas.circleOutline, angle, color, type);
@@ -32,6 +32,7 @@ function gameInit()
32
32
  button_exitMenu.onclick = function() { setMenuVisible(false); }
33
33
  input_test.onchange = function() { alert('New text: ' + this.value); }
34
34
  input_rangeTest.onchange = function() { alert('New value: ' + this.value); }
35
+ LJS.setCanvasClearColor(hsl(0,0,.2));
35
36
 
36
37
  // show menu for demo
37
38
  setMenuVisible(true);
@@ -60,7 +61,6 @@ function gameUpdatePost()
60
61
  function gameRender()
61
62
  {
62
63
  // test game rendering
63
- LJS.drawRect(vec2(), vec2(1e3), hsl(0,0,.2));
64
64
  for (let i=0; i<1e3; ++i)
65
65
  {
66
66
  const time = LJS.time;
@@ -61,58 +61,62 @@ class ExampleInfo
61
61
  this.filename = filename;
62
62
  this.description = description;
63
63
  this.largeExample = largeExample;
64
+ this.isHeading = !filename;
65
+ this.text = this.name;
66
+ if (this.description)
67
+ this.text += ' - ' + this.description;
64
68
  }
65
69
  }
66
70
 
67
71
  const exampleList =
68
72
  [
69
73
  new ExampleInfo('--- SHORT EXAMPLES ---'),
70
- new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example - [basic, beginner]'),
71
- new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes - [graphics, primitives, basic]'),
72
- new ExampleInfo('Colors', 'colors.js', 'Color object and HSL usage - [graphics, color, basic]'),
73
- new ExampleInfo('Blending', 'blending.js', 'Blending modes and transparency - [graphics, effects]'),
74
- new ExampleInfo('Timers', 'timers.js', 'Timer object and timing functions - [utilities, time]'),
75
- new ExampleInfo('Texture', 'texture.js', 'Display and manipulate textures - [graphics, sprites]'),
76
- new ExampleInfo('Particles', 'particles.js', 'Particle system basics - [effects, particles]'),
77
- new ExampleInfo('Sound Effects', 'sound.js', 'Example sound effects with ZzFX - [audio, sound]'),
78
- new ExampleInfo('Music', 'music.js', 'Example sound loading, play, pause, seek, and stop - [audio, music]'),
79
- new ExampleInfo('System Font', 'systemFont.js', 'Built-in bitmap font rendering - [text, font]'),
80
- new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering - [graphics, sprites]'),
81
- new ExampleInfo('Animation', 'animation.js', 'Sprite animation techniques - [animation, sprites]'),
82
- new ExampleInfo('Clock', 'clock.js', 'Real-time clock display - [time, ui, utilities]'),
83
- new ExampleInfo('Medals', 'medals.js', 'Achievement system demo - [ui, achievements]'),
84
- new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system - [tiles, levels]'),
85
- new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard - [audio, input, ui]'),
86
- new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation - [algorithm, generation]'),
87
- new ExampleInfo('Starfield', 'starfield.js', 'Animated starfield - [effects, space]'),
88
- new ExampleInfo('Parallax', 'parallax.js', 'Parallax mountains - [graphics, effects]'),
74
+ new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example'),
75
+ new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation'),
76
+ new ExampleInfo('Animation', 'animation.js', 'Sprite animation system'),
77
+ new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes and primitives'),
78
+ new ExampleInfo('Colors', 'colors.js', 'Color manipulation and HSL'),
79
+ new ExampleInfo('Starfield', 'starfield.js', 'Animated parallax starfield'),
80
+ new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering'),
81
+ new ExampleInfo('Blending', 'blending.js', 'Additive blending and transparency'),
82
+ new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system'),
83
+ new ExampleInfo('Particles', 'particles.js', 'Particle system'),
84
+ new ExampleInfo('Timers', 'timers.js', 'Timer objects and UI'),
85
+ new ExampleInfo('Sound Effects', 'sound.js', 'ZzFX sound effect generator'),
86
+ new ExampleInfo('Music', 'music.js', 'Music playback and controls'),
87
+ new ExampleInfo('Font Image', 'systemFont.js', 'Bitmap font system with built-in system font'),
88
+ new ExampleInfo('Parallax', 'parallax.js', 'Parallax scrolling mountains'),
89
+ new ExampleInfo('Maze Generator', 'maze.js', 'Procedural maze generation'),
90
+ new ExampleInfo('Medals', 'medals.js', 'Achievement system'),
91
+ new ExampleInfo('Piano', 'piano.js', 'Interactive piano keyboard'),
92
+ new ExampleInfo('Clock', 'clock.js', 'Animated analog clock'),
89
93
  new ExampleInfo('--- MINI GAMES ---'),
90
- new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run platformer - [game, platformer, physics]'),
91
- new ExampleInfo('Top Down Game', 'topDown.js', 'Adventure with collision - [game, topdown, rpg]'),
92
- new ExampleInfo('Tilted View', 'tiltedView.js', 'Pseudo 3D tilted perspective - [3d, camera, perspective]'),
93
- new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship with parallax starfield - [game, space, shooter]'),
94
- new ExampleInfo('Lander Game', 'landerGame.js', 'Lunar lander physics game - [game, physics, thrust]'),
95
- new ExampleInfo('Flappy Game', 'flappyGame.js', 'Flappy bird clone - [game, arcade, flying]'),
96
- new ExampleInfo('Hill Glide Game', 'hillGlideGame.js', 'Tiny wings style glider - [game, physics, flying]'),
97
- new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball game - [game, classic, arcade]'),
98
- new ExampleInfo('Sliding Puzzle', 'slidingPuzzle.js', 'Tile sliding puzzle game - [game, puzzle, logic]'),
99
- new ExampleInfo('Raycasting Game', 'raycasting.js', 'Pseudo 3D raycasting demo - [3d, raycasting, wolfenstein]'),
94
+ new ExampleInfo('Pong Game', 'pongGame.js', 'Classic paddle ball bouncing'),
95
+ new ExampleInfo('Platformer Game', 'platformer.js', 'Jump and run side view'),
96
+ new ExampleInfo('Top Down Game', 'topDown.js', 'Top-down style camera'),
97
+ new ExampleInfo('Tilted View Game', 'tiltedView.js', 'Pseudo-3D oblique view'),
98
+ new ExampleInfo('Flappy Game', 'flappyGame.js', 'Flappy bird style game'),
99
+ new ExampleInfo('Lander Game', 'landerGame.js', 'Lunar lander style game'),
100
+ new ExampleInfo('Hill Glide Game', 'hillGlideGame.js', 'Tiny wings style sliding game'),
101
+ new ExampleInfo('Sliding Puzzle', 'slidingPuzzle.js', '15 tile sliding puzzle'),
102
+ new ExampleInfo('Space Game', 'spaceGame.js', 'Spaceship shooter with parallax'),
103
+ new ExampleInfo('Raycasting 3D Game', 'raycasting.js', 'Wolfenstein-style raycasting'),
100
104
  new ExampleInfo('--- PLUGINS ---'),
101
- new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin demo - [physics, box2d, plugin]'),
102
- new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D - [physics, box2d, vehicle]'),
103
- new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters - [graphics, shaders, effects]'),
104
- new ExampleInfo('UI System Plugin', 'uiSystem.js', 'UI elements and widgets - [ui, plugin, interface]'),
105
- new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panel rendering - [ui, graphics, scaling]'),
105
+ new ExampleInfo('Box2d Demo', 'box2d.js', 'Box2D physics plugin'),
106
+ new ExampleInfo('Box2d Car', 'box2dCar.js', 'Drivable car with Box2D physics'),
107
+ new ExampleInfo('Post Processing', 'postProcess.js', 'Shader effects and filters'),
108
+ new ExampleInfo('UI System Plugin', 'uiSystem.js', 'Buttons, sliders, and checkboxes'),
109
+ new ExampleInfo('Nine Slice', 'nineSlice.js', 'Scalable UI panels'),
106
110
  new ExampleInfo('--- FULL EXAMPLES ---'),
107
- new ExampleInfo('Starter', 'starter', 'Clean project template - [template, beginner]', true),
108
- new ExampleInfo('Breakout Tutorial', 'breakoutTutorial', 'Step-by-step breakout guide - [tutorial, breakout]', true),
109
- new ExampleInfo('Breakout Game', 'breakout', 'Complete breakout with effects - [game, breakout, complete]', true),
110
- new ExampleInfo('Platforming Game', 'platformer', 'Full platformer with levels - [game, platformer, complete]', true),
111
- new ExampleInfo('Puzzle Game', 'puzzle', 'Match-3 puzzle with HD graphics - [game, puzzle, match3]', true),
112
- new ExampleInfo('Stress Test', 'stress', 'Performance test with music - [performance, test, music]', true),
113
- new ExampleInfo('Box2D Plugin', 'box2d', 'Complete Box2D physics demo - [physics, box2d, complete]', true),
114
- new ExampleInfo('HTML Menus', 'htmlMenu', 'HTML integration example - [html, ui, integration]', true),
115
- new ExampleInfo('UI System Plugin', 'uiSystem', 'Complete UI system demo - [ui, plugin, complete]', true),
111
+ new ExampleInfo('Starter', 'starter', 'Clean project template', true),
112
+ new ExampleInfo('Breakout Tutorial', 'breakoutTutorial', 'Step-by-step breakout tutorial', true),
113
+ new ExampleInfo('Breakout Game', 'breakout', 'Complete breakout game', true),
114
+ new ExampleInfo('Platforming Game', 'platformer', 'Full platformer with levels', true),
115
+ new ExampleInfo('Puzzle Game', 'puzzle', 'Match-3 puzzle game', true),
116
+ new ExampleInfo('Stress Test', 'stress', 'Performance and music test', true),
117
+ new ExampleInfo('Box2D Plugin', 'box2d', 'Full Box2D physics demo', true),
118
+ new ExampleInfo('HTML Menus', 'htmlMenu', 'HTML UI integration', true),
119
+ new ExampleInfo('UI System Plugin', 'uiSystem', 'Complete UI system demo', true),
116
120
  ];
117
121
 
118
122
  ///////////////////////////////////////////////////////////////////////////////
@@ -222,7 +226,7 @@ function setExample()
222
226
 
223
227
  // load the example
224
228
  const example = exampleList[exampleIndex];
225
- exampleInfo.innerText = example.name + (example.description ? ' - ' + example.description : '');
229
+ exampleInfo.innerText = example.text;
226
230
  const filename = 'examples/' + example.filename;
227
231
  exampleLink.href = 'https://github.com/KilledByAPixel/LittleJS/tree/main/' + filename;
228
232
  exampleLink.innerText = 'View code on GitHub';
@@ -300,16 +304,48 @@ function filterExamples(reset=0)
300
304
 
301
305
  // filter and add matching examples
302
306
  const searchTerm = inputSearch.value.toLowerCase().trim();
307
+
308
+ // first pass: find which examples match
309
+ const matchingIndices = [];
303
310
  for (let i = 0; i < exampleList.length; i++)
304
311
  {
305
- // check if search term matches name or description
306
312
  const example = exampleList[i];
307
- const text = example.name + (example.description ? ` - ${example.description}` : '');
313
+ if (!example.filename)
314
+ continue;
315
+
316
+ // Check if search term matches name or description
308
317
  if (example.name.toLowerCase().includes(searchTerm) ||
309
318
  example.description.toLowerCase().includes(searchTerm))
319
+ matchingIndices.push(i);
320
+ }
321
+
322
+ // second pass: add headings and matching examples
323
+ let lastHeadingIndex = -1;
324
+ for (let i = 0; i < exampleList.length; i++)
325
+ {
326
+ const example = exampleList[i];
327
+ if (example.isHeading)
328
+ {
329
+ // check if there are any matches between this and the next
330
+ for (let j = i + 1; j < exampleList.length; j++)
331
+ {
332
+ if (exampleList[j].isHeading)
333
+ break; // hit next heading
334
+ if (matchingIndices.includes(j))
335
+ {
336
+ // only add heading if there are matches under it
337
+ lastHeadingIndex = i;
338
+ const o = new Option(example.text);
339
+ o.disabled = true;
340
+ selectExample.add(o);
341
+ break;
342
+ }
343
+ }
344
+ }
345
+ else if (matchingIndices.includes(i))
310
346
  {
311
- const o = new Option(text);
312
- o.disabled = !example.filename;
347
+ // add matching example
348
+ const o = new Option(example.text);
313
349
  o.originalIndex = i;
314
350
  selectExample.add(o);
315
351
  }
@@ -317,7 +353,7 @@ function filterExamples(reset=0)
317
353
 
318
354
  if (!selectExample.options.length)
319
355
  {
320
- // show a message if no matches
356
+ // show a message if no matches were found
321
357
  const o = new Option(`No examples found matching '${searchTerm}'`);
322
358
  o.disabled = true;
323
359
  selectExample.add(o);
@@ -387,7 +423,7 @@ function setCode(code, filename)
387
423
  {
388
424
  // try to extract line number from stack trace
389
425
  // look for <anonymous> or injectedScript to find user code
390
- const anonymousMatch = stack.match(/(<anonymous>|injectedScript):(\d+)/);
426
+ const anonymousMatch = stack?.match(/(<anonymous>|injectedScript):(\d+)/);
391
427
  return anonymousMatch ? parseInt(anonymousMatch[2]) : -1;
392
428
  }
393
429
  iframeContent.onerror = (message, source, lineno, colno, error) =>
@@ -484,7 +520,12 @@ function setCode(code, filename)
484
520
  const errorLine = getErrorLine(error.stack);
485
521
  if (errorLine >= 0)
486
522
  setErrorLine(errorLine);
487
- setErrorMessage(error.message + '\n' + error.stack);
523
+ let message = error;
524
+ if (error.message)
525
+ message = error.message;
526
+ if (error.stack)
527
+ message += '\n' + error.stack;
528
+ setErrorMessage(message);
488
529
  throw error;
489
530
  })
490
531
  .then(()=>
@@ -69,8 +69,7 @@ function gameInit()
69
69
  {
70
70
  // setup canvas
71
71
  LJS.setCanvasFixedSize(vec2(1920, 1080)); // 1080p
72
- LJS.mainCanvas.style.background = backgroundColor;
73
-
72
+ LJS.setCanvasClearColor(backgroundColor);
74
73
  // load high score
75
74
  bestScore = localStorage[highScoreKey] || 0;
76
75
 
@@ -1,7 +1,7 @@
1
+ canvasClearColor = GRAY;
2
+
1
3
  function gameRender()
2
4
  {
3
- drawRect(vec2(0,0), vec2(100), GRAY); // draw background
4
-
5
5
  {
6
6
  // animate using multiple frames
7
7
  const pos = vec2(-5, 2*abs(Math.sin(time*2*PI)));
@@ -4,6 +4,7 @@ async function gameInit()
4
4
  await box2dInit();
5
5
  mouseJoint = 0;
6
6
  gravity.y = -50;
7
+ canvasClearColor = hsl(0,0,.9);
7
8
 
8
9
  // create ground object
9
10
  groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0, GRAY, box2d.bodyTypeStatic);
@@ -6,6 +6,7 @@ async function gameInit()
6
6
  groundObject = new Box2dObject(vec2(-8), vec2(), 0, 0, GRAY, box2d.bodyTypeStatic);
7
7
  groundObject.addBox(vec2(100,6));
8
8
  new CarObject(vec2(0,-2));
9
+ canvasClearColor = hsl(0,0,.9);
9
10
  }
10
11
 
11
12
  class CarObject extends Box2dObject
@@ -2,7 +2,7 @@ class Wall extends EngineObject
2
2
  {
3
3
  constructor(pos, size)
4
4
  {
5
- super(pos, size, 0, 0, hsl(.4,.5,.4));
5
+ super(pos, size, 0, 0, hsl(.3,.5,.3));
6
6
  this.setCollision(); // make object collide
7
7
  }
8
8
  update()
@@ -50,4 +50,5 @@ function gameInit()
50
50
  new Wall(vec2(14 + i*spacing,y), vec2(3,h));
51
51
  }
52
52
  new Player(vec2(-7,6));
53
+ canvasClearColor = hsl(.55,1,.8);
53
54
  }
@@ -1,7 +1,7 @@
1
1
  function gameRender()
2
2
  {
3
- // draw background rect
4
- drawRect(cameraPos, vec2(50), hsl(0,0,.2));
3
+ // draw background gradient
4
+ drawRectGradient(cameraPos, getCameraSize(), BLACK, WHITE);
5
5
 
6
6
  // draw text to the overlay canvas
7
7
  drawTextOverlay('Hello World', vec2(0,3), 3);
@@ -40,17 +40,20 @@ function gameInit()
40
40
  {
41
41
  gravity.y = -.01;
42
42
  new Player(vec2(0,5));
43
+ canvasClearColor = hsl(.7,1,.8);
43
44
  }
44
45
 
45
46
  function gameRender()
46
47
  {
47
48
  const h = 100, w = 20;
48
- const pos = vec2(), size = vec2(.2,h), color = WHITE;
49
+ const pos = vec2(), sizeTop = vec2(.4), size = vec2(.2,h), color = WHITE;
49
50
  for (let x=cameraPos.x-w; x<cameraPos.x+w; x+=.1)
50
51
  {
51
52
  pos.x = x;
52
- pos.y = getGroundHeight(x)-h/2;
53
- color.setHSLA(.2+.2*wave(.2,1,x), .5, .5);
53
+ pos.y = getGroundHeight(x);
54
+ drawRect(pos, sizeTop, BLACK);
55
+ pos.y -= h/2;
56
+ color.setHSLA(.2+.2*wave(.2,1,x), .7, .5);
54
57
  drawRect(pos, size, color);
55
58
  }
56
59
  }
@@ -44,4 +44,5 @@ function gameInit()
44
44
  }
45
45
  tileLayer.redraw(); // redraw tile layer with new data
46
46
  cameraPos = mazeSize.scale(.5); // center camera
47
+ canvasClearColor = hsl(rand(),.3,.2); // random background color
47
48
  }
@@ -12,6 +12,9 @@ medalsInit(saveName);
12
12
  medalsForEach(medal=> medal.unlocked = false);
13
13
  medal_openedExample.unlock();
14
14
 
15
+ // set background color
16
+ canvasClearColor = hsl(.5,.3,.2);
17
+
15
18
  function gameUpdate()
16
19
  {
17
20
  if (mouseWasPressed(0))
@@ -1,18 +1,16 @@
1
- // Music player variables
1
+ let playButton, stopButton, progressBar;
2
2
  let musicSound = new SoundWave('song.mp3');
3
3
  let musicVolume = 1;
4
4
  let musicInstance;
5
5
 
6
- // UI elements
7
- let playButton, stopButton, progressBar;
8
-
9
6
  function gameInit()
10
7
  {
11
- // load ui system plugin
8
+ // setup ui system plugin
12
9
  new UISystemPlugin;
13
10
  uiSystem.defaultSoundPress = new Sound([.5,0,220]);
14
11
  uiSystem.defaultSoundClick = new Sound([.5,0,440]);
15
12
  uiSystem.defaultCornerRadius = 20;
13
+ canvasClearColor = hsl(.9,.3,.2); // background color
16
14
 
17
15
  // setup music player UI
18
16
  const musicPlayer = new UIObject(mainCanvasSize.scale(.5), vec2(500, 300));
@@ -1,51 +1,50 @@
1
- const pianoSound = new Sound([,0,220,,9]), keys = [];
1
+ const pianoSound = new Sound([,0,220,,9]);
2
2
 
3
- class PianoKey extends EngineObject
3
+ class PianoKey extends UIButton
4
4
  {
5
5
  constructor(pos, size, semitone, isWhite)
6
6
  {
7
- const keySize = 2;
7
+ const keySize = 65;
8
8
  size = size.scale(keySize);
9
- pos = pos.scale(keySize).add(vec2(0,4-size.y/2));
10
- super(pos, size, 0, 0, hsl(0,0,isWhite ? 1 : .3));
11
- this.drawSize = size.subtract(vec2(.1,0));
9
+ pos = pos.scale(keySize).add(vec2(0,size.y/2-keySize*2));
10
+ pos = pos.add(mainCanvasSize.scale(.5));
11
+ super(pos, size, '', hsl(0,0,isWhite ? 1 : .3));
12
12
  this.semitone = semitone;
13
+ this.dragActivate = true;
14
+ this.hoverColor = hsl(0,1,isWhite ? .9 : .3);
15
+ this.activeColor = RED;
13
16
  }
14
- press()
17
+ onPress()
15
18
  {
16
19
  if (!this.soundInstance)
17
20
  this.soundInstance = pianoSound.playNote(this.semitone);
18
21
  }
19
- release()
22
+ onRelease()
20
23
  {
21
24
  if (this.soundInstance)
22
25
  this.soundInstance.stop(.2);
23
26
  this.soundInstance = 0;
24
27
  }
25
- render()
26
- {
27
- const color = this.soundInstance ? RED : this.color;
28
- drawRect(this.pos, this.drawSize, color);
29
- }
30
28
  }
31
29
 
32
30
  function gameInit()
33
31
  {
32
+ // initialize UI system
33
+ new UISystemPlugin;
34
+
34
35
  // create piano keyboard
35
36
  for (let i=15; i--;)
36
- keys.unshift(new PianoKey(vec2(i-7,0), vec2(1,4), [0,2,4,5,7,9,11][i%7]+(i/7|0)*12, 1));
37
+ {
38
+ const pos = vec2(i-7,0);
39
+ const size = vec2(1,4);
40
+ const semitone = [0,2,4,5,7,9,11][i%7]+(i/7|0)*12;
41
+ new PianoKey(pos, size, semitone, true);
42
+ }
37
43
  for (let i=10; i--;)
38
- keys.unshift(new PianoKey(vec2([1,2,4,5,6][i%5]+(i/5|0)*7-7.5,0), vec2(1,2), [1,3,6,8,10][i%5]+(i/5|0)*12));
39
- }
40
-
41
- function gameUpdate()
42
- {
43
- // update state of piano keys
44
- const pressedKey = keys.find(k=>k.soundInstance);
45
- const newPressedKey = mouseIsDown(0) && keys.find(k=>isOverlapping(k.pos, k.size, mousePos));
46
- if (newPressedKey != pressedKey)
47
44
  {
48
- pressedKey && pressedKey.release();
49
- newPressedKey && newPressedKey.press();
45
+ const pos = vec2([1,2,4,5,6][i%5]+(i/5|0)*7-7.5,0)
46
+ const size = vec2(1,2);
47
+ const semitone = [1,3,6,8,10][i%5]+(i/5|0)*12;
48
+ new PianoKey(pos, size, semitone);
50
49
  }
51
50
  }
@@ -26,6 +26,7 @@ function gameInit()
26
26
  // setup level
27
27
  gravity.y = -.05;
28
28
  new Player(vec2(5,6));
29
+ canvasClearColor = hsl(.6,.3,.5);
29
30
 
30
31
  // create tile layer
31
32
  const pos = vec2();
@@ -1,18 +1,18 @@
1
1
  function gameRender()
2
2
  {
3
3
  // circles and ellipses
4
- drawCircle(vec2(0,5), 1+wave(.5), RED);
5
- drawEllipse(vec2(-6,5), vec2(1,1), YELLOW, .5);
6
- drawEllipse(vec2(6,5), vec2(1,2), CLEAR_BLACK, .3, .5, CYAN)
4
+ drawEllipse(vec2(-6,5), vec2(3,2), YELLOW, .5);
5
+ drawCircle(vec2(0,5), 2+wave(.5), RED);
6
+ drawEllipse(vec2(6,5), vec2(2,4), CLEAR_BLACK, .3, .5, CYAN)
7
7
 
8
8
  // polygon shapes
9
9
  drawRectGradient(vec2(-6,0), vec2(5,4), RED, BLUE)
10
- drawRegularPoly(vec2(0,0), vec2(2), 3, PURPLE, .2, WHITE, time);
10
+ drawRegularPoly(vec2(0,0), vec2(4), 3, PURPLE, .2, WHITE, time);
11
11
  const starPath = [vec2(0,2), vec2(2,-2), vec2(-3,.5), vec2(3,.5), vec2(-2,-2), vec2(0,2)];
12
- drawLineList(starPath, .2, MAGENTA, true, vec2(6, 0));
12
+ drawLineList(starPath, .2, GREEN, true, vec2(6, 0));
13
13
 
14
14
  // rects and lines
15
15
  drawRect(vec2(0,-5), vec2(4,3), ORANGE, time);
16
16
  drawLine(vec2(-5,-7), vec2(5,-3), 1, hsl(0,0,1,.5));
17
- drawLineList([vec2(5,-3), vec2(-5,-3), vec2(5,-7), vec2(-5,-7)], .2, GREEN);
17
+ drawLineList([vec2(5,-3), vec2(-5,-3), vec2(5,-7), vec2(-5,-7)], .2, MAGENTA);
18
18
  }
@@ -1,37 +1,26 @@
1
- class SoundButton extends EngineObject
1
+ function gameInit()
2
2
  {
3
- constructor(pos, icon, sound)
4
- {
5
- super(pos, vec2(5,4));
6
- this.icon = icon;
7
- this.sound = sound;
8
- }
9
-
10
- update()
11
- {
12
- // play sound when clicked
13
- if (mouseWasPressed(0) && isOverlapping(this.pos, this.size, mousePos))
14
- this.sound.play(this.pos);
15
- }
3
+ // initialize UI system
4
+ new UISystemPlugin;
5
+ canvasClearColor = hsl(.6,.3,.2);
16
6
 
17
- render()
7
+ // create a grid of buttons with different sounds
8
+ const w = 200, h = 150, gap = 20;
9
+ function makeSoundButton(pos, icon, sound)
18
10
  {
19
- // draw the button and icon
20
- drawRect(this.pos, this.size, hsl(0,0,.8));
21
- drawTextOverlay(this.icon, this.pos, 3);
11
+ pos = pos.multiply(vec2(w+gap, h+gap));
12
+ pos = pos.add(mainCanvasSize.scale(.5));
13
+ const button = new UIButton(pos, vec2(w, h), icon);
14
+ button.textHeight = 100;
15
+ button.onClick = ()=> sound.play();
22
16
  }
23
- }
24
-
25
- function gameInit()
26
- {
27
- // create a grid of buttons with different sounds
28
- new SoundButton(vec2(-6, 5),'💰', new Sound([,,1675,,.06,.24,1,1.82,,,837,.06]));
29
- new SoundButton(vec2( 0, 5),'🥊', new Sound([,,925,.04,.3,.6,1,.3,,6.27,-184,.09,.17]));
30
- new SoundButton(vec2( 6, 5),'', new Sound([,,539,0,.04,.29,1,1.92,,,567,.02,.02,,,,.04]));
31
- new SoundButton(vec2(-6, 0),'🐁', new Sound([,.2,1e3,.02,,.01,2,,18,,475,.01,.01]));
32
- new SoundButton(vec2( 0, 0),'🎹', new Sound([1.5,.5,270,,.1,,1,1.5,,,,,,,,.1,.01]));
33
- new SoundButton(vec2( 6, 0),'🏌️', new Sound([,,150,.05,,.05,,1.3,,,,,,3]));
34
- new SoundButton(vec2(-6,-5),'🌊', new Sound([,.2,40,.5,,1.5,,11,,,,,,199]));
35
- new SoundButton(vec2( 0,-5),'🛰️', new Sound([,.5,847,.02,.3,.9,1,1.67,,,-294,.04,.13,,,,.1]));
36
- new SoundButton(vec2( 6,-5),'⚡', new Sound([,,471,,.09,.47,4,1.06,-6.7,,,,,.9,61,.1,,.82,.1]));
17
+ makeSoundButton(vec2(-1, 1),'💰', new Sound([,,1675,,.06,.24,1,1.82,,,837,.06]));
18
+ makeSoundButton(vec2( 0, 1),'🥊', new Sound([,,925,.04,.3,.6,1,.3,,6.27,-184,.09,.17]));
19
+ makeSoundButton(vec2( 1, 1),'✨', new Sound([,,539,0,.04,.29,1,1.92,,,567,.02,.02,,,,.04]));
20
+ makeSoundButton(vec2(-1, 0),'🐁', new Sound([,.2,1e3,.02,,.01,2,,18,,475,.01,.01]));
21
+ makeSoundButton(vec2( 0, 0),'🎹', new Sound([1.5,.5,270,,.1,,1,1.5,,,,,,,,.1,.01]));
22
+ makeSoundButton(vec2( 1, 0),'🏌️', new Sound([,,150,.05,,.05,,1.3,,,,,,3]));
23
+ makeSoundButton(vec2(-1,-1),'🌊', new Sound([,.2,40,.5,,1.5,,11,,,,,,199]));
24
+ makeSoundButton(vec2( 0,-1),'🛰️', new Sound([,.5,847,.02,.3,.9,1,1.67,,,-294,.04,.13,,,,.1]));
25
+ makeSoundButton(vec2( 1,-1),'', new Sound([,,471,,.09,.47,4,1.06,-6.7,,,,,.9,61,.1,,.82,.1]));
37
26
  }
@@ -1,4 +1,5 @@
1
1
  let spriteAtlas;
2
+ canvasClearColor = GRAY;
2
3
 
3
4
  function gameInit()
4
5
  {
@@ -16,8 +17,6 @@ function gameInit()
16
17
 
17
18
  function gameRender()
18
19
  {
19
- drawRect(vec2(0,0), vec2(100), GRAY); // draw background
20
-
21
20
  // draw a sprite from the atlas
22
21
  let pos = vec2(Math.sin(time)*5,-3);// world position to draw
23
22
  let angle = 0; // world space angle to draw the tile
@@ -2,6 +2,7 @@ function gameInit()
2
2
  {
3
3
  cameraPos = vec2(16); // setup camera
4
4
  gravity.y = -.01; // enable gravity
5
+ canvasClearColor = hsl(0,0,.2); // background color
5
6
 
6
7
  // create tile layer
7
8
  const pos = vec2();
@@ -37,7 +37,7 @@ function gameInit()
37
37
 
38
38
  // create background objects
39
39
  for (let i=1; i<300; ++i)
40
- new EngineObject(randInCircle(90), vec2(rand(59),rand(59)), 0, 0, hsl(.4,.3,rand(.1,.2),.8), -1e5);
40
+ new EngineObject(randInCircle(90), vec2(rand(59),rand(59)), 0, 0, hsl(.4,.2,rand(.4,.5),.8), -1e5);
41
41
 
42
42
  // create world objects
43
43
  for (let i=1; i<1e3; ++i)