littlejsengine 1.4.6 → 1.4.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.
package/engine/engine.js CHANGED
@@ -13,6 +13,7 @@
13
13
  - Particle effect system
14
14
  - Medal system tracks and displays achievements
15
15
  - Debug tools and debug rendering system
16
+ - Post processing effects
16
17
  - Call engineInit() to start it up!
17
18
  */
18
19
 
@@ -22,7 +23,7 @@
22
23
  const engineName = 'LittleJS';
23
24
 
24
25
  /** Version of engine */
25
- const engineVersion = '1.4.6';
26
+ const engineVersion = '1.4.8';
26
27
 
27
28
  /** Frames per second to update objects
28
29
  * @default */
@@ -47,18 +48,13 @@ let time = 0;
47
48
  /** Actual clock time since start in seconds (not affected by pause or frame rate clamping) */
48
49
  let timeReal = 0;
49
50
 
50
- /** Is the game paused? Causes time and objects to not be updated. */
51
+ /** Is the game paused? Causes time and objects to not be updated */
51
52
  let paused = 0;
52
53
 
53
- // Engine internal variables not exposed to documentation
54
- let frameTimeLastMS = 0, frameTimeBufferMS = 0, tileImageSize, tileImageFixBleed;
55
-
56
- // Engine stat tracking, if showWatermark is true
57
- let averageFPS, drawCount;
58
-
59
- // css text used for elements created by engine
60
- const styleBody = 'margin:0;overflow:hidden;background:#000';
61
- const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
54
+ /** Set if game is paused
55
+ * @param {Boolean} paused
56
+ */
57
+ function setPaused(_paused) { paused = _paused; }
62
58
 
63
59
  ///////////////////////////////////////////////////////////////////////////////
64
60
 
@@ -80,10 +76,13 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
80
76
  debug && (tileImage.onload=()=>ASSERT(1)); // tile sheet can not reloaded
81
77
 
82
78
  // setup html
79
+ const styleBody = 'margin:0;overflow:hidden;background:#000' + // fill the window
80
+ ';touch-action:none' + // prevent mobile pinch to resize
81
+ ';user-select:none' + // prevent mobile hold to select
82
+ ';-webkit-user-select:none'; // compatibility for ios
83
83
  document.body.style = styleBody;
84
84
  document.body.appendChild(mainCanvas = document.createElement('canvas'));
85
85
  mainContext = mainCanvas.getContext('2d');
86
- mainCanvas.style = styleCanvas;
87
86
 
88
87
  // init stuff and start engine
89
88
  debugInit();
@@ -92,21 +91,27 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
92
91
  // create overlay canvas for hud to appear above gl canvas
93
92
  document.body.appendChild(overlayCanvas = document.createElement('canvas'));
94
93
  overlayContext = overlayCanvas.getContext('2d');
95
- overlayCanvas.style = styleCanvas;
94
+
95
+ // set canvas style to fill the window
96
+ const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
97
+ (glCanvas||mainCanvas).style = mainCanvas.style = overlayCanvas.style = styleCanvas;
96
98
 
97
99
  gameInit();
98
100
  touchGamepadCreate();
99
101
  engineUpdate();
100
102
  };
101
103
 
104
+ // frame time tracking
105
+ let frameTimeLastMS = 0, frameTimeBufferMS, averageFPS;
106
+
102
107
  // main update loop
103
- const engineUpdate = (frameTimeMS=0)=>
108
+ function engineUpdate(frameTimeMS=0)
104
109
  {
105
110
  // update time keeping
106
111
  let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
107
112
  frameTimeLastMS = frameTimeMS;
108
113
  if (debug || showWatermark)
109
- averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
114
+ averageFPS = lerp(.05, averageFPS, 1e3/(frameTimeDeltaMS||1));
110
115
  const debugSpeedUp = debug && keyIsDown(107); // +
111
116
  const debugSpeedDown = debug && keyIsDown(109); // -
112
117
  if (debug)
@@ -118,24 +123,19 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
118
123
 
119
124
  if (canvasFixedSize.x)
120
125
  {
121
- // clear set fixed size
126
+ // clear canvas and set fixed size
122
127
  overlayCanvas.width = mainCanvas.width = canvasFixedSize.x;
123
128
  overlayCanvas.height = mainCanvas.height = canvasFixedSize.y;
124
129
 
125
130
  // fit to window by adding space on top or bottom if necessary
126
131
  const aspect = innerWidth / innerHeight;
127
132
  const fixedAspect = mainCanvas.width / mainCanvas.height;
128
- mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
129
- mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
130
- if (glCanvas)
131
- {
132
- glCanvas.style.width = mainCanvas.style.width;
133
- glCanvas.style.height = mainCanvas.style.height;
134
- }
133
+ (glCanvas||mainCanvas).style.width = mainCanvas.style.width = overlayCanvas.style.width = aspect < fixedAspect ? '100%' : '';
134
+ (glCanvas||mainCanvas).style.height = mainCanvas.style.height = overlayCanvas.style.height = aspect < fixedAspect ? '' : '100%';
135
135
  }
136
136
  else
137
137
  {
138
- // clear and set size to same as window
138
+ // clear canvas and set size to same as window
139
139
  overlayCanvas.width = mainCanvas.width = min(innerWidth, canvasMaxSize.x);
140
140
  overlayCanvas.height = mainCanvas.height = min(innerHeight, canvasMaxSize.y);
141
141
  }
@@ -202,7 +202,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
202
202
  overlayContext.fillStyle = '#000';
203
203
  const text = engineName + ' ' + 'v' + engineVersion + ' / '
204
204
  + drawCount + ' / ' + engineObjects.length + ' / ' + averageFPS.toFixed(1)
205
- + ' ' + (glEnable ? 'GL' : '2D') ;
205
+ + (glEnable ? ' GL' : ' 2D') ;
206
206
  overlayContext.fillText(text, mainCanvas.width-3, 3);
207
207
  overlayContext.fillStyle = '#fff';
208
208
  overlayContext.fillText(text, mainCanvas.width-2, 2);
@@ -216,7 +216,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
216
216
  tileImageSource ? tileImage.src = tileImageSource : tileImage.onload();
217
217
  }
218
218
 
219
- // called by engine to setup render system
219
+ // Called automatically by engine to setup render system
220
220
  function enginePreRender()
221
221
  {
222
222
  // save canvas size
@@ -226,12 +226,10 @@ function enginePreRender()
226
226
  mainContext.imageSmoothingEnabled = !cavasPixelated;
227
227
 
228
228
  // setup gl rendering if enabled
229
- glEnable && glPreRender(mainCanvas.width, mainCanvas.height, cameraPos.x, cameraPos.y, cameraScale);
229
+ glEnable && glPreRender();
230
230
  }
231
231
 
232
- ///////////////////////////////////////////////////////////////////////////////
233
-
234
- /** Calls update on each engine object (recursively if child), removes destroyed objects, and updated time */
232
+ /** Update each engine object, remove destroyed objects, and update time */
235
233
  function engineObjectsUpdate()
236
234
  {
237
235
  // get list of solid objects for physics optimzation
@@ -57,7 +57,7 @@ class Sound
57
57
  {
58
58
  if (!soundEnable) return;
59
59
 
60
- let pan = 0;
60
+ let pan;
61
61
  if (pos)
62
62
  {
63
63
  const range = this.range;
@@ -87,7 +87,7 @@ class Sound
87
87
  * @param {Number} [volume=1] - How much to scale volume by (in addition to range fade)
88
88
  * @return {AudioBufferSourceNode} - The audio, can be used to stop sound later
89
89
  */
90
- playNote(semitoneOffset, pos, volume=1)
90
+ playNote(semitoneOffset, pos, volume)
91
91
  {
92
92
  if (!soundEnable) return;
93
93
 
@@ -142,7 +142,7 @@ class Music
142
142
  * @param {Boolean} [loop=1] - True if the music should loop when it reaches the end
143
143
  * @return {AudioBufferSourceNode} - The audio node, can be used to stop sound later
144
144
  */
145
- play(volume = 1, loop = 1)
145
+ play(volume, loop = 1)
146
146
  {
147
147
  if (!soundEnable) return;
148
148
 
@@ -224,7 +224,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
224
224
 
225
225
  // create audio context
226
226
  if (!audioContext)
227
- audioContext = new (window.AudioContext||webkitAudioContext);
227
+ audioContext = new AudioContext;
228
228
 
229
229
  // fix stalled audio
230
230
  audioContext.resume();
@@ -243,18 +243,13 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
243
243
  source.playbackRate.value = rate;
244
244
  source.loop = loop;
245
245
 
246
- // create and connect gain node (createGain is more widley spported then GainNode construtor)
246
+ // create and connect gain node (createGain is more widely spported then GainNode construtor)
247
247
  const gainNode = audioContext.createGain();
248
248
  gainNode.gain.value = soundVolume*volume;
249
249
  gainNode.connect(audioContext.destination);
250
250
 
251
- // connect source to gain
252
- (
253
- window.StereoPannerNode ? // create pan node if possible
254
- source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
255
- : source
256
- )
257
- .connect(gainNode);
251
+ // connect source to stereo panner and gain
252
+ source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)})).connect(gainNode);
258
253
 
259
254
  // play and return sound
260
255
  source.start();
@@ -372,10 +367,8 @@ function zzfxG
372
367
  * @memberof Audio */
373
368
  function zzfxM(instruments, patterns, sequence, BPM = 125)
374
369
  {
370
+ let i, j, k;
375
371
  let instrumentParameters;
376
- let i;
377
- let j;
378
- let k;
379
372
  let note;
380
373
  let sample;
381
374
  let patternChannel;
@@ -112,9 +112,16 @@ echo.>> %OUTPUT_FILENAME%
112
112
 
113
113
  rem --- BUILD ENGINE MODULE RELEASE ---
114
114
 
115
- set OUTPUT_FILENAME=engine.all.release.module.js
115
+ set OUTPUT_FILENAME=engine.all.module.min.js
116
116
  del %OUTPUT_FILENAME%
117
- type engine.all.release.js >> %OUTPUT_FILENAME%
117
+ type engine.all.min.js >> %OUTPUT_FILENAME%
118
118
  echo.>> %OUTPUT_FILENAME%
119
119
  type engineExport.js >> %OUTPUT_FILENAME%
120
- echo.>> %OUTPUT_FILENAME%
120
+ echo.>> %OUTPUT_FILENAME%
121
+
122
+ rem lightly minify with uglify
123
+ call npx uglifyjs -o %OUTPUT_FILENAME% -- %OUTPUT_FILENAME%
124
+ if %ERRORLEVEL% NEQ 0 (
125
+ pause
126
+ exit /b %ERRORLEVEL%
127
+ )
@@ -22,11 +22,6 @@
22
22
 
23
23
  'use strict';
24
24
 
25
- /** Tile sheet for batch rendering system
26
- * @type {Image}
27
- * @memberof Draw */
28
- const tileImage = new Image();
29
-
30
25
  /** The primary 2D canvas visible to the user
31
26
  * @type {HTMLCanvasElement}
32
27
  * @memberof Draw */
@@ -52,6 +47,14 @@ let overlayContext;
52
47
  * @memberof Draw */
53
48
  let mainCanvasSize = vec2();
54
49
 
50
+ /** Tile sheet for batch rendering system
51
+ * @type {Image}
52
+ * @memberof Draw */
53
+ const tileImage = new Image;
54
+
55
+ // Engine internal variables not exposed to documentation
56
+ let tileImageSize, tileImageFixBleed, drawCount;
57
+
55
58
  /** Convert from screen to world space coordinates
56
59
  * - if calling outside of render, you may need to manually set mainCanvasSize
57
60
  * @param {Vector2} screenPos
@@ -76,7 +79,7 @@ const worldToScreen = (worldPos)=>
76
79
 
77
80
  /** Draw textured tile centered in world space, with color applied if using WebGL
78
81
  * @param {Vector2} pos - Center of the tile in world space
79
- * @param {Vector2} [size=new Vector2(1,1)] - Size of the tile in world space, width and height
82
+ * @param {Vector2} [size=new Vector2(1,1)] - Size of the tile in world space
80
83
  * @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
81
84
  * @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
82
85
  * @param {Color} [color=new Color(1,1,1)] - Color to modulate with
@@ -295,10 +298,10 @@ class FontImage
295
298
  */
296
299
  constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
297
300
  {
298
- if (!image && !engineFontImage)
301
+ if (!engineFontImage)
299
302
  {
300
303
  // load default font image
301
- engineFontImage = new Image();
304
+ engineFontImage = new Image;
302
305
  engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
303
306
  }
304
307
 
@@ -365,7 +368,7 @@ class FontImage
365
368
  /** Returns true if fullscreen mode is active
366
369
  * @return {Boolean}
367
370
  * @memberof Draw */
368
- const isFullscreen =()=> document.fullscreenElement;
371
+ const isFullscreen = ()=> document.fullscreenElement;
369
372
 
370
373
  /** Toggle fullsceen mode
371
374
  * @memberof Draw */
@@ -375,14 +378,7 @@ function toggleFullscreen()
375
378
  {
376
379
  if (document.exitFullscreen)
377
380
  document.exitFullscreen();
378
- else if (document.mozCancelFullScreen)
379
- document.mozCancelFullScreen();
380
- }
381
- else
382
- {
383
- if (document.body.webkitRequestFullScreen)
384
- document.body.webkitRequestFullScreen();
385
- else if (document.body.mozRequestFullScreen)
386
- document.body.mozRequestFullScreen();
387
381
  }
382
+ else if (document.body.requestFullscreen)
383
+ document.body.requestFullscreen();
388
384
  }
@@ -3,14 +3,15 @@
3
3
  * <br> - Export engine as a module with extra functions where necessary
4
4
  */
5
5
 
6
- // setters for all variables that devs will need to modify
6
+ // Setters for all variables that devs will need to modify
7
7
  const setCameraPos = (v)=> cameraPos = v;
8
8
  const setCameraScale = (v)=> cameraScale = v;
9
- const setRandSeed = (v)=> randSeed = v;
10
9
  const setCanvasMaxSize = (v)=> canvasMaxSize = v;
11
10
  const setCanvasFixedSize = (v)=> canvasFixedSize = v;
12
11
  const setCavasPixelated = (v)=> cavasPixelated = v;
13
12
  const setFontDefault = (v)=> fontDefault = v;
13
+ const setGlEnable = (v)=> glEnable = v;
14
+ const setGlOverlay = (v)=> glOverlay = v;
14
15
  const setTileSizeDefault = (v)=> tileSizeDefault = v;
15
16
  const setTileFixBleedScale = (v)=> tileFixBleedScale = v;
16
17
  const setObjectDefaultSize = (v)=> objectDefaultSize = v;
@@ -23,8 +24,6 @@ const setObjectDefaultFriction = (v)=> objectDefaultFriction = v;
23
24
  const setObjectMaxSpeed = (v)=> objectMaxSpeed = v;
24
25
  const setGravity = (v)=> gravity = v;
25
26
  const setParticleEmitRateScale = (v)=> particleEmitRateScale = v;
26
- const setGlEnable = (v)=> glEnable = v;
27
- const setGlOverlay = (v)=> glOverlay = v;
28
27
  const setGamepadsEnable = (v)=> gamepadsEnable = v;
29
28
  const setGamepadDirectionEmulateStick = (v)=> gamepadDirectionEmulateStick = v;
30
29
  const setInputWASDEmulateDirection = (v)=> inputWASDEmulateDirection = v;
@@ -43,18 +42,17 @@ const setMedalDisplayWidth = (v)=> medalDisplayWidth = v;
43
42
  const setMedalDisplayHeight = (v)=> medalDisplayHeight = v;
44
43
  const setMedalDisplayIconSize = (v)=> medalDisplayIconSize = v;
45
44
  const setMedalsPreventUnlock = (v)=> medalsPreventUnlock = v;
46
- const setShowWatermark = (v)=> showWatermark = v;
47
- const setGodMode = (v)=> godMode = v;
48
45
 
49
46
  export {
50
- // Custom methods
47
+ // Setters for global variables
51
48
  setCameraPos,
52
49
  setCameraScale,
53
- setRandSeed,
54
50
  setCanvasMaxSize,
55
51
  setCanvasFixedSize,
56
52
  setCavasPixelated,
57
53
  setFontDefault,
54
+ setGlEnable,
55
+ setGlOverlay,
58
56
  setTileSizeDefault,
59
57
  setTileFixBleedScale,
60
58
  setObjectDefaultSize,
@@ -67,8 +65,6 @@ export {
67
65
  setObjectMaxSpeed,
68
66
  setGravity,
69
67
  setParticleEmitRateScale,
70
- setGlEnable,
71
- setGlOverlay,
72
68
  setGamepadsEnable,
73
69
  setGamepadDirectionEmulateStick,
74
70
  setInputWASDEmulateDirection,
@@ -87,8 +83,6 @@ export {
87
83
  setMedalDisplayHeight,
88
84
  setMedalDisplayIconSize,
89
85
  setMedalsPreventUnlock,
90
- setShowWatermark,
91
- setGodMode,
92
86
 
93
87
  // Settings
94
88
  canvasMaxSize,
@@ -180,6 +174,7 @@ export {
180
174
  randVector,
181
175
  randColor,
182
176
  randSeed,
177
+ setRandSeed,
183
178
  randSeeded,
184
179
 
185
180
  // Utility Classes
@@ -244,15 +239,12 @@ export {
244
239
  // onmousemove,
245
240
  // onwheel,
246
241
  // oncontextmenu,
247
- mouseToScreen,
248
242
  //stickData,
243
+ mouseToScreen,
249
244
  gamepadsUpdate,
250
245
  vibrate,
251
246
  vibrateStop,
252
247
  isTouchDevice,
253
- //touchGamepadTimer,
254
- touchGamepadCreate,
255
- touchGamepadRender,
256
248
 
257
249
  // Audio
258
250
  Sound,
@@ -320,14 +312,9 @@ export {
320
312
  time,
321
313
  timeReal,
322
314
  paused,
323
- //frameTimeLastMS,
324
- //averageFPS,
325
- //drawCount,
326
- //styleBody,
327
- //styleCanvas,
315
+ setPaused,
328
316
  engineInit,
329
- //enginePreRender,
330
- //engineObjectsUpdate,
317
+ engineObjectsUpdate,
331
318
  engineObjectsDestroy,
332
319
  engineObjectsCallback,
333
320
  };
Binary file
@@ -14,7 +14,7 @@
14
14
  * @param {Number} [device=0]
15
15
  * @return {Boolean}
16
16
  * @memberof Input */
17
- const keyIsDown = (key, device=0)=> inputData[device] && inputData[device][key] & 1 ? 1 : 0;
17
+ const keyIsDown = (key, device=0)=> inputData[device] && inputData[device][key] & 1;
18
18
 
19
19
  /** Returns true if device key was pressed this frame
20
20
  * @param {Number} key
@@ -253,32 +253,41 @@ const isTouchDevice = window.ontouchstart !== undefined;
253
253
  // try to enable touch mouse
254
254
  if (isTouchDevice)
255
255
  {
256
- // handle all touch events the same way
257
- let wasTouching, hadTouch;
258
- ontouchstart = ontouchmove = ontouchend = (e)=>
256
+ // override mouse events
257
+ let wasTouching, mouseDown = onmousedown, mouseUp = onmouseup;
258
+ onmousedown = onmouseup = ()=> 0;
259
+
260
+ // setup touch input
261
+ ontouchstart = (e)=>
259
262
  {
260
- e.button = 0; // all touches are left click
263
+ // fix mobile audio, force it to play a sound on first touch
264
+ zzfx(0);
261
265
 
262
- // check if touching and pass to mouse events
263
- const touching = e.touches.length;
264
- if (touching)
266
+ // handle all touch events the same way
267
+ ontouchstart = ontouchmove = ontouchend = (e)=>
265
268
  {
266
- // fix mobile audio, force it to play a sound on first touch
267
- hadTouch || zzfx(0, hadTouch=1);
269
+ e.button = 0; // all touches are left click
268
270
 
269
- // set event pos and pass it along
270
- e.x = e.touches[0].clientX;
271
- e.y = e.touches[0].clientY;
272
- wasTouching ? onmousemove(e) : onmousedown(e);
273
- }
274
- else if (wasTouching)
275
- onmouseup(e);
271
+ // check if touching and pass to mouse events
272
+ const touching = e.touches.length;
273
+ if (touching)
274
+ {
275
+ // set event pos and pass it along
276
+ e.x = e.touches[0].clientX;
277
+ e.y = e.touches[0].clientY;
278
+ wasTouching ? onmousemove(e) : mouseDown(e);
279
+ }
280
+ else if (wasTouching)
281
+ mouseUp(e);
282
+
283
+ // set was touching
284
+ wasTouching = touching;
276
285
 
277
- // set was touching
278
- wasTouching = touching;
286
+ // must return true so the document will get focus
287
+ return true;
288
+ }
279
289
 
280
- // must return true so the document will get focus
281
- return true;
290
+ return ontouchstart(e);
282
291
  }
283
292
  }
284
293
 
@@ -286,7 +295,7 @@ if (isTouchDevice)
286
295
  // touch gamepad, virtual on screen gamepad emulator for touch devices
287
296
 
288
297
  // touch input internal variables
289
- let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2();
298
+ let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
290
299
 
291
300
  // create the touch gamepad, called automatically by the engine
292
301
  function touchGamepadCreate()
@@ -294,65 +303,73 @@ function touchGamepadCreate()
294
303
  if (!touchGamepadEnable || !isTouchDevice)
295
304
  return;
296
305
 
297
- ontouchstart = ontouchmove = ontouchend = (e)=>
306
+ // touch input internal variables
307
+ touchGamepadButtons = [];
308
+ touchGamepadStick = vec2();
309
+
310
+ // setup touch input
311
+ ontouchstart = (e)=>
298
312
  {
299
- if (!touchGamepadEnable)
300
- return;
313
+ // fix mobile audio, force it to play a sound on first touch
314
+ zzfx(0);
301
315
 
302
- // clear touch gamepad input
303
- touchGamepadStick = vec2();
304
- touchGamepadButtons = [];
305
-
306
- const touching = e.touches.length;
307
- if (touching)
316
+ ontouchstart = ontouchmove = ontouchend = (e)=>
308
317
  {
309
- touchGamepadTimer.isSet() || zzfx(0) ; // fix mobile audio, force it to play a sound the first time
310
-
311
- // set that gamepad is active
312
- isUsingGamepad = 1;
313
- touchGamepadTimer.set();
314
-
315
- if (paused)
318
+ // clear touch gamepad input
319
+ touchGamepadStick = vec2();
320
+ touchGamepadButtons = [];
321
+
322
+ const touching = e.touches.length;
323
+ if (touching)
316
324
  {
317
- // touch anywhere to press start when paused
318
- touchGamepadButtons[9] = 1;
319
- return;
325
+ // set that gamepad is active
326
+ isUsingGamepad = 1;
327
+ touchGamepadTimer.set();
328
+
329
+ if (paused)
330
+ {
331
+ // touch anywhere to press start when paused
332
+ touchGamepadButtons[9] = 1;
333
+ return;
334
+ }
320
335
  }
321
- }
322
336
 
323
- // get center of left and right sides
324
- const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
325
- const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
326
- const startCenter = mainCanvasSize.scale(.5);
337
+ // get center of left and right sides
338
+ const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
339
+ const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
340
+ const startCenter = mainCanvasSize.scale(.5);
327
341
 
328
- // check each touch point
329
- for (const touch of e.touches)
330
- {
331
- const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
332
- if (touchPos.distance(stickCenter) < touchGamepadSize)
342
+ // check each touch point
343
+ for (const touch of e.touches)
333
344
  {
334
- // virtual analog stick
335
- if (touchGamepadAnalog)
336
- touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
337
- else
345
+ const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
346
+ if (touchPos.distance(stickCenter) < touchGamepadSize)
338
347
  {
339
- // 8 way dpad
340
- const angle = touchPos.subtract(stickCenter).angle();
341
- touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
348
+ // virtual analog stick
349
+ if (touchGamepadAnalog)
350
+ touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
351
+ else
352
+ {
353
+ // 8 way dpad
354
+ const angle = touchPos.subtract(stickCenter).angle();
355
+ touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
356
+ }
357
+ }
358
+ else if (touchPos.distance(buttonCenter) < touchGamepadSize)
359
+ {
360
+ // virtual face buttons
361
+ const button = touchPos.subtract(buttonCenter).direction();
362
+ touchGamepadButtons[button] = 1;
363
+ }
364
+ else if (touchPos.distance(startCenter) < touchGamepadSize)
365
+ {
366
+ // virtual start button in center
367
+ touchGamepadButtons[9] = 1;
342
368
  }
343
- }
344
- else if (touchPos.distance(buttonCenter) < touchGamepadSize)
345
- {
346
- // virtual face buttons
347
- const button = touchPos.subtract(buttonCenter).direction();
348
- touchGamepadButtons[button] = 1;
349
- }
350
- else if (touchPos.distance(startCenter) < touchGamepadSize)
351
- {
352
- // virtual start button in center
353
- touchGamepadButtons[9] = 1;
354
369
  }
355
370
  }
371
+
372
+ return ontouchstart(e);
356
373
  }
357
374
  }
358
375