littlejsengine 1.2.0 → 1.2.3

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.
@@ -659,6 +659,11 @@ let inputWASDEmulateDirection = 1;
659
659
  * @memberof Settings */
660
660
  let touchGamepadEnable = 0;
661
661
 
662
+ /** True if touch gamepad should be analog stick or false to use if 8 way dpad
663
+ * @default
664
+ * @memberof Settings */
665
+ let touchGamepadAnalog = 1;
666
+
662
667
  /** Size of virutal gamepad for touch devices in pixels
663
668
  * @default
664
669
  * @memberof Settings */
@@ -748,7 +753,7 @@ let medalDisplayIconSize = 50;
748
753
  const engineName = 'LittleJS';
749
754
 
750
755
  /** Version of engine */
751
- const engineVersion = '1.2.0';
756
+ const engineVersion = '1.2.3';
752
757
 
753
758
  /** Frames per second to update objects
754
759
  * @default */
@@ -785,7 +790,7 @@ let averageFPS, drawCount;
785
790
  // css text used for elements created by engine
786
791
  const styleBody = 'margin:0;overflow:hidden;background:#000' +
787
792
  ';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
788
- const styleCanvas = 'position:absolute';
793
+ const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
789
794
 
790
795
  ///////////////////////////////////////////////////////////////////////////////
791
796
 
@@ -886,24 +891,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
886
891
 
887
892
  // fit to window by adding space on top or bottom if necessary
888
893
  const aspect = innerWidth / innerHeight;
889
- const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
890
-
891
- // clear css
892
- mainCanvas.style.width = overlayCanvas.style.width =
893
- mainCanvas.style.height = overlayCanvas.style.height =
894
- mainCanvas.style.top = overlayCanvas.style.top =
895
- mainCanvas.style.left = overlayCanvas.style.left = '';
896
-
897
- // letterbox center canvas
898
- if (aspect < fixedAspect)
899
- {
900
- mainCanvas.style.width = overlayCanvas.style.width = innerWidth;
901
- mainCanvas.style.top = overlayCanvas.style.top = (innerHeight - innerWidth/fixedAspect)/2;
902
- }
903
- else
894
+ const fixedAspect = mainCanvas.width / mainCanvas.height;
895
+ mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
896
+ mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
897
+ if (glCanvas)
904
898
  {
905
- mainCanvas.style.height = overlayCanvas.style.height = innerHeight;
906
- mainCanvas.style.left = overlayCanvas.style.left = (innerWidth - innerHeight*fixedAspect)/2;
899
+ glCanvas.style.width = mainCanvas.style.width;
900
+ glCanvas.style.height = mainCanvas.style.height;
907
901
  }
908
902
  }
909
903
  else
@@ -911,18 +905,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
911
905
  // clear and set size to same as window
912
906
  mainCanvas.width = min(innerWidth, canvasMaxSize.x);
913
907
  mainCanvas.height = min(innerHeight, canvasMaxSize.y);
914
-
915
- // center canvas
916
- overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
917
- overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
918
- }
919
-
920
- if (glEnable)
921
- {
922
- glCanvas.style.width = mainCanvas.style.width;
923
- glCanvas.style.height = mainCanvas.style.height;
924
- glCanvas.style.top = mainCanvas.style.top;
925
- glCanvas.style.left = mainCanvas.style.left;
926
908
  }
927
909
 
928
910
  // render sort then render while removing destroyed objects
@@ -1642,6 +1624,9 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
1642
1624
  * // draw text
1643
1625
  * font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
1644
1626
  */
1627
+
1628
+ let engineFontImage;
1629
+
1645
1630
  class FontImage
1646
1631
  {
1647
1632
  /** Create an image font
@@ -1653,14 +1638,14 @@ class FontImage
1653
1638
  */
1654
1639
  constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
1655
1640
  {
1656
- if (!image)
1641
+ if (!image && !engineFontImage)
1657
1642
  {
1658
- // use default font image
1659
- image = new Image();
1660
- image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6MCIBdFY+Q0PMNgf0yCgsSAaZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWw1hPWNRcEyWh2npdpBmSUBrRaN1WSlQAWXaWLF0kkwR7C/N7XUvIaQr4xpeqeQigjLwBRZ/6KkufAACcv5z7GYhvgWHII8xN/4C2hvLCkmZSVgIJMHNTxpYOsJOiitEjUQnIQAFFP+opoQfCbjWmfzWiNzCjEvxAPFARmw/Y5Te0sl2zEp4M+m7cToJjdJS7y1xcFlFT7EtWBZ8nl7uLSff7UgYewtlyY6yHM4RrjNrnsfn/W0DBK9xNZctZ7gsTeXTe5eqLqXFlKnAn+vkNXzH6TqPW10ksKoSlauKk1TcncZLSTxNnQ8Av/8RGPXOFIuMCZ/fpwIZx5vU4QIRQ44HwUyHx/QrZhm5VtXFex7ja2se0KBpnPT0DSwS4xd86uAy+SqnLBgkPY5JolhJ33ENyK6n0VyRzCnzebIkFdY/NUtM66lTdhix1KUss6q5snH5ftVJXG0QITb1gUDvzD3z/BbyOLMirwCtPzAAAAAElFTkSuQmCC';
1643
+ // load default font image
1644
+ engineFontImage = new Image();
1645
+ engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
1661
1646
  }
1662
1647
 
1663
- this.image = image;
1648
+ this.image = image || engineFontImage;
1664
1649
  this.tileSize = tileSize;
1665
1650
  this.paddingSize = paddingSize;
1666
1651
  this.startTileIndex = startTileIndex;
@@ -2027,7 +2012,7 @@ if (isTouchDevice)
2027
2012
  // touch gamepad, virtual on screen gamepad emulator for touch devices
2028
2013
 
2029
2014
  // touch input internal variables
2030
- let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2(), touchGamepadAnalog = 1;
2015
+ let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
2031
2016
 
2032
2017
  // create the touch gamepad, called automatically by the engine
2033
2018
  function touchGamepadCreate()
@@ -2074,7 +2059,7 @@ function touchGamepadCreate()
2074
2059
  {
2075
2060
  // virtual analog stick
2076
2061
  if (touchGamepadAnalog)
2077
- touchGamepadStick = touchPos.subtract(stickCenter).clampLength(touchGamepadSize/2).scale(2/touchGamepadSize);
2062
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
2078
2063
  else
2079
2064
  {
2080
2065
  // 8 way dpad
@@ -2115,12 +2100,28 @@ function touchGamepadRender()
2115
2100
  overlayContext.lineWidth = 3;
2116
2101
 
2117
2102
  // draw left analog stick
2118
- overlayContext.fillStyle = gamepadStick(0).length() > 0 ? '#fff' : '#000';
2103
+ overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
2119
2104
  overlayContext.beginPath();
2120
- overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
2121
- overlayContext.fill();
2122
- overlayContext.stroke();
2123
2105
 
2106
+ const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2107
+ if (touchGamepadAnalog)
2108
+ {
2109
+ overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
2110
+ overlayContext.fill();
2111
+ overlayContext.stroke();
2112
+ }
2113
+ else // draw cross shaped gamepad
2114
+ {
2115
+ for(let i=10; i--;)
2116
+ {
2117
+ const angle = i*PI/4;
2118
+ overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
2119
+ i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
2120
+ i==1 && overlayContext.fill();
2121
+ }
2122
+ overlayContext.stroke();
2123
+ }
2124
+
2124
2125
  // draw right face buttons
2125
2126
  const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
2126
2127
  for (let i=4; i--;)
@@ -2966,9 +2967,9 @@ class TileLayer extends EngineObject
2966
2967
  * let pos = vec2(2,3);
2967
2968
  * let particleEmiter = new ParticleEmitter
2968
2969
  * (
2969
- * pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
2970
+ * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
2970
2971
  * 0, vec2(16), // tileIndex, tileSize
2971
- * new Color, new Color(0,0,0), // colorStartA, colorStartB
2972
+ * new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
2972
2973
  * new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
2973
2974
  * 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
2974
2975
  * .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
@@ -2979,6 +2980,7 @@ class ParticleEmitter extends EngineObject
2979
2980
  {
2980
2981
  /** Create a particle system with the given settings
2981
2982
  * @param {Vector2} position - World space position of the emitter
2983
+ * @param {Number} [angle=0] - Angle to rotate the object
2982
2984
  * @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
2983
2985
  * @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
2984
2986
  * @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
@@ -3008,6 +3010,7 @@ class ParticleEmitter extends EngineObject
3008
3010
  constructor
3009
3011
  (
3010
3012
  pos,
3013
+ angle,
3011
3014
  emitSize = 0,
3012
3015
  emitTime = 0,
3013
3016
  emitRate = 100,
@@ -3035,7 +3038,7 @@ class ParticleEmitter extends EngineObject
3035
3038
  renderOrder = additive ? 1e9 : 0
3036
3039
  )
3037
3040
  {
3038
- super(pos, new Vector2, tileIndex, tileSize, 0, undefined, renderOrder);
3041
+ super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
3039
3042
 
3040
3043
  // emitter settings
3041
3044
  /** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
@@ -3468,9 +3471,6 @@ class Newgrounds
3468
3471
  * @param {Number} value - The score value */
3469
3472
  postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
3470
3473
 
3471
- /** Send message to log a view */
3472
- logView() { return this.call('App.logView', {'host':this.host}, 1); }
3473
-
3474
3474
  /** Get scores from a scoreboard
3475
3475
  * @param {Number} id - The scoreboard id
3476
3476
  * @param {String} [user=0] - A user's id or name
@@ -3482,6 +3482,9 @@ class Newgrounds
3482
3482
  getScores(id, user=0, social=0, skip=0, limit=10)
3483
3483
  { return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
3484
3484
 
3485
+ /** Send message to log a view */
3486
+ logView() { return this.call('App.logView', {'host':this.host}, 1); }
3487
+
3485
3488
  /** Send a message to call a component of the Newgrounds API
3486
3489
  * @param {String} component - Name of the component
3487
3490
  * @param {Object} [parameters=0] - Parameters to use for call
@@ -3578,24 +3581,24 @@ function glInit()
3578
3581
 
3579
3582
  // setup vertex and fragment shaders
3580
3583
  glShader = glCreateProgram(
3581
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3582
- 'uniform mat4 m;'+ // transform matrix
3583
- 'attribute vec2 p,t;'+ // position, uv
3584
- 'attribute vec4 c,a;'+ // color, additiveColor
3585
- 'varying vec2 v;'+ // return uv
3586
- 'varying vec4 d,e;'+ // return color, additiveColor
3587
- 'void main(){'+ // shader entry point
3588
- 'gl_Position=m*vec4(p,1,1);'+// transform position
3589
- 'v=t;d=c;e=a;'+ // pass stuff to fragment shader
3590
- '}' // end of shader
3584
+ 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3585
+ 'uniform mat4 m;'+ // transform matrix
3586
+ 'attribute vec2 p,t;'+ // position, uv
3587
+ 'attribute vec4 c,a;'+ // color, additiveColor
3588
+ 'varying vec2 v;'+ // return uv
3589
+ 'varying vec4 d,e;'+ // return color, additiveColor
3590
+ 'void main(){'+ // shader entry point
3591
+ 'gl_Position=m*vec4(p,1,1);'+ // transform position
3592
+ 'v=t;d=c;e=a;'+ // pass stuff to fragment shader
3593
+ '}' // end of shader
3591
3594
  ,
3592
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3593
- 'varying vec2 v;'+ // uv
3594
- 'varying vec4 d,e;'+ // color, additiveColor
3595
- 'uniform sampler2D j;'+ // texture
3596
- 'void main(){'+ // shader entry point
3597
- 'gl_FragColor=texture2D(j,v)*d+e;'+ // modulate texture by color plus additive
3598
- '}' // end of shader
3595
+ 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
3596
+ 'varying vec2 v;'+ // uv
3597
+ 'varying vec4 d,e;'+ // color, additiveColor
3598
+ 'uniform sampler2D s;'+ // texture
3599
+ 'void main(){'+ // shader entry point
3600
+ 'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
3601
+ '}' // end of shader
3599
3602
  );
3600
3603
 
3601
3604
  // init buffers
@@ -3807,44 +3810,44 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
3807
3810
 
3808
3811
  // prepare to create the verts from size and angle
3809
3812
  const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
3810
- const cX = c*sizeX, cY = c*sizeY, sX = s*sizeX, sY = s*sizeY;
3813
+ const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
3811
3814
 
3812
3815
  // setup 2 triangles to form a quad
3813
3816
  let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
3814
3817
 
3815
3818
  // vertex 0
3816
- glPositionData[offset++] = x - cX - sY;
3817
- glPositionData[offset++] = y - cY + sX;
3819
+ glPositionData[offset++] = x - cx - sy;
3820
+ glPositionData[offset++] = y - cy + sx;
3818
3821
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
3819
3822
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3820
3823
 
3821
3824
  // vertex 1
3822
- glPositionData[offset++] = x + cX + sY;
3823
- glPositionData[offset++] = y + cY - sX;
3825
+ glPositionData[offset++] = x + cx + sy;
3826
+ glPositionData[offset++] = y + cy - sx;
3824
3827
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
3825
3828
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3826
3829
 
3827
3830
  // vertex 2
3828
- glPositionData[offset++] = x - cX + sY;
3829
- glPositionData[offset++] = y + cY + sX;
3831
+ glPositionData[offset++] = x - cx + sy;
3832
+ glPositionData[offset++] = y + cy + sx;
3830
3833
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
3831
3834
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3832
3835
 
3833
3836
  // vertex 0
3834
- glPositionData[offset++] = x - cX - sY;
3835
- glPositionData[offset++] = y - cY + sX;
3837
+ glPositionData[offset++] = x - cx - sy;
3838
+ glPositionData[offset++] = y - cy + sx;
3836
3839
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
3837
3840
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3838
3841
 
3839
3842
  // vertex 3
3840
- glPositionData[offset++] = x + cX - sY;
3841
- glPositionData[offset++] = y - cY - sX;
3843
+ glPositionData[offset++] = x + cx - sy;
3844
+ glPositionData[offset++] = y - cy - sx;
3842
3845
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
3843
3846
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3844
3847
 
3845
3848
  // vertex 1
3846
- glPositionData[offset++] = x + cX + sY;
3847
- glPositionData[offset++] = y + cY - sX;
3849
+ glPositionData[offset++] = x + cx + sy;
3850
+ glPositionData[offset++] = y + cy - sx;
3848
3851
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
3849
3852
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
3850
3853
  }
package/engine/engine.js CHANGED
@@ -22,7 +22,7 @@
22
22
  const engineName = 'LittleJS';
23
23
 
24
24
  /** Version of engine */
25
- const engineVersion = '1.2.0';
25
+ const engineVersion = '1.2.3';
26
26
 
27
27
  /** Frames per second to update objects
28
28
  * @default */
@@ -59,7 +59,7 @@ let averageFPS, drawCount;
59
59
  // css text used for elements created by engine
60
60
  const styleBody = 'margin:0;overflow:hidden;background:#000' +
61
61
  ';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
62
- const styleCanvas = 'position:absolute';
62
+ const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
63
63
 
64
64
  ///////////////////////////////////////////////////////////////////////////////
65
65
 
@@ -160,24 +160,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
160
160
 
161
161
  // fit to window by adding space on top or bottom if necessary
162
162
  const aspect = innerWidth / innerHeight;
163
- const fixedAspect = canvasFixedSize.x / canvasFixedSize.y;
164
-
165
- // clear css
166
- mainCanvas.style.width = overlayCanvas.style.width =
167
- mainCanvas.style.height = overlayCanvas.style.height =
168
- mainCanvas.style.top = overlayCanvas.style.top =
169
- mainCanvas.style.left = overlayCanvas.style.left = '';
170
-
171
- // letterbox center canvas
172
- if (aspect < fixedAspect)
173
- {
174
- mainCanvas.style.width = overlayCanvas.style.width = innerWidth;
175
- mainCanvas.style.top = overlayCanvas.style.top = (innerHeight - innerWidth/fixedAspect)/2;
176
- }
177
- else
163
+ const fixedAspect = mainCanvas.width / mainCanvas.height;
164
+ mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
165
+ mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
166
+ if (glCanvas)
178
167
  {
179
- mainCanvas.style.height = overlayCanvas.style.height = innerHeight;
180
- mainCanvas.style.left = overlayCanvas.style.left = (innerWidth - innerHeight*fixedAspect)/2;
168
+ glCanvas.style.width = mainCanvas.style.width;
169
+ glCanvas.style.height = mainCanvas.style.height;
181
170
  }
182
171
  }
183
172
  else
@@ -185,18 +174,6 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
185
174
  // clear and set size to same as window
186
175
  mainCanvas.width = min(innerWidth, canvasMaxSize.x);
187
176
  mainCanvas.height = min(innerHeight, canvasMaxSize.y);
188
-
189
- // center canvas
190
- overlayCanvas.style.top = mainCanvas.style.top = (innerHeight - mainCanvas.height)/2;
191
- overlayCanvas.style.left = mainCanvas.style.left = (innerWidth - mainCanvas.width)/2;
192
- }
193
-
194
- if (glEnable)
195
- {
196
- glCanvas.style.width = mainCanvas.style.width;
197
- glCanvas.style.height = mainCanvas.style.height;
198
- glCanvas.style.top = mainCanvas.style.top;
199
- glCanvas.style.left = mainCanvas.style.left;
200
177
  }
201
178
 
202
179
  // render sort then render while removing destroyed objects
@@ -160,7 +160,7 @@ const debugUpdate = ()=>
160
160
  {
161
161
  if (!debug)
162
162
  return;
163
-
163
+
164
164
  if (keyWasPressed(192)) // ~
165
165
  debugOverlay = !debugOverlay;
166
166
  if (debugOverlay)
@@ -254,6 +254,9 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
254
254
  * // draw text
255
255
  * font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
256
256
  */
257
+
258
+ let engineFontImage;
259
+
257
260
  class FontImage
258
261
  {
259
262
  /** Create an image font
@@ -265,14 +268,14 @@ class FontImage
265
268
  */
266
269
  constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
267
270
  {
268
- if (!image)
271
+ if (!image && !engineFontImage)
269
272
  {
270
- // use default font image
271
- image = new Image();
272
- image.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6MCIBdFY+Q0PMNgf0yCgsSAaZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWw1hPWNRcEyWh2npdpBmSUBrRaN1WSlQAWXaWLF0kkwR7C/N7XUvIaQr4xpeqeQigjLwBRZ/6KkufAACcv5z7GYhvgWHII8xN/4C2hvLCkmZSVgIJMHNTxpYOsJOiitEjUQnIQAFFP+opoQfCbjWmfzWiNzCjEvxAPFARmw/Y5Te0sl2zEp4M+m7cToJjdJS7y1xcFlFT7EtWBZ8nl7uLSff7UgYewtlyY6yHM4RrjNrnsfn/W0DBK9xNZctZ7gsTeXTe5eqLqXFlKnAn+vkNXzH6TqPW10ksKoSlauKk1TcncZLSTxNnQ8Av/8RGPXOFIuMCZ/fpwIZx5vU4QIRQ44HwUyHx/QrZhm5VtXFex7ja2se0KBpnPT0DSwS4xd86uAy+SqnLBgkPY5JolhJ33ENyK6n0VyRzCnzebIkFdY/NUtM66lTdhix1KUss6q5snH5ftVJXG0QITb1gUDvzD3z/BbyOLMirwCtPzAAAAAElFTkSuQmCC';
273
+ // load default font image
274
+ engineFontImage = new Image();
275
+ engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
273
276
  }
274
277
 
275
- this.image = image;
278
+ this.image = image || engineFontImage;
276
279
  this.tileSize = tileSize;
277
280
  this.paddingSize = paddingSize;
278
281
  this.startTileIndex = startTileIndex;
Binary file
@@ -281,7 +281,7 @@ if (isTouchDevice)
281
281
  // touch gamepad, virtual on screen gamepad emulator for touch devices
282
282
 
283
283
  // touch input internal variables
284
- let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2(), touchGamepadAnalog = 1;
284
+ let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
285
285
 
286
286
  // create the touch gamepad, called automatically by the engine
287
287
  function touchGamepadCreate()
@@ -328,7 +328,7 @@ function touchGamepadCreate()
328
328
  {
329
329
  // virtual analog stick
330
330
  if (touchGamepadAnalog)
331
- touchGamepadStick = touchPos.subtract(stickCenter).clampLength(touchGamepadSize/2).scale(2/touchGamepadSize);
331
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
332
332
  else
333
333
  {
334
334
  // 8 way dpad
@@ -369,12 +369,28 @@ function touchGamepadRender()
369
369
  overlayContext.lineWidth = 3;
370
370
 
371
371
  // draw left analog stick
372
- overlayContext.fillStyle = gamepadStick(0).length() > 0 ? '#fff' : '#000';
372
+ overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
373
373
  overlayContext.beginPath();
374
- overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
375
- overlayContext.fill();
376
- overlayContext.stroke();
377
374
 
375
+ const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
376
+ if (touchGamepadAnalog)
377
+ {
378
+ overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
379
+ overlayContext.fill();
380
+ overlayContext.stroke();
381
+ }
382
+ else // draw cross shaped gamepad
383
+ {
384
+ for(let i=10; i--;)
385
+ {
386
+ const angle = i*PI/4;
387
+ overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
388
+ i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
389
+ i==1 && overlayContext.fill();
390
+ }
391
+ overlayContext.stroke();
392
+ }
393
+
378
394
  // draw right face buttons
379
395
  const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
380
396
  for (let i=4; i--;)
@@ -235,9 +235,6 @@ class Newgrounds
235
235
  * @param {Number} value - The score value */
236
236
  postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
237
237
 
238
- /** Send message to log a view */
239
- logView() { return this.call('App.logView', {'host':this.host}, 1); }
240
-
241
238
  /** Get scores from a scoreboard
242
239
  * @param {Number} id - The scoreboard id
243
240
  * @param {String} [user=0] - A user's id or name
@@ -249,6 +246,9 @@ class Newgrounds
249
246
  getScores(id, user=0, social=0, skip=0, limit=10)
250
247
  { return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
251
248
 
249
+ /** Send message to log a view */
250
+ logView() { return this.call('App.logView', {'host':this.host}, 1); }
251
+
252
252
  /** Send a message to call a component of the Newgrounds API
253
253
  * @param {String} component - Name of the component
254
254
  * @param {Object} [parameters=0] - Parameters to use for call
@@ -15,9 +15,9 @@
15
15
  * let pos = vec2(2,3);
16
16
  * let particleEmiter = new ParticleEmitter
17
17
  * (
18
- * pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
18
+ * pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
19
19
  * 0, vec2(16), // tileIndex, tileSize
20
- * new Color, new Color(0,0,0), // colorStartA, colorStartB
20
+ * new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
21
21
  * new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
22
22
  * 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
23
23
  * .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
@@ -28,6 +28,7 @@ class ParticleEmitter extends EngineObject
28
28
  {
29
29
  /** Create a particle system with the given settings
30
30
  * @param {Vector2} position - World space position of the emitter
31
+ * @param {Number} [angle=0] - Angle to rotate the object
31
32
  * @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
32
33
  * @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
33
34
  * @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
@@ -57,6 +58,7 @@ class ParticleEmitter extends EngineObject
57
58
  constructor
58
59
  (
59
60
  pos,
61
+ angle,
60
62
  emitSize = 0,
61
63
  emitTime = 0,
62
64
  emitRate = 100,
@@ -84,7 +86,7 @@ class ParticleEmitter extends EngineObject
84
86
  renderOrder = additive ? 1e9 : 0
85
87
  )
86
88
  {
87
- super(pos, new Vector2, tileIndex, tileSize, 0, undefined, renderOrder);
89
+ super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
88
90
 
89
91
  // emitter settings
90
92
  /** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
@@ -145,6 +145,11 @@ let inputWASDEmulateDirection = 1;
145
145
  * @memberof Settings */
146
146
  let touchGamepadEnable = 0;
147
147
 
148
+ /** True if touch gamepad should be analog stick or false to use if 8 way dpad
149
+ * @default
150
+ * @memberof Settings */
151
+ let touchGamepadAnalog = 1;
152
+
148
153
  /** Size of virutal gamepad for touch devices in pixels
149
154
  * @default
150
155
  * @memberof Settings */
@@ -47,24 +47,24 @@ function glInit()
47
47
 
48
48
  // setup vertex and fragment shaders
49
49
  glShader = glCreateProgram(
50
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
51
- 'uniform mat4 m;'+ // transform matrix
52
- 'attribute vec2 p,t;'+ // position, uv
53
- 'attribute vec4 c,a;'+ // color, additiveColor
54
- 'varying vec2 v;'+ // return uv
55
- 'varying vec4 d,e;'+ // return color, additiveColor
56
- 'void main(){'+ // shader entry point
57
- 'gl_Position=m*vec4(p,1,1);'+// transform position
58
- 'v=t;d=c;e=a;'+ // pass stuff to fragment shader
59
- '}' // end of shader
50
+ 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
51
+ 'uniform mat4 m;'+ // transform matrix
52
+ 'attribute vec2 p,t;'+ // position, uv
53
+ 'attribute vec4 c,a;'+ // color, additiveColor
54
+ 'varying vec2 v;'+ // return uv
55
+ 'varying vec4 d,e;'+ // return color, additiveColor
56
+ 'void main(){'+ // shader entry point
57
+ 'gl_Position=m*vec4(p,1,1);'+ // transform position
58
+ 'v=t;d=c;e=a;'+ // pass stuff to fragment shader
59
+ '}' // end of shader
60
60
  ,
61
- 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
62
- 'varying vec2 v;'+ // uv
63
- 'varying vec4 d,e;'+ // color, additiveColor
64
- 'uniform sampler2D j;'+ // texture
65
- 'void main(){'+ // shader entry point
66
- 'gl_FragColor=texture2D(j,v)*d+e;'+ // modulate texture by color plus additive
67
- '}' // end of shader
61
+ 'precision highp float;'+ // use highp for better accuracy, lowp for better perf
62
+ 'varying vec2 v;'+ // uv
63
+ 'varying vec4 d,e;'+ // color, additiveColor
64
+ 'uniform sampler2D s;'+ // texture
65
+ 'void main(){'+ // shader entry point
66
+ 'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
67
+ '}' // end of shader
68
68
  );
69
69
 
70
70
  // init buffers
@@ -276,44 +276,44 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
276
276
 
277
277
  // prepare to create the verts from size and angle
278
278
  const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
279
- const cX = c*sizeX, cY = c*sizeY, sX = s*sizeX, sY = s*sizeY;
279
+ const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
280
280
 
281
281
  // setup 2 triangles to form a quad
282
282
  let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
283
283
 
284
284
  // vertex 0
285
- glPositionData[offset++] = x - cX - sY;
286
- glPositionData[offset++] = y - cY + sX;
285
+ glPositionData[offset++] = x - cx - sy;
286
+ glPositionData[offset++] = y - cy + sx;
287
287
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
288
288
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
289
289
 
290
290
  // vertex 1
291
- glPositionData[offset++] = x + cX + sY;
292
- glPositionData[offset++] = y + cY - sX;
291
+ glPositionData[offset++] = x + cx + sy;
292
+ glPositionData[offset++] = y + cy - sx;
293
293
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
294
294
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
295
295
 
296
296
  // vertex 2
297
- glPositionData[offset++] = x - cX + sY;
298
- glPositionData[offset++] = y + cY + sX;
297
+ glPositionData[offset++] = x - cx + sy;
298
+ glPositionData[offset++] = y + cy + sx;
299
299
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
300
300
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
301
301
 
302
302
  // vertex 0
303
- glPositionData[offset++] = x - cX - sY;
304
- glPositionData[offset++] = y - cY + sX;
303
+ glPositionData[offset++] = x - cx - sy;
304
+ glPositionData[offset++] = y - cy + sx;
305
305
  glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
306
306
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
307
307
 
308
308
  // vertex 3
309
- glPositionData[offset++] = x + cX - sY;
310
- glPositionData[offset++] = y - cY - sX;
309
+ glPositionData[offset++] = x + cx - sy;
310
+ glPositionData[offset++] = y - cy - sx;
311
311
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
312
312
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
313
313
 
314
314
  // vertex 1
315
- glPositionData[offset++] = x + cX + sY;
316
- glPositionData[offset++] = y + cY - sX;
315
+ glPositionData[offset++] = x + cx + sy;
316
+ glPositionData[offset++] = y + cy - sx;
317
317
  glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
318
318
  glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
319
319
  }