littlejsengine 1.14.7 → 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.
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
33
33
  * @type {string}
34
34
  * @default
35
35
  * @memberof Engine */
36
- const engineVersion = '1.14.7';
36
+ const engineVersion = '1.14.8';
37
37
 
38
38
  /** Frames per second to update
39
39
  * @type {number}
@@ -865,8 +865,11 @@ function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
865
865
  * @memberof Utilities */
866
866
  function isOverlapping(posA, sizeA, posB, sizeB=vec2())
867
867
  {
868
- return abs(posA.x - posB.x)*2 < sizeA.x + sizeB.x
869
- && abs(posA.y - posB.y)*2 < sizeA.y + sizeB.y;
868
+ const dx = (posA.x - posB.x)*2;
869
+ const dy = (posA.y - posB.y)*2;
870
+ const sx = sizeA.x + sizeB.x;
871
+ const sy = sizeA.y + sizeB.y;
872
+ return dx >= -sx && dx < sx && dy >= -sy && dy < sy;
870
873
  }
871
874
 
872
875
  /** Returns true if a line segment is intersecting an axis aligned box
@@ -2097,7 +2100,7 @@ function setGLEnable(enable)
2097
2100
  glCanvas.style.visibility = enable ? 'visible' : 'hidden';
2098
2101
  }
2099
2102
 
2100
- /** Set how many sided polygons to use when drawing circles and elipses with WebGL
2103
+ /** Set how many sided polygons to use when drawing circles and ellipses with WebGL
2101
2104
  * @param {number} sides
2102
2105
  * @memberof Settings */
2103
2106
  function setGLCircleSides(sides) { glCircleSides = sides; }
@@ -2308,7 +2311,7 @@ class EngineObject
2308
2311
  /** @property {Vector2} - World space position of the object */
2309
2312
  this.pos = pos.copy();
2310
2313
  /** @property {Vector2} - World space width and height of the object */
2311
- this.size = size;
2314
+ this.size = size.copy();
2312
2315
  /** @property {Vector2} - Size of object used for drawing, uses size if not set */
2313
2316
  this.drawSize = undefined;
2314
2317
  /** @property {TileInfo} - Tile info to render object (undefined is untextured) */
@@ -2316,7 +2319,7 @@ class EngineObject
2316
2319
  /** @property {number} - Angle to rotate the object */
2317
2320
  this.angle = angle;
2318
2321
  /** @property {Color} - Color to apply when rendered */
2319
- this.color = color;
2322
+ this.color = color.copy();
2320
2323
  /** @property {Color} - Additive color to apply when rendered */
2321
2324
  this.additiveColor = undefined;
2322
2325
  /** @property {boolean} - Should it flip along y axis when rendered */
@@ -2440,7 +2443,7 @@ class EngineObject
2440
2443
  for (const o of engineObjectsCollide)
2441
2444
  {
2442
2445
  // non solid objects don't collide with each other
2443
- if (!this.isSolid && !o.isSolid || o.destroyed || o.parent || o === this)
2446
+ if ((!this.isSolid && !o.isSolid) || o.destroyed || o.parent || o === this)
2444
2447
  continue;
2445
2448
 
2446
2449
  // check collision
@@ -2479,7 +2482,7 @@ class EngineObject
2479
2482
  {
2480
2483
  // push outside object collision
2481
2484
  this.pos.y = o.pos.y + (sizeBoth.y/2 + epsilon) * sign(oldPos.y - o.pos.y);
2482
- if (o.groundObject && wasMovingDown || !o.mass)
2485
+ if ((o.groundObject && wasMovingDown) || !o.mass)
2483
2486
  {
2484
2487
  // set ground object if landed on something
2485
2488
  if (wasMovingDown)
@@ -3630,7 +3633,7 @@ class FontImage
3630
3633
  * @param {Vector2} pos
3631
3634
  * @param {number} [scale=.25]
3632
3635
  * @param {boolean} [center]
3633
- * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}[context=drawContext]
3636
+ * @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
3634
3637
  */
3635
3638
  drawText(text, pos, scale=1, center, context=drawContext)
3636
3639
  {
@@ -3897,6 +3900,7 @@ function inputInit()
3897
3900
  document.addEventListener('wheel', onMouseWheel);
3898
3901
  document.addEventListener('contextmenu', onContextMenu);
3899
3902
  document.addEventListener('blur', onBlur);
3903
+ document.addEventListener('mouseleave', onMouseLeave);
3900
3904
 
3901
3905
  // init touch input
3902
3906
  if (isTouchDevice && touchInputEnable)
@@ -3955,6 +3959,12 @@ function inputInit()
3955
3959
  function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
3956
3960
  function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
3957
3961
  function onBlur() { inputClear(); } // reset input when focus is lost
3962
+ function onMouseLeave()
3963
+ {
3964
+ // set mouse position and delta when leaving canvas
3965
+ mousePosScreen = vec2(Infinity);
3966
+ mouseDeltaScreen = vec2(0);
3967
+ }
3958
3968
  }
3959
3969
 
3960
3970
  // convert a mouse or touch event position to screen space
@@ -5076,7 +5086,7 @@ class TileLayerData
5076
5086
  /** @property {boolean} - If the tile should be mirrored along the x axis */
5077
5087
  this.mirror = mirror;
5078
5088
  /** @property {Color} - Color of the tile */
5079
- this.color = color;
5089
+ this.color = color.copy();
5080
5090
  }
5081
5091
 
5082
5092
  /** Set this tile to clear, it will not be rendered */
@@ -5175,7 +5185,7 @@ class CanvasLayer extends EngineObject
5175
5185
  * @param {TileInfo} [tileInfo]
5176
5186
  * @param {Color} [color=(1,1,1,1)]
5177
5187
  * @param {number} [angle=0]
5178
- * @param {boolean} [mirror=0] */
5188
+ * @param {boolean} [mirror=false] */
5179
5189
  drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
5180
5190
  {
5181
5191
  this.drawCanvas2D(pos, size, angle, mirror, (context)=>
@@ -6773,7 +6783,7 @@ function glPolyStrip(points)
6773
6783
  while (indices.length > 3 && attempts++ < maxAttempts)
6774
6784
  {
6775
6785
  let foundEar = false;
6776
- for (let i = indices.length; --i;)
6786
+ for (let i = 0; i < indices.length; i++)
6777
6787
  {
6778
6788
  const i0 = indices[(i + indices.length - 1) % indices.length];
6779
6789
  const i1 = indices[i];
@@ -6810,7 +6820,7 @@ function glPolyStrip(points)
6810
6820
  if (!foundEar)
6811
6821
  {
6812
6822
  let worstIndex = -1, worstValue = Infinity;
6813
- for (let i = indices.length; --i;)
6823
+ for (let i = 0; i < indices.length; i++)
6814
6824
  {
6815
6825
  const i0 = indices[(i + indices.length - 1) % indices.length];
6816
6826
  const i1 = indices[i];
@@ -7373,6 +7383,10 @@ class UISystemPlugin
7373
7383
  this.uiObjects = [];
7374
7384
  /** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
7375
7385
  this.uiContext = context;
7386
+ /** @property {UIObject} - Top most object user is over */
7387
+ this.hoverObject = undefined;
7388
+ /** @property {UIObject} - Object user is currently interacting with */
7389
+ this.activeObject = undefined;
7376
7390
 
7377
7391
  engineAddPlugin(uiUpdate, uiRender);
7378
7392
 
@@ -7402,6 +7416,8 @@ class UISystemPlugin
7402
7416
  else
7403
7417
  updateInvisibleObject(o);
7404
7418
  }
7419
+ // reset hover object at start of update
7420
+ uiSystem.hoverObject = undefined;
7405
7421
  for (let i = uiSystem.uiObjects.length; i--;)
7406
7422
  {
7407
7423
  const o = uiSystem.uiObjects[i];
@@ -7513,6 +7529,8 @@ class UIObject
7513
7529
  this.size = size.copy();
7514
7530
  /** @property {Color} - Color of the object */
7515
7531
  this.color = uiSystem.defaultColor;
7532
+ /** @property {Color} - Color of the object when active, uses color if undefined */
7533
+ this.activeColor = undefined;
7516
7534
  /** @property {string} - Text for this ui object */
7517
7535
  this.text = undefined;
7518
7536
  /** @property {Color} - Color when disabled */
@@ -7543,7 +7561,7 @@ class UIObject
7543
7561
  this.children = [];
7544
7562
  /** @property {UIObject} - This object's parent, position is in parent space */
7545
7563
  this.parent = undefined;
7546
- /** @property {number} - Extra size added to make small buttons easier to touch on mobile devices */
7564
+ /** @property {number} - Added size to make small buttons easier to touch on mobile devices */
7547
7565
  this.extraTouchSize = 0;
7548
7566
  /** @property {Sound} - Sound when interactive element is pressed */
7549
7567
  this.soundPress = uiSystem.defaultSoundPress;
@@ -7551,12 +7569,10 @@ class UIObject
7551
7569
  this.soundRelease = uiSystem.defaultSoundRelease;
7552
7570
  /** @property {Sound} - Sound when interactive element is clicked */
7553
7571
  this.soundClick = uiSystem.defaultSoundClick;
7554
- /** @property {boolean} - Is the mouse over this element */
7555
- this.mouseIsOver = false;
7556
7572
  /** @property {boolean} - Is this element interactive */
7557
7573
  this.interactive = false;
7558
- /** @property {boolean} - Is the mouse held on this element (was pressed while over and hasn't been released) */
7559
- this.mouseIsHeld = false;
7574
+ /** @property {boolean} - Activate when dragged over with mouse held down */
7575
+ this.dragActivate = false;
7560
7576
  uiSystem.uiObjects.push(this);
7561
7577
  }
7562
7578
 
@@ -7583,15 +7599,18 @@ class UIObject
7583
7599
  /** Update the object, called automatically by plugin once each frame */
7584
7600
  update()
7585
7601
  {
7586
- const mouseWasOver = this.mouseIsOver;
7587
- const mousePress = mouseWasPressed(0);
7602
+ const wasHover = this.isHoverObject();
7603
+ const isActive = this.isActiveObject();
7588
7604
  const mouseDown = mouseIsDown(0);
7589
- if (mousePress || !mouseDown || this.mouseIsHeld)
7605
+ const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
7606
+ if (!uiSystem.hoverObject)
7607
+ if (mousePress || !mouseDown || isActive)
7590
7608
  {
7591
7609
  const size = this.size.add(vec2(isTouchDevice && this.extraTouchSize || 0));
7592
- this.mouseIsOver = isOverlapping(this.pos, size, mousePosScreen);
7610
+ if (isOverlapping(this.pos, size, mousePosScreen))
7611
+ uiSystem.hoverObject = this;
7593
7612
  }
7594
- if (this.mouseIsOver)
7613
+ if (this.isHoverObject())
7595
7614
  {
7596
7615
  if (mousePress)
7597
7616
  inputClearKey(0,0,0,1,0); // clear mouse was pressed state
@@ -7604,10 +7623,12 @@ class UIObject
7604
7623
  this.onPress();
7605
7624
  if (this.soundPress)
7606
7625
  this.soundPress.play();
7626
+ if (uiSystem.activeObject && !isActive)
7627
+ uiSystem.activeObject.onRelease();
7628
+ uiSystem.activeObject = this;
7607
7629
  }
7608
- this.mouseIsHeld = true;
7609
7630
  }
7610
- if (!mouseDown && this.mouseIsHeld && this.interactive)
7631
+ if (!mouseDown && uiSystem.activeObject === this && this.interactive)
7611
7632
  {
7612
7633
  this.onClick();
7613
7634
  if (this.soundClick)
@@ -7615,35 +7636,34 @@ class UIObject
7615
7636
  }
7616
7637
  }
7617
7638
  }
7618
- if (!mouseDown)
7639
+ if (isActive && (!mouseDown || !this.isHoverObject()))
7619
7640
  {
7620
- if (this.mouseIsHeld && this.interactive)
7621
- {
7622
- this.onRelease();
7623
- if (this.soundRelease)
7624
- this.soundRelease.play();
7625
- }
7626
- if (isTouchDevice)
7627
- this.mouseIsOver = false;
7628
- this.mouseIsHeld = false;
7641
+ this.onRelease();
7642
+ if (this.soundRelease)
7643
+ this.soundRelease.play();
7644
+ uiSystem.activeObject = undefined;
7629
7645
  }
7630
7646
 
7631
- if (this.mouseIsOver !== mouseWasOver)
7632
- this.mouseIsOver ? this.onEnter() : this.onLeave();
7647
+ if (this.isHoverObject() !== wasHover)
7648
+ this.isHoverObject() ? this.onEnter() : this.onLeave();
7633
7649
  }
7634
7650
 
7635
7651
  /** Render the object, called automatically by plugin once each frame */
7636
7652
  render()
7637
7653
  {
7638
- if (this.size.x && this.size.y)
7639
- uiSystem.drawRect(this.pos, this.size, this.color, this.lineWidth, this.lineColor, this.cornerRadius);
7654
+ if (!this.size.x || !this.size.y) return;
7655
+
7656
+ const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
7657
+ const color = this.interactive ? this.disabled ? this.disabledColor : this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
7658
+ uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
7640
7659
  }
7641
7660
 
7642
7661
  /** Special update when object is not visible */
7643
7662
  updateInvisible()
7644
7663
  {
7645
7664
  // reset input state when not visible
7646
- this.mouseIsOver = this.mouseIsHeld = false;
7665
+ if (this.isActiveObject())
7666
+ uiSystem.activeObject = undefined;
7647
7667
  }
7648
7668
 
7649
7669
  /** Get the size for text with overrides and scale
@@ -7656,6 +7676,12 @@ class UIObject
7656
7676
  this.textHeight || this.textScale * this.size.y);
7657
7677
  }
7658
7678
 
7679
+ /** @return {boolean} - Is the mouse hovering over this element */
7680
+ isHoverObject() { return uiSystem.hoverObject === this; }
7681
+
7682
+ /** @return {boolean} - Is the mouse held onto this element */
7683
+ isActiveObject() { return uiSystem.activeObject === this; }
7684
+
7659
7685
  /** Called when the mouse enters the object */
7660
7686
  onEnter() {}
7661
7687
 
@@ -7766,10 +7792,7 @@ class UIButton extends UIObject
7766
7792
  }
7767
7793
  render()
7768
7794
  {
7769
- // draw the button
7770
- const lineColor = this.mouseIsHeld && !this.disabled ? this.color : this.lineColor;
7771
- const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
7772
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
7795
+ super.render();
7773
7796
 
7774
7797
  // draw the text scaled to fit
7775
7798
  const textSize = this.getTextSize();
@@ -7810,8 +7833,7 @@ class UICheckbox extends UIObject
7810
7833
  }
7811
7834
  render()
7812
7835
  {
7813
- const color = this.disabled ? this.disabledColor : this.mouseIsOver ? this.hoverColor : this.color;
7814
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, this.lineColor, this.cornerRadius);
7836
+ super.render();
7815
7837
  if (this.checked)
7816
7838
  {
7817
7839
  const p = this.cornerRadius / min(this.size.x, this.size.y) * 2;
@@ -7861,7 +7883,7 @@ class UIScrollbar extends UIObject
7861
7883
  update()
7862
7884
  {
7863
7885
  super.update();
7864
- if (this.mouseIsHeld && this.interactive)
7886
+ if (this.isActiveObject() && this.interactive)
7865
7887
  {
7866
7888
  // check if value changed
7867
7889
  const handleSize = vec2(this.size.y);
@@ -7875,12 +7897,7 @@ class UIScrollbar extends UIObject
7875
7897
  }
7876
7898
  render()
7877
7899
  {
7878
- // draw the scrollbar background
7879
- const lineColor = this.interactive && this.mouseIsHeld && !this.disabled ?
7880
- this.color : this.lineColor;
7881
- const color = this.disabled ? this.disabledColor :
7882
- this.interactive && this.mouseIsHeld ? this.hoverColor : this.color;
7883
- uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
7900
+ super.render();
7884
7901
 
7885
7902
  // draw the scrollbar handle
7886
7903
  const handleSize = vec2(this.size.y);
@@ -7889,7 +7906,7 @@ class UIScrollbar extends UIObject
7889
7906
  const p2 = this.pos.x + handleWidth/2;
7890
7907
  const handlePos = vec2(lerp(p1, p2, this.value), this.pos.y);
7891
7908
  const handleColor = this.disabled ? this.disabledColor :
7892
- this.interactive && this.mouseIsHeld ? this.color : this.handleColor;
7909
+ this.interactive && this.isActiveObject() ? this.color : this.handleColor;
7893
7910
  uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
7894
7911
 
7895
7912
  // draw the text scaled to fit on the scrollbar
@@ -72,12 +72,12 @@ const exampleList =
72
72
  [
73
73
  new ExampleInfo('--- SHORT EXAMPLES ---'),
74
74
  new ExampleInfo('Hello World', 'helloWorld.js', 'Minimal starter example'),
75
- new ExampleInfo('Starfield', 'starfield.js', 'Animated parallax starfield'),
75
+ new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation'),
76
+ new ExampleInfo('Animation', 'animation.js', 'Sprite animation system'),
76
77
  new ExampleInfo('Shapes', 'shapes.js', 'Draw geometric shapes and primitives'),
77
78
  new ExampleInfo('Colors', 'colors.js', 'Color manipulation and HSL'),
78
- new ExampleInfo('Texture', 'texture.js', 'Texture display and manipulation'),
79
+ new ExampleInfo('Starfield', 'starfield.js', 'Animated parallax starfield'),
79
80
  new ExampleInfo('Sprite Atlas', 'spriteAtlas.js', 'Sprite atlas and tile rendering'),
80
- new ExampleInfo('Animation', 'animation.js', 'Sprite animation system'),
81
81
  new ExampleInfo('Blending', 'blending.js', 'Additive blending and transparency'),
82
82
  new ExampleInfo('Tile Layer', 'tileLayer.js', 'Tile layer rendering system'),
83
83
  new ExampleInfo('Particles', 'particles.js', 'Particle system'),
@@ -423,7 +423,7 @@ function setCode(code, filename)
423
423
  {
424
424
  // try to extract line number from stack trace
425
425
  // look for <anonymous> or injectedScript to find user code
426
- const anonymousMatch = stack.match(/(<anonymous>|injectedScript):(\d+)/);
426
+ const anonymousMatch = stack?.match(/(<anonymous>|injectedScript):(\d+)/);
427
427
  return anonymousMatch ? parseInt(anonymousMatch[2]) : -1;
428
428
  }
429
429
  iframeContent.onerror = (message, source, lineno, colno, error) =>
@@ -520,7 +520,12 @@ function setCode(code, filename)
520
520
  const errorLine = getErrorLine(error.stack);
521
521
  if (errorLine >= 0)
522
522
  setErrorLine(errorLine);
523
- 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);
524
529
  throw error;
525
530
  })
526
531
  .then(()=>
@@ -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
  }
@@ -1,38 +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]));
37
- canvasClearColor = hsl(.6,.3,.2); // background color
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]));
38
26
  }
@@ -10,22 +10,29 @@ function gameInit()
10
10
  uiSystem.defaultSoundClick = new Sound([1,0,440]);
11
11
  uiSystem.defaultCornerRadius = 8;
12
12
 
13
+ // example button that returns to menu
14
+ const buttonBack = new UIButton(mainCanvasSize.scale(.5), vec2(200), 'Back\nto\nMenu');
15
+ buttonBack.textHeight = 60;
16
+ buttonBack.onClick = ()=> uiMenu.visible = true;
17
+
13
18
  // setup example menu
14
- const uiMenu = new UIObject(mainCanvasSize.scale(.5), vec2(450));
19
+ const uiMenu = new UIObject(mainCanvasSize.scale(.5), vec2(600,450));
15
20
  canvasClearColor = hsl(0,0,.8);
16
21
 
17
22
  // example text
18
- const textTitle = new UIText(vec2(0,-120), vec2(400, 60), 'LittleJS UI\nSystem Demo');
19
- uiMenu.addChild(textTitle);
23
+ uiMenu.addChild(new UIText(vec2(-80,-120), vec2(500, 80), 'LittleJS UI\nSystem Demo'));
24
+
25
+ // example image
26
+ uiMenu.addChild(new UITile(vec2(200,-140), vec2(128), tile(3, 128)));
20
27
 
21
28
  // example button
22
- const button1 = new UIButton(vec2(70,0), vec2(140, 80), 'Test');
29
+ const button1 = new UIButton(vec2(80,0), vec2(140, 80), 'Test');
23
30
  button1.textHeight = 60;
24
31
  uiMenu.addChild(button1);
25
- button1.onClick = ()=> uiBackground.color = randColor();
32
+ button1.onClick = ()=> canvasClearColor = randColor();
26
33
 
27
34
  // example checkbox
28
- const checkbox = new UICheckbox(vec2(-70,0), vec2(50));
35
+ const checkbox = new UICheckbox(vec2(-80,0), vec2(50));
29
36
  checkbox.onChange = ()=> button1.disabled = checkbox.checked;
30
37
  uiMenu.addChild(checkbox);
31
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "littlejsengine",
3
- "version": "1.14.7",
3
+ "version": "1.14.8",
4
4
  "description": "LittleJS - Tiny and Fast HTML5 Game Engine",
5
5
  "main": "dist/littlejs.esm.js",
6
6
  "types": "dist/littlejs.d.ts",