littlejsengine 1.1.2 → 1.1.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.
- package/README.md +55 -0
- package/build.bat +2 -1
- package/engine/engine.all.js +1320 -930
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +965 -601
- package/engine/engine.js +71 -60
- package/engine/engineAudio.js +80 -22
- package/engine/engineBuild.bat +2 -2
- package/engine/engineDebug.js +65 -36
- package/engine/engineDraw.js +71 -56
- package/engine/engineInput.js +178 -28
- package/engine/engineMedals.js +66 -58
- package/engine/engineObject.js +86 -71
- package/engine/engineParticles.js +101 -63
- package/engine/engineRelease.js +4 -1
- package/engine/engineSettings.js +62 -53
- package/engine/engineTileLayer.js +99 -83
- package/engine/engineUtilities.js +95 -59
- package/engine/engineWebGL.js +53 -48
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +7 -5
- package/examples/platformer/game.js +7 -4
- package/examples/platformer/gameEffects.js +8 -11
- package/examples/platformer/gameLevel.js +15 -8
- package/examples/platformer/gameObjects.js +2 -2
- package/examples/platformer/gamePlayer.js +6 -6
- package/examples/platformer/index.html +10 -8
- package/examples/puzzle/game.js +3 -3
- package/examples/puzzle/index.html +6 -4
- package/examples/stress/index.html +7 -5
- package/game.js +10 -10
- package/index.html +17 -15
- package/package.json +4 -7
- package/tiles.png +0 -0
- package/LittleJS.zip +0 -0
- package/docs/Audio.html +0 -2264
- package/docs/Color.html +0 -2506
- package/docs/Debug.html +0 -2929
- package/docs/Draw.html +0 -3854
- package/docs/EngineObject.html +0 -2506
- package/docs/Input.html +0 -2478
- package/docs/Medal.html +0 -992
- package/docs/Medals.html +0 -624
- package/docs/Music.html +0 -569
- package/docs/Newgrounds.html +0 -1384
- package/docs/Particle.html +0 -567
- package/docs/ParticleEmitter.html +0 -1537
- package/docs/Random.html +0 -1770
- package/docs/Settings.html +0 -2885
- package/docs/Sound.html +0 -998
- package/docs/TileLayer.html +0 -3211
- package/docs/TileLayerData.html +0 -569
- package/docs/Timer.html +0 -1168
- package/docs/Utilities.html +0 -3266
- package/docs/Vector2.html +0 -3956
- package/docs/WebGL.html +0 -1842
- package/docs/engine.js.html +0 -428
- package/docs/engineAudio.js.html +0 -567
- package/docs/engineDebug.js.html +0 -721
- package/docs/engineDraw.js.html +0 -409
- package/docs/engineInput.js.html +0 -397
- package/docs/engineMedals.js.html +0 -445
- package/docs/engineObject.js.html +0 -492
- package/docs/engineParticles.js.html +0 -399
- package/docs/engineSettings.js.html +0 -358
- package/docs/engineTileLayer.js.html +0 -482
- package/docs/engineUtilities.js.html +0 -600
- package/docs/engineWebGL.js.html +0 -507
- package/docs/fonts/MavenPro-Regular.ttf +0 -0
- package/docs/fonts/Montserrat-Regular.ttf +0 -0
- package/docs/fonts/Muli-Black.ttf +0 -0
- package/docs/fonts/OFL-hind.txt +0 -93
- package/docs/fonts/OFL-montserrat.txt +0 -93
- package/docs/global.html +0 -1635
- package/docs/index.html +0 -243
- package/docs/scripts/fix-code-block.js +0 -53
- package/docs/scripts/fix-navbar.js +0 -25
- package/docs/scripts/linenumber.js +0 -25
- package/docs/scripts/misc.js +0 -217
- package/docs/scripts/resize.js +0 -85
- package/docs/scripts/search.js +0 -83
- package/docs/scripts/third-party/Apache-License-2.0.txt +0 -202
- package/docs/scripts/third-party/fuse.js +0 -9
- package/docs/scripts/third-party/lang-css.js +0 -2
- package/docs/scripts/third-party/prettify.js +0 -28
- package/docs/static/favicon.png +0 -0
- package/docs/styles/clean-jsdoc-theme-base.css +0 -393
- package/docs/styles/clean-jsdoc-theme-dark.css +0 -324
- package/docs/styles/clean-jsdoc-theme-light.css +0 -319
- package/docs/styles/reset.css +0 -346
- package/docs/styles/third-party/ionicons.min.css +0 -11
- package/docs/styles/third-party/prettify-jsdoc.css +0 -111
- package/docs/styles/third-party/prettify-tomorrow.css +0 -5
package/engine/engineDraw.js
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
2
|
+
* LittleJS Drawing System
|
|
3
|
+
* <br> - Hybrid with both Canvas2D and WebGL available
|
|
4
|
+
* <br> - Super fast tile sheet rendering with WebGL
|
|
5
|
+
* <br> - Can apply rotation, mirror, color and additive color
|
|
6
|
+
* <br> - Many useful utility functions
|
|
7
|
+
* <br>
|
|
8
|
+
* <br>LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
|
|
9
|
+
* <br>There are 3 canvas/contexts available to draw to...
|
|
10
|
+
* <br> - mainCanvas - 2D background canvas, non WebGL stuff like tile layers are drawn here.
|
|
11
|
+
* <br> - glCanvas - Used by the accelerated WebGL batch rendering system.
|
|
12
|
+
* <br> - overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
13
|
+
* <br>
|
|
14
|
+
* <br>The WebGL rendering system is very fast with some caveats...
|
|
15
|
+
* <br> - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
|
|
16
|
+
* <br> - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
17
|
+
* <br> - Group additive rendering together using renderOrder to mitigate this issue
|
|
18
|
+
* <br>
|
|
19
|
+
* <br>The LittleJS rendering solution is intentionally simple, feel free to adjust it for your needs!
|
|
20
|
+
* @namespace Draw
|
|
8
21
|
*/
|
|
9
22
|
|
|
10
23
|
'use strict';
|
|
11
24
|
|
|
12
|
-
/**
|
|
25
|
+
/** Tile sheet for batch rendering system
|
|
13
26
|
* @type {Image}
|
|
14
27
|
* @memberof Draw */
|
|
15
28
|
const tileImage = new Image();
|
|
@@ -34,7 +47,7 @@ let overlayCanvas;
|
|
|
34
47
|
* @memberof Draw */
|
|
35
48
|
let overlayContext;
|
|
36
49
|
|
|
37
|
-
/** The size of the main canvas (and other secondary canvases
|
|
50
|
+
/** The size of the main canvas (and other secondary canvases)
|
|
38
51
|
* @type {Vector2}
|
|
39
52
|
* @memberof Draw */
|
|
40
53
|
let mainCanvasSize = vec2();
|
|
@@ -53,42 +66,39 @@ const screenToWorld = (screenPos)=>
|
|
|
53
66
|
const worldToScreen = (worldPos)=>
|
|
54
67
|
worldPos.subtract(cameraPos).multiply(vec2(cameraScale,-cameraScale)).add(mainCanvasSize.scale(.5)).subtract(vec2(.5));
|
|
55
68
|
|
|
56
|
-
/** Draw textured tile centered
|
|
57
|
-
* @param {Vector2} pos
|
|
58
|
-
* @param {Vector2} [size=new Vector2(1,1)]
|
|
59
|
-
* @param {Number} [tileIndex=-1]
|
|
60
|
-
* @param {Vector2} [tileSize=
|
|
61
|
-
* @param {Color} [color=new Color(1,1,1)]
|
|
62
|
-
* @param {Number} [angle=0]
|
|
63
|
-
* @param {Boolean} [mirror=0]
|
|
64
|
-
* @param {Color} [additiveColor=new Color(0,0,0,0)]
|
|
69
|
+
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
70
|
+
* @param {Vector2} pos - Center of the tile in world space
|
|
71
|
+
* @param {Vector2} [size=new Vector2(1,1)] - Size of the tile in world space, width and height
|
|
72
|
+
* @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
|
|
73
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
74
|
+
* @param {Color} [color=new Color(1,1,1)] - Color to modulate with
|
|
75
|
+
* @param {Number} [angle=0] - Angle to rotate by
|
|
76
|
+
* @param {Boolean} [mirror=0] - If true image is flipped along the Y axis
|
|
77
|
+
* @param {Color} [additiveColor=new Color(0,0,0,0)] - Additive color to be applied
|
|
78
|
+
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
65
79
|
* @memberof Draw */
|
|
66
|
-
function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=
|
|
67
|
-
additiveColor=new Color(0,0,0,0))
|
|
80
|
+
function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color, angle=0, mirror,
|
|
81
|
+
additiveColor=new Color(0,0,0,0), useWebGL=glEnable)
|
|
68
82
|
{
|
|
69
83
|
showWatermark && ++drawCount;
|
|
70
|
-
if (glEnable)
|
|
84
|
+
if (glEnable && useWebGL)
|
|
71
85
|
{
|
|
72
|
-
if (tileIndex < 0)
|
|
86
|
+
if (tileIndex < 0 || !tileImage.width)
|
|
73
87
|
{
|
|
74
|
-
// if negative tile index, force untextured
|
|
88
|
+
// if negative tile index or image not found, force untextured
|
|
75
89
|
glDraw(pos.x, pos.y, size.x, size.y, angle, 0, 0, 0, 0, 0, color.rgbaInt());
|
|
76
90
|
}
|
|
77
91
|
else
|
|
78
92
|
{
|
|
79
93
|
// calculate uvs and render
|
|
80
|
-
const cols =
|
|
81
|
-
const uvSizeX = tileSize.x
|
|
82
|
-
const uvSizeY = tileSize.y
|
|
94
|
+
const cols = tileImageSize.x / tileSize.x |0;
|
|
95
|
+
const uvSizeX = tileSize.x / tileImageSize.x;
|
|
96
|
+
const uvSizeY = tileSize.y / tileImageSize.y;
|
|
83
97
|
const uvX = (tileIndex%cols)*uvSizeX, uvY = (tileIndex/cols|0)*uvSizeY;
|
|
84
|
-
|
|
85
|
-
// shrink uvs to prevent bleeding
|
|
86
|
-
const shrinkTilesX = tileBleedShrinkFix * tileImageSizeInverse.x;
|
|
87
|
-
const shrinkTilesY = tileBleedShrinkFix * tileImageSizeInverse.y;
|
|
88
98
|
|
|
89
99
|
glDraw(pos.x, pos.y, mirror ? -size.x : size.x, size.y, angle,
|
|
90
|
-
uvX +
|
|
91
|
-
uvX -
|
|
100
|
+
uvX + tileImageFixBleed.x, uvY + tileImageFixBleed.y,
|
|
101
|
+
uvX - tileImageFixBleed.x + uvSizeX, uvY - tileImageFixBleed.y + uvSizeY,
|
|
92
102
|
color.rgbaInt(), additiveColor.rgbaInt());
|
|
93
103
|
}
|
|
94
104
|
}
|
|
@@ -106,11 +116,11 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=defaultTileSize, col
|
|
|
106
116
|
else
|
|
107
117
|
{
|
|
108
118
|
// calculate uvs and render
|
|
109
|
-
const cols =
|
|
110
|
-
const sX = (tileIndex%cols)*tileSize.x +
|
|
111
|
-
const sY = (tileIndex/cols|0)*tileSize.y +
|
|
112
|
-
const sWidth = tileSize.x - 2*
|
|
113
|
-
const sHeight = tileSize.y - 2*
|
|
119
|
+
const cols = tileImageSize.x / tileSize.x |0;
|
|
120
|
+
const sX = (tileIndex%cols)*tileSize.x + tileFixBleedScale;
|
|
121
|
+
const sY = (tileIndex/cols|0)*tileSize.y + tileFixBleedScale;
|
|
122
|
+
const sWidth = tileSize.x - 2*tileFixBleedScale;
|
|
123
|
+
const sHeight = tileSize.y - 2*tileFixBleedScale;
|
|
114
124
|
context.globalAlpha = color.a; // only alpha is supported
|
|
115
125
|
context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
|
|
116
126
|
}
|
|
@@ -118,41 +128,44 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=defaultTileSize, col
|
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
/** Draw colored
|
|
131
|
+
/** Draw colored rect centered on pos
|
|
122
132
|
* @param {Vector2} pos
|
|
123
133
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
124
134
|
* @param {Color} [color=new Color(1,1,1)]
|
|
125
135
|
* @param {Number} [angle=0]
|
|
136
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
126
137
|
* @memberof Draw */
|
|
127
|
-
function drawRect(pos, size, color, angle)
|
|
138
|
+
function drawRect(pos, size, color, angle, useWebGL)
|
|
128
139
|
{
|
|
129
|
-
drawTile(pos, size, -1,
|
|
140
|
+
drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, 0, useWebGL);
|
|
130
141
|
}
|
|
131
142
|
|
|
132
143
|
/** Draw textured tile centered on pos in screen space
|
|
133
|
-
* @param {Vector2} pos
|
|
134
|
-
* @param {Vector2} [size=new Vector2(1,1)]
|
|
135
|
-
* @param {Number} [tileIndex=-1]
|
|
136
|
-
* @param {Vector2} [tileSize=
|
|
144
|
+
* @param {Vector2} pos - Center of the tile
|
|
145
|
+
* @param {Vector2} [size=new Vector2(1,1)] - Size of the tile
|
|
146
|
+
* @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
|
|
147
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
137
148
|
* @param {Color} [color=new Color]
|
|
138
149
|
* @param {Number} [angle=0]
|
|
139
150
|
* @param {Boolean} [mirror=0]
|
|
140
151
|
* @param {Color} [additiveColor=new Color(0,0,0,0)]
|
|
152
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
141
153
|
* @memberof Draw */
|
|
142
|
-
function drawTileScreenSpace(pos, size=vec2(1), tileIndex, tileSize, color, angle, mirror, additiveColor)
|
|
154
|
+
function drawTileScreenSpace(pos, size=vec2(1), tileIndex, tileSize, color, angle, mirror, additiveColor, useWebGL)
|
|
143
155
|
{
|
|
144
|
-
drawTile(screenToWorld(pos), size.scale(1/cameraScale), tileIndex, tileSize, color, angle, mirror, additiveColor);
|
|
156
|
+
drawTile(screenToWorld(pos), size.scale(1/cameraScale), tileIndex, tileSize, color, angle, mirror, additiveColor, useWebGL);
|
|
145
157
|
}
|
|
146
158
|
|
|
147
|
-
/** Draw colored
|
|
159
|
+
/** Draw colored rectangle in screen space
|
|
148
160
|
* @param {Vector2} pos
|
|
149
161
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
150
162
|
* @param {Color} [color=new Color(1,1,1)]
|
|
151
163
|
* @param {Number} [angle=0]
|
|
164
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
152
165
|
* @memberof Draw */
|
|
153
|
-
function drawRectScreenSpace(pos, size, color, angle)
|
|
166
|
+
function drawRectScreenSpace(pos, size, color, angle, useWebGL)
|
|
154
167
|
{
|
|
155
|
-
|
|
168
|
+
drawTileSrceenSpace(pos, size, -1, tileSizeDefault, color, angle, 0, 0, useWebGL);
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
/** Draw colored line between two points
|
|
@@ -160,15 +173,16 @@ function drawRectScreenSpace(pos, size, color, angle)
|
|
|
160
173
|
* @param {Vector2} posB
|
|
161
174
|
* @param {Number} [thickness=.1]
|
|
162
175
|
* @param {Color} [color=new Color(1,1,1)]
|
|
176
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
163
177
|
* @memberof Draw */
|
|
164
|
-
function drawLine(posA, posB, thickness=.1, color)
|
|
178
|
+
function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
165
179
|
{
|
|
166
180
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
167
181
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
168
|
-
drawRect(posA.add(halfDelta), size, color, halfDelta.angle());
|
|
182
|
+
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), 0, 0, useWebGL);
|
|
169
183
|
}
|
|
170
184
|
|
|
171
|
-
/** Draw directly to a 2d canvas context in world space
|
|
185
|
+
/** Draw directly to a 2d canvas context in world space
|
|
172
186
|
* @param {Vector2} pos
|
|
173
187
|
* @param {Vector2} size
|
|
174
188
|
* @param {Number} angle
|
|
@@ -184,7 +198,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
184
198
|
context.save();
|
|
185
199
|
context.translate(pos.x+.5|0, pos.y-.5|0);
|
|
186
200
|
context.rotate(angle);
|
|
187
|
-
context.scale(mirror
|
|
201
|
+
context.scale(mirror ? -size.x : size.x, size.y);
|
|
188
202
|
drawFunction(context);
|
|
189
203
|
context.restore();
|
|
190
204
|
}
|
|
@@ -198,7 +212,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
198
212
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
199
213
|
* @param {String} [textAlign='center']
|
|
200
214
|
* @memberof Draw */
|
|
201
|
-
function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=
|
|
215
|
+
function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
202
216
|
{
|
|
203
217
|
pos = worldToScreen(pos);
|
|
204
218
|
overlayContext.font = size*cameraScale + 'px '+ font;
|
|
@@ -214,12 +228,13 @@ function drawText(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new
|
|
|
214
228
|
overlayContext.fillText(text, pos.x, pos.y);
|
|
215
229
|
}
|
|
216
230
|
|
|
217
|
-
/** Enable
|
|
231
|
+
/** Enable normal or additive blend mode
|
|
218
232
|
* @param {Boolean} [additive=0]
|
|
233
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
219
234
|
* @memberof Draw */
|
|
220
|
-
function setBlendMode(additive)
|
|
235
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
221
236
|
{
|
|
222
|
-
if (glEnable)
|
|
237
|
+
if (glEnable && useWebGL)
|
|
223
238
|
glSetBlendMode(additive);
|
|
224
239
|
else
|
|
225
240
|
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
package/engine/engineInput.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* LittleJS Input System
|
|
3
|
+
* <br> - Tracks key down, pressed, and released
|
|
4
|
+
* <br> - Also tracks mouse buttons, position, and wheel
|
|
5
|
+
* <br> - Supports multiple gamepads
|
|
6
|
+
* <br> - Virtual gamepad for touch devices with touchGamepadSize
|
|
7
|
+
* @namespace Input
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
'use strict';
|
|
@@ -31,7 +32,7 @@ const keyWasReleased = (key, device=0)=> inputData[device] && inputData[device][
|
|
|
31
32
|
|
|
32
33
|
/** Clears all input
|
|
33
34
|
* @memberof Input */
|
|
34
|
-
const clearInput = ()=> inputData
|
|
35
|
+
const clearInput = ()=> inputData = [[]];
|
|
35
36
|
|
|
36
37
|
/** Returns true if mouse button is down
|
|
37
38
|
* @param {Number} button
|
|
@@ -67,7 +68,7 @@ let mouseWheel = 0;
|
|
|
67
68
|
|
|
68
69
|
/** Returns true if user is using gamepad (has more recently pressed a gamepad button)
|
|
69
70
|
* @memberof Input */
|
|
70
|
-
let
|
|
71
|
+
let isUsingGamepad = 0;
|
|
71
72
|
|
|
72
73
|
/** Returns true if gamepad button is down
|
|
73
74
|
* @param {Number} button
|
|
@@ -100,7 +101,9 @@ const gamepadStick = (stick, gamepad=0)=> stickData[gamepad] ? stickData[gamepa
|
|
|
100
101
|
///////////////////////////////////////////////////////////////////////////////
|
|
101
102
|
// Input update called by engine
|
|
102
103
|
|
|
103
|
-
|
|
104
|
+
// store input as a bit field for each key: 1 = isDown, 2 = wasPressed, 4 = wasReleased
|
|
105
|
+
// mouse and keyboard are stored together in device 0, gamepads are in devices > 0
|
|
106
|
+
let inputData = [[]];
|
|
104
107
|
|
|
105
108
|
function inputUpdate()
|
|
106
109
|
{
|
|
@@ -126,34 +129,38 @@ function inputUpdatePost()
|
|
|
126
129
|
///////////////////////////////////////////////////////////////////////////////
|
|
127
130
|
// Keyboard event handlers
|
|
128
131
|
|
|
129
|
-
onkeydown = e=>
|
|
132
|
+
onkeydown = (e)=>
|
|
130
133
|
{
|
|
131
134
|
if (debug && e.target != document.body) return;
|
|
132
|
-
e.repeat || (inputData[
|
|
135
|
+
e.repeat || (inputData[isUsingGamepad = 0][remapKeyCode(e.keyCode)] = 3);
|
|
133
136
|
debug || e.preventDefault();
|
|
134
137
|
}
|
|
135
|
-
onkeyup = e=>
|
|
138
|
+
onkeyup = (e)=>
|
|
136
139
|
{
|
|
137
140
|
if (debug && e.target != document.body) return;
|
|
138
141
|
inputData[0][remapKeyCode(e.keyCode)] = 4;
|
|
139
142
|
}
|
|
140
|
-
const remapKeyCode = c=>
|
|
143
|
+
const remapKeyCode = (c)=> inputWASDEmulateDirection ? c==87?38 : c==83?40 : c==65?37 : c==68?39 : c : c;
|
|
141
144
|
|
|
142
145
|
///////////////////////////////////////////////////////////////////////////////
|
|
143
146
|
// Mouse event handlers
|
|
144
147
|
|
|
145
|
-
onmousedown = e=> {inputData[
|
|
146
|
-
onmouseup = e=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
147
|
-
onmousemove = e=>
|
|
148
|
+
onmousedown = (e)=> {inputData[isUsingGamepad = 0][e.button] = 3; onmousemove(e); e.button && e.preventDefault();}
|
|
149
|
+
onmouseup = (e)=> inputData[0][e.button] = inputData[0][e.button] & 2 | 4;
|
|
150
|
+
onmousemove = (e)=> mousePosScreen = mouseToScreen(e);
|
|
151
|
+
onwheel = (e)=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
|
|
152
|
+
oncontextmenu = (e)=> !1; // prevent right click menu
|
|
153
|
+
|
|
154
|
+
// convert a mouse or touch event position to screen space
|
|
155
|
+
const mouseToScreen = (mousePos)=>
|
|
148
156
|
{
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
if (!mainCanvas)
|
|
158
|
+
return vec2(); // fix bug that can occur if user clicks before page loads
|
|
159
|
+
|
|
151
160
|
const rect = mainCanvas.getBoundingClientRect();
|
|
152
|
-
|
|
153
|
-
|
|
161
|
+
return mainCanvasSize.multiply(
|
|
162
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
154
163
|
}
|
|
155
|
-
onwheel = e=> e.ctrlKey || (mouseWheel = sign(e.deltaY));
|
|
156
|
-
oncontextmenu = e=> !1; // prevent right click menu
|
|
157
164
|
|
|
158
165
|
///////////////////////////////////////////////////////////////////////////////
|
|
159
166
|
// Gamepad input
|
|
@@ -161,6 +168,21 @@ oncontextmenu = e=> !1; // prevent right click menu
|
|
|
161
168
|
const stickData = [];
|
|
162
169
|
function gamepadsUpdate()
|
|
163
170
|
{
|
|
171
|
+
if (touchGamepadEnable && touchGamepadTimer.isSet())
|
|
172
|
+
{
|
|
173
|
+
// read virtual analog stick
|
|
174
|
+
const sticks = stickData[0] || (stickData[0] = []);
|
|
175
|
+
sticks[0] = vec2(touchGamepadStick.x, -touchGamepadStick.y); // flip vertical
|
|
176
|
+
|
|
177
|
+
// read virtual gamepad buttons
|
|
178
|
+
const data = inputData[1] || (inputData[1] = []);
|
|
179
|
+
for (let i=10; i--;)
|
|
180
|
+
{
|
|
181
|
+
const j = i == 3 ? 2 : i == 2 ? 3 : i; // fix button locations
|
|
182
|
+
data[j] = touchGamepadButtons[i] ? 1 + 2*!gamepadIsDown(j,0) : 4*gamepadIsDown(j,0);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
164
186
|
if (!gamepadsEnable || !navigator.getGamepads || !document.hasFocus() && !debug)
|
|
165
187
|
return;
|
|
166
188
|
|
|
@@ -178,8 +200,8 @@ function gamepadsUpdate()
|
|
|
178
200
|
// read clamp dead zone of analog sticks
|
|
179
201
|
const deadZone = .3, deadZoneMax = .8;
|
|
180
202
|
const applyDeadZone = (v)=>
|
|
181
|
-
v > deadZone ? percent( v,
|
|
182
|
-
v < -deadZone ? -percent(-v,
|
|
203
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
204
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
183
205
|
|
|
184
206
|
// read analog sticks
|
|
185
207
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -190,10 +212,11 @@ function gamepadsUpdate()
|
|
|
190
212
|
{
|
|
191
213
|
const button = gamepad.buttons[j];
|
|
192
214
|
data[j] = button.pressed ? 1 + 2*!gamepadIsDown(j,i) : 4*gamepadIsDown(j,i);
|
|
193
|
-
|
|
215
|
+
isUsingGamepad |= !i && button.pressed;
|
|
216
|
+
touchGamepadEnable && touchGamepadTimer.unset(); // disable touch gamepad if using real gamepad
|
|
194
217
|
}
|
|
195
|
-
|
|
196
|
-
if (
|
|
218
|
+
|
|
219
|
+
if (gamepadDirectionEmulateStick)
|
|
197
220
|
{
|
|
198
221
|
// copy dpad to left analog stick when pressed
|
|
199
222
|
const dpad = vec2(gamepadIsDown(15,i) - gamepadIsDown(14,i), gamepadIsDown(12,i) - gamepadIsDown(13,i));
|
|
@@ -204,18 +227,31 @@ function gamepadsUpdate()
|
|
|
204
227
|
}
|
|
205
228
|
}
|
|
206
229
|
|
|
230
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
231
|
+
|
|
232
|
+
/** Pulse the vibration hardware if it exists
|
|
233
|
+
* @param {Number} [pattern=100] - a single value in miliseconds or vibration interval array
|
|
234
|
+
* @memberof Input */
|
|
235
|
+
const vibrate = (pattern)=> vibrateEnable && Navigator.vibrate && Navigator.vibrate(pattern);
|
|
236
|
+
|
|
237
|
+
/** Cancel any ongoing vibration
|
|
238
|
+
* @memberof Input */
|
|
239
|
+
const vibrateStop = ()=> vibrate(0);
|
|
240
|
+
|
|
207
241
|
///////////////////////////////////////////////////////////////////////////////
|
|
208
242
|
// Touch input
|
|
209
243
|
|
|
210
244
|
/** True if a touch device has been detected
|
|
211
245
|
* @const {boolean}
|
|
212
246
|
* @memberof Input */
|
|
213
|
-
const isTouchDevice =
|
|
247
|
+
const isTouchDevice = window.ontouchstart !== undefined;
|
|
248
|
+
|
|
249
|
+
// try to enable touch mouse
|
|
214
250
|
if (isTouchDevice)
|
|
215
251
|
{
|
|
216
252
|
// handle all touch events the same way
|
|
217
253
|
let wasTouching, hadTouchInput;
|
|
218
|
-
ontouchstart = ontouchmove = ontouchend = e=>
|
|
254
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
219
255
|
{
|
|
220
256
|
e.button = 0; // all touches are left click
|
|
221
257
|
|
|
@@ -239,4 +275,118 @@ if (isTouchDevice)
|
|
|
239
275
|
// prevent normal mouse events from being called
|
|
240
276
|
return !e.cancelable;
|
|
241
277
|
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
281
|
+
// touch gamepad, virtual on screen gamepad emulator for touch devices
|
|
282
|
+
|
|
283
|
+
// touch input internal variables
|
|
284
|
+
let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick = vec2(), touchGamepadAnalog = 1;
|
|
285
|
+
|
|
286
|
+
// create the touch gamepad, called automatically by the engine
|
|
287
|
+
function touchGamepadCreate()
|
|
288
|
+
{
|
|
289
|
+
if (!touchGamepadEnable || !isTouchDevice)
|
|
290
|
+
return;
|
|
291
|
+
|
|
292
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
293
|
+
{
|
|
294
|
+
if (!touchGamepadEnable)
|
|
295
|
+
return;
|
|
296
|
+
|
|
297
|
+
// clear touch gamepad input
|
|
298
|
+
touchGamepadStick = vec2();
|
|
299
|
+
touchGamepadButtons = [];
|
|
300
|
+
|
|
301
|
+
const touching = e.touches.length;
|
|
302
|
+
if (touching)
|
|
303
|
+
{
|
|
304
|
+
touchGamepadTimer.isSet() || zzfx(0) ; // fix mobile audio, force it to play a sound the first time
|
|
305
|
+
|
|
306
|
+
// set that gamepad is active
|
|
307
|
+
isUsingGamepad = 1;
|
|
308
|
+
touchGamepadTimer.set();
|
|
309
|
+
|
|
310
|
+
if (paused)
|
|
311
|
+
{
|
|
312
|
+
// touch anywhere to press start when paused
|
|
313
|
+
touchGamepadButtons[9] = 1;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// get center of left and right sides
|
|
319
|
+
const stickCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
320
|
+
const buttonCenter = mainCanvasSize.subtract(vec2(touchGamepadSize, touchGamepadSize));
|
|
321
|
+
const startCenter = mainCanvasSize.scale(.5);
|
|
322
|
+
|
|
323
|
+
// check each touch point
|
|
324
|
+
for (const touch of e.touches)
|
|
325
|
+
{
|
|
326
|
+
const touchPos = mouseToScreen(vec2(touch.clientX, touch.clientY));
|
|
327
|
+
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
328
|
+
{
|
|
329
|
+
// virtual analog stick
|
|
330
|
+
if (touchGamepadAnalog)
|
|
331
|
+
touchGamepadStick = touchPos.subtract(stickCenter).clampLength(touchGamepadSize/2).scale(2/touchGamepadSize);
|
|
332
|
+
else
|
|
333
|
+
{
|
|
334
|
+
// 8 way dpad
|
|
335
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
336
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
340
|
+
{
|
|
341
|
+
// virtual face buttons
|
|
342
|
+
const button = touchPos.subtract(buttonCenter).direction();
|
|
343
|
+
touchGamepadButtons[button] = 1;
|
|
344
|
+
}
|
|
345
|
+
else if (touchPos.distance(startCenter) < touchGamepadSize)
|
|
346
|
+
{
|
|
347
|
+
// virtual start button in center
|
|
348
|
+
touchGamepadButtons[9] = 1;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// render the touch gamepad, called automatically by the engine
|
|
355
|
+
function touchGamepadRender()
|
|
356
|
+
{
|
|
357
|
+
if (!touchGamepadEnable || !touchGamepadTimer.isSet())
|
|
358
|
+
return;
|
|
359
|
+
|
|
360
|
+
// fade off when not touching or paused
|
|
361
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
362
|
+
if (!alpha || paused)
|
|
363
|
+
return;
|
|
364
|
+
|
|
365
|
+
// setup the canvas
|
|
366
|
+
overlayContext.save();
|
|
367
|
+
overlayContext.globalAlpha = alpha*touchGamepadAlpha;
|
|
368
|
+
overlayContext.strokeStyle = '#fff';
|
|
369
|
+
overlayContext.lineWidth = 3;
|
|
370
|
+
|
|
371
|
+
// draw left analog stick
|
|
372
|
+
overlayContext.fillStyle = gamepadStick(0).length() > 0 ? '#fff' : '#000';
|
|
373
|
+
overlayContext.beginPath();
|
|
374
|
+
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
375
|
+
overlayContext.fill();
|
|
376
|
+
overlayContext.stroke();
|
|
377
|
+
|
|
378
|
+
// draw right face buttons
|
|
379
|
+
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
380
|
+
for (let i=4; i--;)
|
|
381
|
+
{
|
|
382
|
+
const pos = rightCenter.add((new Vector2).setAngle(i*PI/2, touchGamepadSize/2));
|
|
383
|
+
overlayContext.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
|
|
384
|
+
overlayContext.beginPath();
|
|
385
|
+
overlayContext.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
|
|
386
|
+
overlayContext.fill();
|
|
387
|
+
overlayContext.stroke();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// set canvas back to normal
|
|
391
|
+
overlayContext.restore();
|
|
242
392
|
}
|