littlejsengine 1.13.1 → 1.14.2

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 (83) hide show
  1. package/README.md +15 -11
  2. package/copyToGithub.bat +28 -0
  3. package/dist/littlejs.d.ts +430 -304
  4. package/dist/littlejs.esm.js +1745 -982
  5. package/dist/littlejs.esm.min.js +1 -1
  6. package/dist/littlejs.js +1716 -965
  7. package/dist/littlejs.min.js +1 -1
  8. package/dist/littlejs.release.js +1673 -922
  9. package/examples/box2d/game.js +2 -2
  10. package/examples/box2d/gameObjects.js +2 -2
  11. package/examples/box2d/scenes.js +1 -1
  12. package/examples/breakout/game.js +30 -25
  13. package/examples/breakoutTutorial/README.md +44 -41
  14. package/examples/breakoutTutorial/game.js +2 -2
  15. package/examples/electron/build.bat +7 -0
  16. package/examples/electron/build.js +124 -0
  17. package/examples/electron/electron.js +35 -0
  18. package/examples/electron/game.js +140 -0
  19. package/examples/electron/index.html +13 -0
  20. package/examples/electron/package.json +12 -0
  21. package/examples/electron/tiles.png +0 -0
  22. package/examples/empty/game.js +1 -0
  23. package/examples/htmlMenu/game.js +1 -1
  24. package/examples/index.html +332 -160
  25. package/examples/module/game.js +5 -2
  26. package/examples/particles/index.html +12 -3
  27. package/examples/platformer/gameEffects.js +20 -19
  28. package/examples/platformer/gameLevel.js +6 -1
  29. package/examples/shorts/base.html +1 -1
  30. package/examples/shorts/blending.js +9 -5
  31. package/examples/shorts/box2d.js +1 -1
  32. package/examples/shorts/box2dCar.js +1 -1
  33. package/examples/shorts/clock.js +1 -1
  34. package/examples/shorts/colors.js +2 -2
  35. package/examples/shorts/flappyGame.js +2 -1
  36. package/examples/shorts/helloWorld.js +1 -1
  37. package/examples/shorts/maze.js +1 -1
  38. package/examples/shorts/music.js +106 -0
  39. package/examples/shorts/nineSlice.js +9 -9
  40. package/examples/shorts/parallax.js +71 -0
  41. package/examples/shorts/piano.js +9 -10
  42. package/examples/shorts/postProcess.js +25 -8
  43. package/examples/shorts/raycasting.js +2 -2
  44. package/examples/shorts/shapes.js +7 -15
  45. package/examples/shorts/slidingPuzzle.js +2 -2
  46. package/examples/shorts/song.mp3 +0 -0
  47. package/examples/shorts/starfield.js +5 -7
  48. package/examples/shorts/systemFont.js +1 -1
  49. package/examples/shorts/uiSystem.js +16 -8
  50. package/examples/starter/build.js +9 -8
  51. package/examples/starter/game.js +5 -2
  52. package/examples/starter/index.html +2 -2
  53. package/examples/stress/index.html +13 -8
  54. package/examples/style.css +19 -6
  55. package/examples/typescript/build.js +1 -1
  56. package/examples/typescript/game.js +2 -2
  57. package/examples/typescript/game.ts +5 -2
  58. package/examples/uiSystem/game.js +13 -12
  59. package/package.json +2 -2
  60. package/plugins/box2d.js +24 -97
  61. package/plugins/drawUtilities.js +29 -23
  62. package/plugins/newgrounds.js +6 -6
  63. package/plugins/pluginExport.js +1 -1
  64. package/plugins/postProcess.js +7 -0
  65. package/plugins/uiSystem.js +106 -82
  66. package/plugins/zzfxm.js +10 -10
  67. package/reference.md +90 -54
  68. package/src/engine.js +28 -23
  69. package/src/engineAudio.js +285 -110
  70. package/src/engineBuild.js +9 -9
  71. package/src/engineDebug.js +28 -28
  72. package/src/engineDraw.js +346 -202
  73. package/src/engineExport.js +28 -16
  74. package/src/engineInput.js +58 -68
  75. package/src/engineMedals.js +29 -29
  76. package/src/engineObject.js +28 -25
  77. package/src/engineParticles.js +67 -60
  78. package/src/engineRelease.js +1 -1
  79. package/src/engineSettings.js +70 -16
  80. package/src/engineTileLayer.js +34 -34
  81. package/src/engineUtilities.js +69 -62
  82. package/src/engineWebGL.js +467 -65
  83. /package/examples/shorts/{playSound.js → sound.js} +0 -0
@@ -101,7 +101,10 @@ function gameUpdate()
101
101
 
102
102
  // move particles to mouse location if on screen
103
103
  if (LJS.mousePosScreen.x)
104
+ {
104
105
  particleEmitter.pos = LJS.mousePos;
106
+ particleEmitter.velocity = LJS.mouseDelta.scale(.3*LJS.timeDelta);
107
+ }
105
108
  }
106
109
 
107
110
  ///////////////////////////////////////////////////////////////////////////////
@@ -113,8 +116,8 @@ function gameUpdatePost()
113
116
  ///////////////////////////////////////////////////////////////////////////////
114
117
  function gameRender()
115
118
  {
116
- // draw a grey square in the background without using webgl
117
- LJS.drawRect(vec2(16,8), vec2(20,14), hsl(0,0,.6), 0, false);
119
+ // draw a grey square in the background
120
+ LJS.drawRect(vec2(16,8), vec2(20,14), hsl(0,0,.6));
118
121
 
119
122
  // draw the logo as a tile
120
123
  LJS.drawTile(vec2(21,5), vec2(4.5), tile(3,128));
@@ -94,7 +94,7 @@ function gameInit()
94
94
  if (type == 'color')
95
95
  {
96
96
  const color = particleSystem[name];
97
- defaultValue = color.toString(0);
97
+ defaultValue = color.toString(false);
98
98
  }
99
99
  else if (type == 'alpha')
100
100
  {
@@ -266,7 +266,7 @@ function updateSetting(setting, save=1)
266
266
  if (type == 'color')
267
267
  {
268
268
  const color = particleSystem[name];
269
- input.value = color.toString(0);
269
+ input.value = color.toString(false);
270
270
  }
271
271
  else if (type == 'alpha')
272
272
  {
@@ -369,7 +369,16 @@ function gameUpdate()
369
369
 
370
370
  if (document.activeElement == document.body)
371
371
  {
372
- particleSystem.pos = LJS.mouseIsDown(0) ? LJS.mousePos : vec2();
372
+ if (LJS.mouseIsDown(0))
373
+ {
374
+ particleSystem.pos = LJS.mousePos;
375
+ particleSystem.velocity = LJS.mouseDelta.scale(.3*LJS.timeDelta);
376
+ }
377
+ else
378
+ {
379
+ particleSystem.pos = vec2();
380
+ particleSystem.velocity = vec2();
381
+ }
373
382
  if (LJS.mouseWheel)
374
383
  {
375
384
  // zoom camera
@@ -82,7 +82,7 @@ export function explosion(pos, radius=3)
82
82
  GameLevel.decorateTile(pos.add(vec2(x,y)).floor());
83
83
  }
84
84
 
85
- // update webgl texture
85
+ // update WebGL texture
86
86
  GameLevel.updateWebGL(GameLevel.foregroundTileLayer);
87
87
 
88
88
  // kill/push objects
@@ -153,7 +153,7 @@ export function destroyTile(pos, makeSound = 1, cleanup = 1)
153
153
  layer.setData(pos, new LJS.TileLayerData, true);
154
154
  layer.setCollisionData(pos, GameLevel.tileType_empty);
155
155
 
156
- // cleanup neighbors and rebuild webgl
156
+ // cleanup neighbors and rebuild WebGL
157
157
  if (cleanup)
158
158
  {
159
159
  for (let i=-1;i<=1;++i)
@@ -185,7 +185,7 @@ export class Sky extends LJS.EngineObject
185
185
  // fill background with a gradient
186
186
  const canvas = LJS.mainCanvas;
187
187
  const gradientDelta = 5;
188
- for(let y=0; y<canvas.height; y+=gradientDelta)
188
+ for (let y=0; y<canvas.height; y+=gradientDelta)
189
189
  {
190
190
  // draw horizontal lines to create a gradient
191
191
  const color = this.skyColor.lerp(this.horizonColor, y/canvas.height);
@@ -216,21 +216,20 @@ export class Sky extends LJS.EngineObject
216
216
 
217
217
  export class ParallaxLayer extends LJS.CanvasLayer
218
218
  {
219
- constructor(depth)
219
+ constructor(pos, topColor, bottomColor, depth)
220
220
  {
221
221
  const renderOrder = depth - 3e3;
222
222
  const canvasSize = vec2(512, 256);
223
- super(vec2(), vec2(), 0, renderOrder, canvasSize);
224
- this.canvasSize = canvasSize;
223
+ super(pos, vec2(), 0, renderOrder, canvasSize);
224
+ this.centerPos = pos;
225
225
  this.depth = depth;
226
226
 
227
227
  // create a gradient for the mountains
228
- const topColor = GameLevel.levelColor.mutate(.2).lerp(GameLevel.sky.skyColor, .8 - depth*.15);
229
- const bottomColor = GameLevel.levelColor.subtract(LJS.CLEAR_WHITE).mutate(.2);
230
- for(let i = canvasSize.y; i--;)
228
+ const w = canvasSize.x, h = canvasSize.y;
229
+ for (let i = h; i--;)
231
230
  {
232
231
  // draw a 1 pixel gradient line on the left side of the canvas
233
- const p = i/canvasSize.y;
232
+ const p = i/h;
234
233
  this.context.fillStyle = topColor.lerp(bottomColor, p);
235
234
  this.context.fillRect(0, i, 1, 1);
236
235
  }
@@ -239,40 +238,42 @@ export class ParallaxLayer extends LJS.CanvasLayer
239
238
  const pointiness = .2; // how pointy the mountains are
240
239
  const levelness = .005; // how much the mountains level out
241
240
  const slopeRange = 1; // max slope of the mountains
242
- const startGroundLevel = canvasSize.y/2;
241
+ const startGroundLevel = h/2;
243
242
  let y = startGroundLevel, groundSlope = LJS.rand(-slopeRange, slopeRange);
244
- for(let x=canvasSize.x; x--;)
243
+ for (let x=w; x--;)
245
244
  {
246
245
  // pull slope towards start ground level
247
246
  y += groundSlope -= (y-startGroundLevel)*levelness;
248
247
 
249
- // random change slope
248
+ // randomly change slope
250
249
  if (LJS.rand() < pointiness)
251
250
  groundSlope = LJS.rand(-slopeRange, slopeRange);
252
251
 
253
252
  // draw 1 pixel wide vertical slice of mountain
254
- this.context.drawImage(this.canvas, 0, 0, 1, canvasSize.y, x, y, 1, canvasSize.y - y);
253
+ this.context.drawImage(this.canvas, 0, 0, 1, h, x, y, 1, h - y);
255
254
  }
256
255
 
257
256
  // remove gradient sliver from left side
258
- this.context.clearRect(0,0,1,canvasSize.y);
257
+ this.context.clearRect(0,0,1,h);
259
258
 
260
- // make webgl texture
259
+ // make WebGL texture
261
260
  this.useWebGL(LJS.glEnable);
262
261
  }
263
262
 
264
263
  render()
265
264
  {
265
+ const canvasSize = vec2(this.canvas.width, this.canvas.height);
266
266
  const depth = this.depth
267
267
  const distance = 4 + depth;
268
268
  const parallax = vec2(150, 30).scale(depth**2+1);
269
- const levelCenter = GameLevel.levelSize.scale(.5);
270
- const cameraDeltaFromCenter = LJS.cameraPos.subtract(levelCenter).divide(levelCenter.scale(-1).divide(parallax));
269
+ const levelCenter = this.centerPos;
270
+ const cameraDeltaFromCenter = LJS.cameraPos.subtract(levelCenter)
271
+ .divide(levelCenter.scale(-1).divide(parallax));
271
272
  const scale = distance/LJS.cameraScale;
272
273
  const positonOffset = vec2(0, 2-depth);
273
274
  const cameraOffset = cameraDeltaFromCenter.scale(1/LJS.cameraScale);
274
275
  this.pos = LJS.cameraPos.add(positonOffset).add(cameraOffset);
275
- this.size = this.canvasSize.scale(scale);
276
+ this.size = canvasSize.scale(scale);
276
277
  super.render();
277
278
  }
278
279
  }
@@ -39,7 +39,12 @@ export function buildLevel()
39
39
 
40
40
  // create parallax layers
41
41
  for (let i=3; i--;)
42
- new GameEffects.ParallaxLayer(i);
42
+ {
43
+ const pos = levelSize.scale(.5);
44
+ const topColor = levelColor.mutate(.2).lerp(sky.skyColor, .8 - i*.15);
45
+ const bottomColor = levelColor.subtract(LJS.CLEAR_WHITE).mutate(.2);
46
+ new GameEffects.ParallaxLayer(pos, topColor, bottomColor, i );
47
+ }
43
48
  }
44
49
 
45
50
  function loadLevelData()
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html><meta charset=utf-8><body>
2
- <script src=../../dist/littlejs.js?11212></script>
2
+ <script src=../../dist/littlejs.js?1134></script>
3
3
  <script src=../../dist/box2d.wasm.js></script>
4
4
 
5
5
  <!-- LittleJS Engine Source
@@ -1,16 +1,20 @@
1
+ // use smoother textures
2
+ setCanvasPixelated(false);
3
+ setTilesPixelated(false);
4
+
1
5
  function gameRender()
2
6
  {
3
7
  const circleTile = tile(2, 128); // get the circle tile
4
8
 
5
9
  // additive blend
6
10
  setBlendMode(1);
7
- drawTile(vec2(-4,-2), vec2(7), circleTile, rgb(1,0,0));
11
+ drawTile(vec2(-8,-2), vec2(7), circleTile, rgb(0,0,.5), 0, false, rgb(0,0,.5));
8
12
  drawTile(vec2(-6, 2), vec2(7), circleTile, rgb(0,1,0));
9
- drawTile(vec2(-8,-2), vec2(7), circleTile, rgb(0,0,1));
13
+ drawTile(vec2(-4,-2), vec2(7), circleTile, rgb(1,0,0));
10
14
 
11
15
  // alpha blend
12
16
  setBlendMode(0);
13
- drawTile(vec2(4,-2), vec2(7), circleTile, rgb(1,0,0,.5));
14
- drawTile(vec2(6, 2), vec2(7), circleTile, rgb(0,1,0,.5));
15
- drawTile(vec2(8,-2), vec2(7), circleTile, rgb(0,0,1,.5));
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));
16
20
  }
@@ -10,7 +10,7 @@ async function gameInit()
10
10
  groundObject.addBox(vec2(100,2));
11
11
 
12
12
  // add some random objects
13
- for(let i=50; i--;)
13
+ for (let i=50; i--;)
14
14
  {
15
15
  const o = new Box2dObject(randInCircle(5), vec2(), 0, 0, randColor());
16
16
  randInt(2) ? o.addCircle(rand(1,2)) : o.addRandomPoly(rand(1,2));
@@ -18,7 +18,7 @@ class CarObject extends Box2dObject
18
18
  this.addBox(vec2(7,2));
19
19
  const frequency = 4, maxTorque = 250;
20
20
  this.wheels = [];
21
- for(let i=2; i--;)
21
+ for (let i=2; i--;)
22
22
  {
23
23
  const wheelPos = pos.add(vec2(i?2:-2, -1));
24
24
  const wheel = new Box2dObject(wheelPos, vec2(2), tile(7));
@@ -1,7 +1,7 @@
1
1
  function gameRender()
2
2
  {
3
3
  // draw background
4
- for(let i=12; i--;)
4
+ for (let i=12; i--;)
5
5
  {
6
6
  const a = i/6*PI;
7
7
  const pos = vec2(0,7).rotate(a);
@@ -10,12 +10,12 @@ function gameRender()
10
10
  // rgb and hsl color
11
11
  drawRect(vec2(-8, 0), vec2(3), new Color(1,0,0));
12
12
  drawRect(vec2(-4, 0), vec2(3), rgb(0,1,1));
13
- drawRect(vec2(-0, 0), vec2(3), hsl(0,1,.5));
13
+ drawRect(vec2(-0, 0), vec2(3), hsl(0,1,.5, .5));
14
14
  drawRect(vec2( 4, 0), vec2(3), hsl(.6,.5,.5));
15
15
  drawRect(vec2( 8, 0), vec2(3), hsl(0,0,1));
16
16
 
17
17
  // color lerpping
18
- for(let i=5; i--;)
18
+ for (let i=5; i--;)
19
19
  {
20
20
  const color1 = RED;
21
21
  const color2 = YELLOW;
@@ -28,6 +28,7 @@ class Player extends EngineObject
28
28
  if (mouseWasPressed(0) || keyWasPressed('Space'))
29
29
  this.velocity = vec2(0,.2);
30
30
  this.angle = -this.velocity.y*2;
31
+ this.pos.y = max(-50, this.pos.y);
31
32
  }
32
33
 
33
34
  collideWithObject(object)
@@ -44,7 +45,7 @@ function gameInit()
44
45
  gravity.y = -.01;
45
46
  for (let i=100; i--;)
46
47
  {
47
- const h=50, y=-h/2-6+rand(9), spacing=15, gap=5;
48
+ const h=100, y=-h/2-6+rand(9), spacing=15, gap=5;
48
49
  new Wall(vec2(14 + i*spacing,y+h + gap), vec2(3,h));
49
50
  new Wall(vec2(14 + i*spacing,y), vec2(3,h));
50
51
  }
@@ -1,7 +1,7 @@
1
1
  function gameRender()
2
2
  {
3
3
  // draw background rect
4
- drawRect(cameraPos, vec2(30), hsl(0,0,.2));
4
+ drawRect(cameraPos, vec2(50), hsl(0,0,.2));
5
5
 
6
6
  // draw text to the overlay canvas
7
7
  drawTextOverlay('Hello World', vec2(0,3), 3);
@@ -2,7 +2,7 @@ function generateMaze(size)
2
2
  {
3
3
  const maze = [], w = size.x, h = size.y;
4
4
  maze[1 + w] = 1; // start point
5
- for(let k=w*h*9|0; k--;)
5
+ for (let k=w*h*9|0; k--;)
6
6
  {
7
7
  // get a random position on odd coordinates
8
8
  const jx = randInt(w/2-(w%2?0:2)|0)*2 + 1;
@@ -0,0 +1,106 @@
1
+ // Music player variables
2
+ let musicSound = new SoundWave('song.mp3');
3
+ let musicVolume = .8;
4
+ let musicInstance;
5
+
6
+ // UI elements
7
+ let playButton, stopButton, progressBar;
8
+
9
+ function gameInit()
10
+ {
11
+ // load ui system plugin
12
+ new UISystemPlugin;
13
+ uiSystem.defaultSoundPress = new Sound([.3,0,220]);
14
+ uiSystem.defaultSoundClick = new Sound([.3,0,440]);
15
+ uiSystem.defaultCornerRadius = 20;
16
+
17
+ // setup music player UI
18
+ const musicPlayer = new UIObject(mainCanvasSize.scale(.5), vec2(500, 300));
19
+
20
+ // title
21
+ const title = new UIText(vec2(0, -100), vec2(500, 40), 'LittleJS Music Player');
22
+ musicPlayer.addChild(title);
23
+
24
+ // volume slider
25
+ const volumeSlider = new UIScrollbar(vec2(0, -40), vec2(400, 30), musicVolume, 'Music Volume');
26
+ musicPlayer.addChild(volumeSlider);
27
+ volumeSlider.onChange = () =>
28
+ {
29
+ musicVolume = volumeSlider.value;
30
+ if (musicInstance)
31
+ musicInstance.setVolume(musicVolume);
32
+ };
33
+
34
+ // play button
35
+ playButton = new UIButton(vec2(-90, 30), vec2(140, 50), 'Play');
36
+ musicPlayer.addChild(playButton);
37
+ playButton.onClick = ()=>
38
+ {
39
+ if (!musicSound.isLoaded())
40
+ return;
41
+
42
+ // handle play/pause toggle
43
+ if (!musicInstance)
44
+ musicInstance = musicSound.playMusic(musicVolume);
45
+ else if (musicInstance.isPaused())
46
+ musicInstance.resume();
47
+ else
48
+ musicInstance.pause();
49
+ };
50
+
51
+ // stop button
52
+ stopButton = new UIButton(vec2(90, 30), vec2(140, 50), 'Stop');
53
+ musicPlayer.addChild(stopButton);
54
+ stopButton.onClick = ()=>
55
+ {
56
+ if (!musicInstance)
57
+ return;
58
+
59
+ // stop sound
60
+ musicInstance.stop();
61
+ };
62
+
63
+ // progress bar and scrollbar for seeking
64
+ progressBar = new UIScrollbar(vec2(0, 100), vec2(400, 30), 0, 'Progress');
65
+ musicPlayer.addChild(progressBar);
66
+ progressBar.onChange = ()=>
67
+ {
68
+ // control music seek position
69
+ const wasPlaying = musicInstance && musicInstance.isPlaying();
70
+ if (!musicInstance)
71
+ musicInstance = musicSound.playMusic(musicVolume, true, true);
72
+ progressBar.value = min(progressBar.value, .999); // prevent looping around
73
+ const seekTime = progressBar.value * musicSound.getDuration();
74
+ musicInstance.start(seekTime);
75
+ if (!wasPlaying)
76
+ musicInstance.pause();
77
+ };
78
+ }
79
+
80
+ function gameUpdate()
81
+ {
82
+ // disable buttons while loading or waiting for interaction
83
+ const isDisabled = !musicSound.isLoaded() || !audioIsRunning();
84
+ playButton.disabled = isDisabled
85
+ stopButton.disabled = isDisabled
86
+ progressBar.disabled = isDisabled
87
+
88
+ // update ui
89
+ if (!musicSound.isLoaded())
90
+ {
91
+ // update loading progress
92
+ const loadingPercent = musicSound.loadedPercent * 100;
93
+ progressBar.text = `Loading: ${(loadingPercent).toFixed(2)}%`;
94
+ }
95
+ else
96
+ {
97
+ // update ui text
98
+ playButton.text = !musicInstance || !musicInstance.isPlaying() ? 'Play' : 'Pause';
99
+ const currentTime = musicInstance ? musicInstance.getCurrentTime() : 0;
100
+ const duration = musicSound.getDuration();
101
+ if (!progressBar.mouseIsHeld)
102
+ progressBar.value = currentTime / duration;
103
+ const timeText = `${formatTime(currentTime)} / ${formatTime(duration)}`;
104
+ progressBar.text = audioIsRunning() ? timeText : 'Click to Enable Audio';
105
+ }
106
+ }
@@ -4,18 +4,18 @@ function gameRender()
4
4
  const threeSliceTile = tile(19);
5
5
 
6
6
  // draw nine slice with thin border and color
7
- drawNineSlice(vec2(-7,4), vec2(13, 6), nineSliceTile, hsl(.1,.5,.9), .5);
7
+ drawNineSlice(vec2(-7,4), vec2(11+wave(.5,2), 6), nineSliceTile, hsl(.1,.5,.9), .5);
8
8
  drawTextOverlay('Nine Slice\nThin Border', vec2(-7,4), 1, BLACK);
9
9
 
10
- // draw nine slice in screen space with thick border
11
- drawNineSliceScreen(vec2(700,150), vec2(250), nineSliceTile);
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
12
  drawTextScreen('Nine Slice\nScreen Space', vec2(700,150), 30, BLACK);
13
13
 
14
- // draw three slice with thick border and additive color
15
- drawThreeSlice(vec2(-7,-4), vec2(9, 7), threeSliceTile, WHITE, 2, hsl(.6,1,.5));
16
- drawTextOverlay('Three Slice\nThick Border', vec2(-7,-4), 1, BLACK);
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
17
 
18
- // draw three slice in screen space with thick border
19
- drawThreeSliceScreen(vec2(700,400), vec2(400,150), threeSliceTile);
20
- drawTextScreen('Three Slice\nScreen Space', vec2(700,400), 30, BLACK);
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);
21
21
  }
@@ -0,0 +1,71 @@
1
+ function gameInit()
2
+ {
3
+ // create parallax layers
4
+ const levelColor = hsl(rand(), .5, .5);
5
+ for (let i=3; i--;)
6
+ {
7
+ const topColor = levelColor.mutate(.3);
8
+ const bottomColor = levelColor.subtract(CLEAR_WHITE).mutate(.3);
9
+ new ParallaxLayer(topColor, bottomColor, i);
10
+ }
11
+ }
12
+
13
+ class ParallaxLayer extends CanvasLayer
14
+ {
15
+ constructor(topColor, bottomColor, depth)
16
+ {
17
+ const renderOrder = depth;
18
+ const canvasSize = vec2(512, 256);
19
+ super(vec2(), vec2(), 0, renderOrder, canvasSize);
20
+ this.depth = depth;
21
+
22
+ // create a gradient for the mountains
23
+ const w = canvasSize.x, h = canvasSize.y;
24
+ for (let i = h; i--;)
25
+ {
26
+ // draw a 1 pixel gradient line on the left side of the canvas
27
+ const p = i/h;
28
+ this.context.fillStyle = topColor.lerp(bottomColor, p);
29
+ this.context.fillRect(0, i, 1, 1);
30
+ }
31
+
32
+ // draw random mountains
33
+ const pointiness = .2; // how pointy the mountains are
34
+ const levelness = .005; // how much the mountains level out
35
+ const slopeRange = 1; // max slope of the mountains
36
+ const startGroundLevel = h/2;
37
+ let y = startGroundLevel, groundSlope = rand(-slopeRange, slopeRange);
38
+ for (let x=w; x--;)
39
+ {
40
+ // pull slope towards start ground level
41
+ y += groundSlope -= (y-startGroundLevel)*levelness;
42
+
43
+ // randomly change slope
44
+ if (rand() < pointiness)
45
+ groundSlope = rand(-slopeRange, slopeRange);
46
+
47
+ // draw 1 pixel wide vertical slice of mountain
48
+ this.context.drawImage(this.canvas, 0, 0, 1, h, x, y, 1, h - y);
49
+ }
50
+
51
+ // remove gradient sliver from left side
52
+ this.context.clearRect(0,0,1,h);
53
+
54
+ // make WebGL texture
55
+ this.useWebGL(glEnable);
56
+ }
57
+
58
+ render()
59
+ {
60
+ const canvasSize = vec2(this.canvas.width, this.canvas.height);
61
+ const viewerPos = mousePos;
62
+ const depth = this.depth
63
+ const distance = 3 + depth;
64
+ const parallax = vec2(.2, .05).scale(depth**2+1);
65
+ const cameraDeltaFromCenter = viewerPos.multiply(parallax);
66
+ const positonOffset = vec2(0, 4-depth*3);
67
+ this.pos = cameraDeltaFromCenter.add(positonOffset)
68
+ this.size = canvasSize.scale(distance/cameraScale);
69
+ super.render();
70
+ }
71
+ }
@@ -13,19 +13,18 @@ class PianoKey extends EngineObject
13
13
  }
14
14
  press()
15
15
  {
16
- if (!this.isDown)
17
- pianoSound.playNote(this.semitone);
18
- this.isDown = true;
16
+ if (!this.soundInstance)
17
+ this.soundInstance = pianoSound.playNote(this.semitone);
19
18
  }
20
19
  release()
21
20
  {
22
- if (this.isDown)
23
- pianoSound.stop(.2);
24
- this.isDown = false;
21
+ if (this.soundInstance)
22
+ this.soundInstance.stop(.2);
23
+ this.soundInstance = 0;
25
24
  }
26
25
  render()
27
26
  {
28
- const color = this.isDown ? RED : this.color;
27
+ const color = this.soundInstance ? RED : this.color;
29
28
  drawRect(this.pos, this.drawSize, color);
30
29
  }
31
30
  }
@@ -33,16 +32,16 @@ class PianoKey extends EngineObject
33
32
  function gameInit()
34
33
  {
35
34
  // create piano keyboard
36
- for(let i=15; i--;)
35
+ for (let i=15; i--;)
37
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));
38
- for(let i=10; i--;)
37
+ for (let i=10; i--;)
39
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));
40
39
  }
41
40
 
42
41
  function gameUpdate()
43
42
  {
44
43
  // update state of piano keys
45
- const pressedKey = keys.find(k=>k.isDown);
44
+ const pressedKey = keys.find(k=>k.soundInstance);
46
45
  const newPressedKey = mouseIsDown(0) && keys.find(k=>isOverlapping(k.pos, k.size, mousePos));
47
46
  if (newPressedKey != pressedKey)
48
47
  {
@@ -6,7 +6,7 @@ function gameInit()
6
6
  function gameRender()
7
7
  {
8
8
  drawRect(vec2(), vec2(99), GRAY);
9
- drawTile(vec2(), vec2(12), tile(3,128));
9
+ drawTile(vec2(Math.sin(time)*3, 0), vec2(12), tile(3,128));
10
10
  }
11
11
 
12
12
  const tvShader = `
@@ -16,27 +16,44 @@ float hash(vec2 p)
16
16
  p=fract(p*.3197);
17
17
  return fract(1.+sin(51.*p.x+73.*p.y)*13753.3);
18
18
  }
19
- float noise(vec2 p)
20
- {
21
- vec2 i=floor(p),f=fract(p),u=f*f*(3.-2.*f);
22
- return mix(mix(hash(i),hash(i+vec2(1,0)),u.x),mix(hash(i+vec2(0,1)),hash(i+1.),u.x),u.y);
23
- }
19
+
24
20
  void mainImage(out vec4 c, vec2 p)
25
21
  {
26
22
  // setup the shader
23
+ vec2 uv = p;
27
24
  p /= iResolution.xy;
28
25
  c = texture(iChannel0, p);
29
26
 
30
27
  // static noise
31
28
  const float staticAlpha = .1;
32
- const float staticScale = .005;
29
+ const float staticScale = .002;
33
30
  c += staticAlpha * hash(floor(p/staticScale) + mod(iTime*500., 1e3));
34
31
 
35
32
  // scan lines
36
33
  const float scanlineScale = 2.;
37
- const float scanlineAlpha = .3;
34
+ const float scanlineAlpha = .5;
38
35
  c *= 1. - scanlineAlpha*cos(p.y*2.*iResolution.y/scanlineScale);
39
36
 
37
+ {
38
+ // bloom effect
39
+ const float blurSize = .002;
40
+ const float bloomIntensity = .2;
41
+
42
+ // 5-tap Gaussian blur
43
+ vec4 bloom = vec4(0);
44
+ bloom += texture(iChannel0, p + vec2(-2.*blurSize, 0)) * .12;
45
+ bloom += texture(iChannel0, p + vec2( -blurSize, 0)) * .24;
46
+ bloom += texture(iChannel0, p) * .28;
47
+ bloom += texture(iChannel0, p + vec2( blurSize, 0)) * .24;
48
+ bloom += texture(iChannel0, p + vec2( 2.*blurSize, 0)) * .12;
49
+ bloom += texture(iChannel0, p + vec2(0, -2.*blurSize)) * .12;
50
+ bloom += texture(iChannel0, p + vec2(0, -blurSize)) * .24;
51
+ bloom += texture(iChannel0, p) * .28;
52
+ bloom += texture(iChannel0, p + vec2(0, blurSize)) * .24;
53
+ bloom += texture(iChannel0, p + vec2(0, 2.*blurSize)) * .12;
54
+ c += bloom * bloomIntensity;
55
+ }
56
+
40
57
  // black vignette around edges
41
58
  const float vignette = 2.;
42
59
  const float vignettePow = 6.;
@@ -42,7 +42,7 @@ function gameRender()
42
42
  {
43
43
  // draw horizontal slizes to create floor and ceiling
44
44
  const h = 9;
45
- let pos = vec2(), size = vec2(39, .11), color = WHITE;
45
+ let pos = vec2(), size = vec2(39, .15), color = WHITE;
46
46
  for (let y=-h; y<h; y+=.1)
47
47
  {
48
48
  const p = 1.01-abs(y/h)
@@ -54,7 +54,7 @@ function gameRender()
54
54
  {
55
55
  // draw vertical slices to create the walls
56
56
  const w = 20;
57
- const pos = vec2(), size = vec2(.11), color = WHITE;
57
+ const pos = vec2(), size = vec2(.15), color = WHITE;
58
58
  for (let x=-w; x<w; x+=.1)
59
59
  {
60
60
  const p = raycast(playerPos, playerAngle + x/w);
@@ -1,24 +1,16 @@
1
1
  function gameRender()
2
2
  {
3
3
  // polygon shapes
4
- for(let j=3; j<7; ++j)
5
- {
6
- let points = [];
7
- for(let i=0; i<j; ++i)
8
- {
9
- let angle = i * Math.PI * 2 / j;
10
- points.push(vec2(j*4-18,0).add(vec2(Math.sin(angle), Math.cos(angle))));
11
- }
12
- drawPoly(points, hsl(j/4,1,.5)); // draw a star
13
- }
4
+ for (let j=3; j<7; ++j)
5
+ drawRegularPoly(vec2(j*4-18,0), vec2(1), j, hsl(j/4+time/9,1,.5), .2, WHITE, time);
14
6
 
15
7
  // circles and ellipses
16
- drawCircle(vec2(0,5), 1, hsl(.15,1,.5));
17
- drawEllipse(vec2(-5,5), 2, 1, 0, hsl(0,1,.5));
18
- drawEllipse(vec2(5,5), .5, 2, .5, hsl(.55,1,.5));
8
+ drawCircle(vec2(0,5), 1+wave(.5), hsl(.15,1,.5));
9
+ drawEllipse(vec2(-5,5), vec2(2,1), hsl(0,1,.5));
10
+ drawEllipse(vec2(5,5), vec2(.5,2), hsl(.55,1,.5), .5);
19
11
 
20
12
  // rects and lines
21
- drawRect(vec2(0,-5), vec2(13,2), hsl(.6,1,.5));
22
- drawLine(vec2(-5,-7), vec2(5,-3), 1, hsl(0,0,1));
13
+ drawRect(vec2(0,-5), vec2(4,3), hsl(.6,1,.5), time);
14
+ drawLine(vec2(-5,-7), vec2(5,-3), 1, hsl(0,0,1, .5));
23
15
  drawLine(vec2(-5,-3), vec2(5,-7), .2, hsl(1,1,.5));
24
16
  }