littlejsengine 1.6.6 → 1.7.1
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 +76 -53
- package/build/littlejs.d.ts +154 -136
- package/build/littlejs.esm.js +461 -305
- package/build/littlejs.esm.min.js +1 -1
- package/build/littlejs.js +452 -291
- package/build/littlejs.min.js +1 -1
- package/build/littlejs.release.js +2317 -2168
- package/examples/breakout/game.js +5 -1
- package/examples/breakout/index.html +5 -5
- package/examples/breakoutTutorial/README.md +6 -6
- package/examples/breakoutTutorial/game.js +3 -0
- package/examples/breakoutTutorial/index.html +3 -3
- package/examples/electron/build.js +105 -0
- package/examples/electron/game.js +4 -2
- package/examples/electron/index.html +13 -2
- package/examples/electron/package.json +5 -3
- package/examples/empty/game.js +3 -1
- package/examples/empty/index.html +1 -1
- package/examples/js13k/build.bat +2 -0
- package/examples/js13k/build.js +110 -0
- package/examples/js13k/game.js +109 -0
- package/examples/js13k/index.html +18 -0
- package/examples/js13k/tiles.png +0 -0
- package/examples/module/game.js +9 -3
- package/examples/module/index.html +2 -2
- package/examples/particles/index.html +7 -13
- package/examples/platformer/game.js +5 -2
- package/examples/platformer/gameEffects.js +12 -12
- package/examples/platformer/gamePlayer.js +2 -2
- package/examples/platformer/index.html +8 -8
- package/examples/puzzle/game.js +5 -3
- package/examples/puzzle/index.html +4 -4
- package/examples/starter/build.bat +2 -78
- package/examples/starter/build.js +109 -0
- package/examples/starter/game.js +4 -1
- package/examples/starter/index.html +15 -18
- package/examples/stress/index.html +5 -7
- package/examples/typescript/build.bat +2 -14
- package/examples/typescript/build.js +31 -0
- package/examples/typescript/game.js +94 -89
- package/examples/typescript/game.ts +9 -3
- package/examples/typescript/index.html +2 -2
- package/package.json +2 -2
- package/src/engine.js +3 -3
- package/src/engineAudio.js +44 -16
- package/src/engineBuild.bat +2 -132
- package/src/engineBuild.js +156 -0
- package/src/engineDebug.js +25 -15
- package/src/engineDraw.js +62 -53
- package/src/engineExport.js +7 -12
- package/src/engineInput.js +27 -35
- package/src/engineObject.js +15 -13
- package/src/engineParticles.js +6 -6
- package/src/engineRelease.js +1 -3
- package/src/engineSettings.js +1 -0
- package/src/engineTileLayer.js +31 -18
- package/src/engineUtilities.js +161 -93
- package/src/engineWebGL.js +63 -27
- package/examples/electron/build.bat +0 -52
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* LittleJS Build System
|
|
5
|
+
* - Combine input files
|
|
6
|
+
* - Run custom build steps
|
|
7
|
+
* - Check for errors
|
|
8
|
+
* - Output to build folder
|
|
9
|
+
* @namespace Build
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const ENGINE_NAME = 'littlejs';
|
|
13
|
+
const BUILD_FOLDER = 'build';
|
|
14
|
+
const SOURCE_FOLDER = 'src';
|
|
15
|
+
const engineSourceFiles =
|
|
16
|
+
[
|
|
17
|
+
`${SOURCE_FOLDER}/engineUtilities.js`,
|
|
18
|
+
`${SOURCE_FOLDER}/engineSettings.js`,
|
|
19
|
+
`${SOURCE_FOLDER}/engineObject.js`,
|
|
20
|
+
`${SOURCE_FOLDER}/engineDraw.js`,
|
|
21
|
+
`${SOURCE_FOLDER}/engineInput.js`,
|
|
22
|
+
`${SOURCE_FOLDER}/engineAudio.js`,
|
|
23
|
+
`${SOURCE_FOLDER}/engineTileLayer.js`,
|
|
24
|
+
`${SOURCE_FOLDER}/engineParticles.js`,
|
|
25
|
+
`${SOURCE_FOLDER}/engineMedals.js`,
|
|
26
|
+
`${SOURCE_FOLDER}/engineWebGL.js`,
|
|
27
|
+
`${SOURCE_FOLDER}/engine.js`,
|
|
28
|
+
];
|
|
29
|
+
const asciiArt =`
|
|
30
|
+
~~~~°°°°ooo°oOo°ooOooOooOo.
|
|
31
|
+
__________ ________ ____ '°OoO.
|
|
32
|
+
|LittleJS| |Engine| |[]|_._._Y
|
|
33
|
+
.|________|_._|______|_._|__|_|_|_|}
|
|
34
|
+
OOO OOO OO OO OO=OO---oo\\
|
|
35
|
+
`;
|
|
36
|
+
const license = '// LittleJS - MIT License - Copyright 2021 Frank Force\n'
|
|
37
|
+
|
|
38
|
+
console.log(asciiArt);
|
|
39
|
+
console.log('Choo Choo... Building LittleJS Engine!\n');
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
const fs = require('node:fs');
|
|
42
|
+
const child_process = require('node:child_process');
|
|
43
|
+
|
|
44
|
+
try
|
|
45
|
+
{
|
|
46
|
+
// Setup build folder
|
|
47
|
+
fs.rmSync(BUILD_FOLDER, { recursive: true, force: true });
|
|
48
|
+
fs.mkdirSync(BUILD_FOLDER);
|
|
49
|
+
}
|
|
50
|
+
catch (e) { handleError(e, 'Failed to create build folder!'); }
|
|
51
|
+
|
|
52
|
+
Build
|
|
53
|
+
(
|
|
54
|
+
'Build Engine -- all',
|
|
55
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
|
|
56
|
+
[`${SOURCE_FOLDER}/engineDebug.js`, ...engineSourceFiles],
|
|
57
|
+
[], license
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
Build
|
|
61
|
+
(
|
|
62
|
+
'Build Engine -- release',
|
|
63
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
|
|
64
|
+
[`${SOURCE_FOLDER}/engineRelease.js`, ...engineSourceFiles],
|
|
65
|
+
[], license
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
Build
|
|
69
|
+
(
|
|
70
|
+
'Build Engine -- minified',
|
|
71
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.min.js`,
|
|
72
|
+
[`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`],
|
|
73
|
+
[closureCompilerStep, uglifyBuildStep]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
Build
|
|
77
|
+
(
|
|
78
|
+
'Build Engine -- ESM',
|
|
79
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.js`,
|
|
80
|
+
[`${BUILD_FOLDER}/${ENGINE_NAME}.js`, `${SOURCE_FOLDER}/engineExport.js`],
|
|
81
|
+
[typeScriptBuildStep]
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
Build
|
|
85
|
+
(
|
|
86
|
+
'Build Engine -- ESM minified release',
|
|
87
|
+
`${BUILD_FOLDER}/${ENGINE_NAME}.esm.min.js`,
|
|
88
|
+
[`${BUILD_FOLDER}/${ENGINE_NAME}.min.js`, `${SOURCE_FOLDER}/engineExport.js`],
|
|
89
|
+
[uglifyBuildStep]
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
console.log(`Engine built in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
93
|
+
|
|
94
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
95
|
+
|
|
96
|
+
// A single build with its own source files, build steps, and output file
|
|
97
|
+
// - each build step is a callback that accepts a single filename
|
|
98
|
+
function Build(message, outputFile, files=[], buildSteps=[], topOfFileText)
|
|
99
|
+
{
|
|
100
|
+
console.log(message);
|
|
101
|
+
|
|
102
|
+
// copy files into a buffer
|
|
103
|
+
let buffer = '';
|
|
104
|
+
if (topOfFileText)
|
|
105
|
+
buffer += topOfFileText + '\n';
|
|
106
|
+
for (const file of files)
|
|
107
|
+
buffer += fs.readFileSync(file) + '\n';
|
|
108
|
+
|
|
109
|
+
// output file
|
|
110
|
+
fs.writeFileSync(outputFile, buffer, {flag: 'w+'});
|
|
111
|
+
|
|
112
|
+
// execute build steps in order
|
|
113
|
+
for (const buildStep of buildSteps)
|
|
114
|
+
buildStep(outputFile);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Process with Closure Compiler to minify and check for errors
|
|
118
|
+
function closureCompilerStep(filename)
|
|
119
|
+
{
|
|
120
|
+
const filenameTemp = filename + '.tmp';
|
|
121
|
+
fs.copyFileSync(filename, filenameTemp);
|
|
122
|
+
try
|
|
123
|
+
{
|
|
124
|
+
child_process.execSync(`npx google-closure-compiler --js=${filenameTemp} --js_output_file=${filename} --language_out=ECMASCRIPT_2021 --warning_level=VERBOSE --jscomp_off=*`);
|
|
125
|
+
fs.rmSync(filenameTemp);
|
|
126
|
+
}
|
|
127
|
+
catch (e) { handleError(e, 'Failed to run Closure Compiler step!'); }
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Process with Uglify to minify
|
|
131
|
+
function uglifyBuildStep(filename)
|
|
132
|
+
{
|
|
133
|
+
try
|
|
134
|
+
{
|
|
135
|
+
child_process.execSync(`npx uglifyjs ${filename} -o ${filename}`);
|
|
136
|
+
}
|
|
137
|
+
catch (e) { handleError(e,'Failed to run Uglify minification step!'); }
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Build TypeScript definitions
|
|
141
|
+
function typeScriptBuildStep(filename)
|
|
142
|
+
{
|
|
143
|
+
try
|
|
144
|
+
{
|
|
145
|
+
child_process.execSync(`npx tsc ${filename} --declaration --allowJs --emitDeclarationOnly --outFile ${BUILD_FOLDER}/${ENGINE_NAME}.d.ts`);
|
|
146
|
+
}
|
|
147
|
+
catch (e) { handleError(e, 'Failed to run TypeScript build step!'); }
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// display the error and exit
|
|
151
|
+
function handleError(e,message)
|
|
152
|
+
{
|
|
153
|
+
console.error(e);
|
|
154
|
+
console.error(message);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
package/src/engineDebug.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Debug System
|
|
3
|
-
* - Press
|
|
3
|
+
* - Press Esc to show debug overlay with mouse pick
|
|
4
4
|
* - Number keys toggle debug functions
|
|
5
5
|
* - +/- apply time scale
|
|
6
6
|
* - Debug primitive rendering
|
|
7
|
-
* - Save a 2d canvas as
|
|
7
|
+
* - Save a 2d canvas as a png image
|
|
8
8
|
* @namespace Debug
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -34,12 +34,6 @@ const debugPointSize = .5;
|
|
|
34
34
|
* @memberof Debug */
|
|
35
35
|
let showWatermark = 1;
|
|
36
36
|
|
|
37
|
-
/** True if god mode is enabled, handle this however you want
|
|
38
|
-
* @type {Boolean}
|
|
39
|
-
* @default
|
|
40
|
-
* @memberof Debug */
|
|
41
|
-
let godMode = 0;
|
|
42
|
-
|
|
43
37
|
/** Key code used to toggle debug mode, Esc by default
|
|
44
38
|
* @type {Boolean}
|
|
45
39
|
* @default
|
|
@@ -144,11 +138,27 @@ function debugClear() { debugPrimitives = []; }
|
|
|
144
138
|
/** Save a canvas to disk
|
|
145
139
|
* @param {HTMLCanvasElement} canvas
|
|
146
140
|
* @param {String} [filename]
|
|
141
|
+
* @param {String} [type='image/png']
|
|
142
|
+
* @memberof Debug */
|
|
143
|
+
function debugSaveCanvas(canvas, filename=engineName, type='image/png')
|
|
144
|
+
{ debugSaveDataURL(canvas.toDataURL(type), filename); }
|
|
145
|
+
|
|
146
|
+
/** Save a text file to disk
|
|
147
|
+
* @param {String} text
|
|
148
|
+
* @param {String} [filename]
|
|
149
|
+
* @param {String} [type='text/plain']
|
|
150
|
+
* @memberof Debug */
|
|
151
|
+
function debugSaveText(text, filename=engineName, type='text/plain')
|
|
152
|
+
{ debugSaveDataURL(URL.createObjectURL(new Blob([text], {'type':type})), filename); }
|
|
153
|
+
|
|
154
|
+
/** Save a data url to disk
|
|
155
|
+
* @param {String} dataURL
|
|
156
|
+
* @param {String} filename
|
|
147
157
|
* @memberof Debug */
|
|
148
|
-
function
|
|
158
|
+
function debugSaveDataURL(dataURL, filename)
|
|
149
159
|
{
|
|
150
|
-
downloadLink.download =
|
|
151
|
-
downloadLink.href =
|
|
160
|
+
downloadLink.download = filename;
|
|
161
|
+
downloadLink.href = dataURL;
|
|
152
162
|
downloadLink.click();
|
|
153
163
|
}
|
|
154
164
|
|
|
@@ -180,7 +190,7 @@ function debugUpdate()
|
|
|
180
190
|
if (keyWasPressed(51)) // 3
|
|
181
191
|
debugGamepads = !debugGamepads;
|
|
182
192
|
if (keyWasPressed(52)) // 4
|
|
183
|
-
|
|
193
|
+
debugRaycast = !debugRaycast;
|
|
184
194
|
if (keyWasPressed(53)) // 5
|
|
185
195
|
debugTakeScreenshot = 1;
|
|
186
196
|
//if (keyWasPressed(54)) // 6
|
|
@@ -359,8 +369,8 @@ function debugRender()
|
|
|
359
369
|
overlayContext.fillText('2: Debug Particles', x, y += h);
|
|
360
370
|
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
361
371
|
overlayContext.fillText('3: Debug Gamepads', x, y += h);
|
|
362
|
-
overlayContext.fillStyle =
|
|
363
|
-
overlayContext.fillText('4:
|
|
372
|
+
overlayContext.fillStyle = debugRaycast ? '#f00' : '#fff';
|
|
373
|
+
overlayContext.fillText('4: Debug Raycasts', x, y += h);
|
|
364
374
|
overlayContext.fillStyle = '#fff';
|
|
365
375
|
overlayContext.fillText('5: Save Screenshot', x, y += h);
|
|
366
376
|
|
|
@@ -385,7 +395,7 @@ function debugRender()
|
|
|
385
395
|
{
|
|
386
396
|
overlayContext.fillText(debugPhysics ? 'Debug Physics' : '', x, y += h);
|
|
387
397
|
overlayContext.fillText(debugParticles ? 'Debug Particles' : '', x, y += h);
|
|
388
|
-
overlayContext.fillText(
|
|
398
|
+
overlayContext.fillText(debugRaycast ? 'Debug Raycasts' : '', x, y += h);
|
|
389
399
|
overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
390
400
|
}
|
|
391
401
|
|
package/src/engineDraw.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Drawing System
|
|
3
|
-
* - Hybrid with both Canvas2D and WebGL available
|
|
3
|
+
* - Hybrid system with both Canvas2D and WebGL available
|
|
4
4
|
* - Super fast tile sheet rendering with WebGL
|
|
5
5
|
* - Can apply rotation, mirror, color and additive color
|
|
6
|
+
* - Font rendering system with built in engine font
|
|
6
7
|
* - Many useful utility functions
|
|
7
8
|
*
|
|
8
9
|
* LittleJS uses a hybrid rendering solution with the best of both Canvas2D and WebGL.
|
|
@@ -12,7 +13,6 @@
|
|
|
12
13
|
* overlayCanvas - Another 2D canvas that appears on top of the other 2 canvases.
|
|
13
14
|
*
|
|
14
15
|
* The WebGL rendering system is very fast with some caveats...
|
|
15
|
-
* - The default setup supports only 1 tile sheet, to support more call glCreateTexture and glSetTexture
|
|
16
16
|
* - Switching blend modes (additive) or textures causes another draw call which is expensive in excess
|
|
17
17
|
* - Group additive rendering together using renderOrder to mitigate this issue
|
|
18
18
|
*
|
|
@@ -56,25 +56,29 @@ const tileImage = new Image;
|
|
|
56
56
|
let tileImageSize, tileImageFixBleed, drawCount;
|
|
57
57
|
|
|
58
58
|
/** Convert from screen to world space coordinates
|
|
59
|
-
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
60
59
|
* @param {Vector2} screenPos
|
|
61
60
|
* @return {Vector2}
|
|
62
61
|
* @memberof Draw */
|
|
63
62
|
function screenToWorld(screenPos)
|
|
64
63
|
{
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
return new Vector2
|
|
65
|
+
(
|
|
66
|
+
(screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale + cameraPos.x,
|
|
67
|
+
(screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale + cameraPos.y
|
|
68
|
+
);
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/** Convert from world to screen space coordinates
|
|
70
|
-
* - if calling outside of render, you may need to manually set mainCanvasSize
|
|
71
72
|
* @param {Vector2} worldPos
|
|
72
73
|
* @return {Vector2}
|
|
73
74
|
* @memberof Draw */
|
|
74
75
|
function worldToScreen(worldPos)
|
|
75
76
|
{
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
return new Vector2
|
|
78
|
+
(
|
|
79
|
+
(worldPos.x - cameraPos.x) * cameraScale + mainCanvasSize.x/2 - .5,
|
|
80
|
+
(worldPos.y - cameraPos.y) * -cameraScale + mainCanvasSize.y/2 - .5
|
|
81
|
+
);
|
|
78
82
|
}
|
|
79
83
|
|
|
80
84
|
/** Draw textured tile centered in world space, with color applied if using WebGL
|
|
@@ -87,13 +91,21 @@ function worldToScreen(worldPos)
|
|
|
87
91
|
* @param {Boolean} [mirror=0] - If true image is flipped along the Y axis
|
|
88
92
|
* @param {Color} [additiveColor=Color(0,0,0,0)] - Additive color to be applied
|
|
89
93
|
* @param {Boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
94
|
+
* @param {Boolean} [screenSpace=0] - If true the pos and size are in screen space
|
|
90
95
|
* @memberof Draw */
|
|
91
|
-
function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
|
|
92
|
-
additiveColor=new Color(0,0,0,0), useWebGL=glEnable)
|
|
96
|
+
function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, color=new Color,
|
|
97
|
+
angle=0, mirror, additiveColor=new Color(0,0,0,0), useWebGL=glEnable, screenSpace)
|
|
93
98
|
{
|
|
94
99
|
showWatermark && ++drawCount;
|
|
100
|
+
|
|
95
101
|
if (glEnable && useWebGL)
|
|
96
102
|
{
|
|
103
|
+
if (screenSpace)
|
|
104
|
+
{
|
|
105
|
+
// convert to world space
|
|
106
|
+
pos = screenToWorld(pos);
|
|
107
|
+
size = size.scale(1/cameraScale);
|
|
108
|
+
}
|
|
97
109
|
if (tileIndex < 0 || !tileImage.width)
|
|
98
110
|
{
|
|
99
111
|
// if negative tile index or image not found, force untextured
|
|
@@ -135,7 +147,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
135
147
|
context.globalAlpha = color.a; // only alpha is supported
|
|
136
148
|
context.drawImage(tileImage, sX, sY, sWidth, sHeight, -.5, -.5, 1, 1);
|
|
137
149
|
}
|
|
138
|
-
});
|
|
150
|
+
}, undefined, screenSpace);
|
|
139
151
|
}
|
|
140
152
|
}
|
|
141
153
|
|
|
@@ -145,38 +157,30 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
145
157
|
* @param {Color} [color=Color()]
|
|
146
158
|
* @param {Number} [angle=0]
|
|
147
159
|
* @param {Boolean} [useWebGL=glEnable]
|
|
160
|
+
* @param {Boolean} [screenSpace=0]
|
|
148
161
|
* @memberof Draw */
|
|
149
|
-
function drawRect(pos, size, color, angle, useWebGL)
|
|
150
|
-
{
|
|
151
|
-
drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, 0, useWebGL);
|
|
152
|
-
}
|
|
162
|
+
function drawRect(pos, size, color, angle, useWebGL, screenSpace)
|
|
163
|
+
{ drawTile(pos, size, -1, tileSizeDefault, color, angle, 0, undefined, useWebGL, screenSpace); }
|
|
153
164
|
|
|
154
|
-
/** Draw
|
|
155
|
-
* @param {
|
|
156
|
-
* @param {Vector2} [size=Vector2(1,1)] - Size of the tile
|
|
157
|
-
* @param {Number} [tileIndex=-1] - Tile index to use, negative is untextured
|
|
158
|
-
* @param {Vector2} [tileSize=tileSizeDefault] - Tile size in source pixels
|
|
165
|
+
/** Draw colored polygon using passed in points
|
|
166
|
+
* @param {Array} points - Array of Vector2 points
|
|
159
167
|
* @param {Color} [color=Color()]
|
|
160
|
-
* @param {Number} [angle=0]
|
|
161
|
-
* @param {Boolean} [mirror=0]
|
|
162
|
-
* @param {Color} [additiveColor=Color(0,0,0,0)]
|
|
163
168
|
* @param {Boolean} [useWebGL=glEnable]
|
|
169
|
+
* @param {Boolean} [screenSpace=0]
|
|
164
170
|
* @memberof Draw */
|
|
165
|
-
function
|
|
171
|
+
function drawPoly(points, color=new Color, useWebGL=glEnable, screenSpace)
|
|
166
172
|
{
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
{
|
|
179
|
-
drawTileScreenSpace(pos, size, -1, tileSizeDefault, color, angle, 0, 0, useWebGL);
|
|
173
|
+
if (useWebGL)
|
|
174
|
+
glDrawPoints(screenSpace ? points.map(screenToWorld) : points, color.rgbaInt());
|
|
175
|
+
else
|
|
176
|
+
{
|
|
177
|
+
// draw using canvas
|
|
178
|
+
mainContext.fillStyle = color;
|
|
179
|
+
mainContext.beginPath();
|
|
180
|
+
for (const point of screenSpace ? points : points.map(worldToScreen))
|
|
181
|
+
mainContext.lineTo(point.x, point.y);
|
|
182
|
+
mainContext.fill();
|
|
183
|
+
}
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
/** Draw colored line between two points
|
|
@@ -185,12 +189,13 @@ function drawRectScreenSpace(pos, size, color, angle, useWebGL)
|
|
|
185
189
|
* @param {Number} [thickness=.1]
|
|
186
190
|
* @param {Color} [color=Color()]
|
|
187
191
|
* @param {Boolean} [useWebGL=glEnable]
|
|
192
|
+
* @param {Boolean} [screenSpace=0]
|
|
188
193
|
* @memberof Draw */
|
|
189
194
|
function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
190
195
|
{
|
|
191
196
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
192
197
|
const size = vec2(thickness, halfDelta.length()*2);
|
|
193
|
-
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL);
|
|
198
|
+
drawRect(posA.add(halfDelta), size, color, halfDelta.angle(), useWebGL, screenSpace);
|
|
194
199
|
}
|
|
195
200
|
|
|
196
201
|
/** Draw directly to a 2d canvas context in world space
|
|
@@ -200,12 +205,16 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL)
|
|
|
200
205
|
* @param {Boolean} mirror
|
|
201
206
|
* @param {Function} drawFunction
|
|
202
207
|
* @param {CanvasRenderingContext2D} [context=mainContext]
|
|
208
|
+
* @param {Boolean} [screenSpace=0]
|
|
203
209
|
* @memberof Draw */
|
|
204
|
-
function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainContext)
|
|
210
|
+
function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainContext, screenSpace)
|
|
205
211
|
{
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
if (!screenSpace)
|
|
213
|
+
{
|
|
214
|
+
// create canvas transform from world space to screen space
|
|
215
|
+
pos = worldToScreen(pos);
|
|
216
|
+
size = size.scale(cameraScale);
|
|
217
|
+
}
|
|
209
218
|
context.save();
|
|
210
219
|
context.translate(pos.x+.5|0, pos.y+.5|0);
|
|
211
220
|
context.rotate(angle);
|
|
@@ -312,6 +321,17 @@ class FontImage
|
|
|
312
321
|
this.context = context;
|
|
313
322
|
}
|
|
314
323
|
|
|
324
|
+
/** Draw text in world space using the image font
|
|
325
|
+
* @param {String} text
|
|
326
|
+
* @param {Vector2} pos
|
|
327
|
+
* @param {Number} [scale=.25]
|
|
328
|
+
* @param {Boolean} [center]
|
|
329
|
+
*/
|
|
330
|
+
drawText(text, pos, scale=1, center)
|
|
331
|
+
{
|
|
332
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
333
|
+
}
|
|
334
|
+
|
|
315
335
|
/** Draw text in screen space using the image font
|
|
316
336
|
* @param {String} text
|
|
317
337
|
* @param {Vector2} pos
|
|
@@ -349,17 +369,6 @@ class FontImage
|
|
|
349
369
|
|
|
350
370
|
context.restore();
|
|
351
371
|
}
|
|
352
|
-
|
|
353
|
-
/** Draw text in world space using the image font
|
|
354
|
-
* @param {String} text
|
|
355
|
-
* @param {Vector2} pos
|
|
356
|
-
* @param {Number} [scale=.25]
|
|
357
|
-
* @param {Boolean} [center]
|
|
358
|
-
*/
|
|
359
|
-
drawText(text, pos, scale=1, center)
|
|
360
|
-
{
|
|
361
|
-
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
362
|
-
}
|
|
363
372
|
}
|
|
364
373
|
|
|
365
374
|
///////////////////////////////////////////////////////////////////////////////
|
package/src/engineExport.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Module Export
|
|
3
|
-
* - Export engine as a module with
|
|
3
|
+
* - Export engine as a module with functions where necessary
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/** Set position of camera in world space
|
|
@@ -181,18 +181,13 @@ function setMedalDisplayIconSize(size) { medalDisplayIconSize = size; }
|
|
|
181
181
|
/** Set to stop medals from being unlockable
|
|
182
182
|
* @param {Boolean} preventUnlock
|
|
183
183
|
* @memberof Settings */
|
|
184
|
-
function setMedalsPreventUnlock(
|
|
184
|
+
function setMedalsPreventUnlock(preventUnlock) { medalsPreventUnlock = preventUnlock; }
|
|
185
185
|
|
|
186
186
|
/** Set if watermark with FPS should be shown
|
|
187
187
|
* @param {Boolean} show
|
|
188
188
|
* @memberof Debug */
|
|
189
189
|
function setShowWatermark(show) { showWatermark = show; }
|
|
190
190
|
|
|
191
|
-
/** Set if god mode is enabled
|
|
192
|
-
* @param {Boolean} enable
|
|
193
|
-
* @memberof Debug */
|
|
194
|
-
function setGodMode(enable) { godMode = enable; }
|
|
195
|
-
|
|
196
191
|
/** Set key code used to toggle debug mode, Esc by default
|
|
197
192
|
* @param {Number} key
|
|
198
193
|
* @memberof Debug */
|
|
@@ -237,7 +232,6 @@ export {
|
|
|
237
232
|
setMedalDisplayIconSize,
|
|
238
233
|
setMedalsPreventUnlock,
|
|
239
234
|
setShowWatermark,
|
|
240
|
-
setGodMode,
|
|
241
235
|
setDebugKey,
|
|
242
236
|
|
|
243
237
|
// Settings
|
|
@@ -280,7 +274,6 @@ export {
|
|
|
280
274
|
// Globals
|
|
281
275
|
debug,
|
|
282
276
|
showWatermark,
|
|
283
|
-
godMode,
|
|
284
277
|
|
|
285
278
|
// Debug
|
|
286
279
|
ASSERT,
|
|
@@ -302,6 +295,10 @@ export {
|
|
|
302
295
|
mod,
|
|
303
296
|
clamp,
|
|
304
297
|
percent,
|
|
298
|
+
distanceWrap,
|
|
299
|
+
lerpWrap,
|
|
300
|
+
distanceAngle,
|
|
301
|
+
lerpAngle,
|
|
305
302
|
lerp,
|
|
306
303
|
smoothStep,
|
|
307
304
|
nearestPowerOfTwo,
|
|
@@ -316,11 +313,9 @@ export {
|
|
|
316
313
|
randInCircle,
|
|
317
314
|
randVector,
|
|
318
315
|
randColor,
|
|
319
|
-
randSeed,
|
|
320
|
-
setRandSeed,
|
|
321
|
-
randSeeded,
|
|
322
316
|
|
|
323
317
|
// Utility Classes
|
|
318
|
+
RandomGenerator,
|
|
324
319
|
Vector2,
|
|
325
320
|
Color,
|
|
326
321
|
Timer,
|
package/src/engineInput.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Input System
|
|
3
|
-
* - Tracks
|
|
4
|
-
* -
|
|
5
|
-
* -
|
|
6
|
-
* - Virtual gamepad for touch devices
|
|
3
|
+
* - Tracks keyboard down, pressed, and released
|
|
4
|
+
* - Tracks mouse buttons, position, and wheel
|
|
5
|
+
* - Tracks multiple analog gamepads
|
|
6
|
+
* - Virtual gamepad for touch devices
|
|
7
7
|
* @namespace Input
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -275,40 +275,32 @@ if (isTouchDevice)
|
|
|
275
275
|
let wasTouching, mouseDown = onmousedown, mouseUp = onmouseup;
|
|
276
276
|
onmousedown = onmouseup = ()=> 0;
|
|
277
277
|
|
|
278
|
-
//
|
|
279
|
-
ontouchstart = (e)=>
|
|
278
|
+
// handle all touch events the same way
|
|
279
|
+
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
280
280
|
{
|
|
281
|
-
|
|
282
|
-
zzfx(0);
|
|
281
|
+
e.button = 0; // all touches are left click
|
|
283
282
|
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
e.button = 0; // all touches are left click
|
|
288
|
-
|
|
289
|
-
// check if touching and pass to mouse events
|
|
290
|
-
const touching = e.touches.length;
|
|
291
|
-
if (touching)
|
|
292
|
-
{
|
|
293
|
-
// set event pos and pass it along
|
|
294
|
-
e.x = e.touches[0].clientX;
|
|
295
|
-
e.y = e.touches[0].clientY;
|
|
296
|
-
wasTouching ? onmousemove(e) : mouseDown(e);
|
|
297
|
-
}
|
|
298
|
-
else if (wasTouching)
|
|
299
|
-
mouseUp(e);
|
|
300
|
-
|
|
301
|
-
// set was touching
|
|
302
|
-
wasTouching = touching;
|
|
283
|
+
// fix stalled audio on mobile
|
|
284
|
+
if (soundEnable)
|
|
285
|
+
audioContext ? audioContext.resume() : zzfx(0);
|
|
303
286
|
|
|
304
|
-
|
|
305
|
-
|
|
287
|
+
// check if touching and pass to mouse events
|
|
288
|
+
const touching = e.touches.length;
|
|
289
|
+
if (touching)
|
|
290
|
+
{
|
|
291
|
+
// set event pos and pass it along
|
|
292
|
+
e.x = e.touches[0].clientX;
|
|
293
|
+
e.y = e.touches[0].clientY;
|
|
294
|
+
wasTouching ? onmousemove(e) : mouseDown(e);
|
|
306
295
|
}
|
|
296
|
+
else if (wasTouching)
|
|
297
|
+
mouseUp(e);
|
|
307
298
|
|
|
308
|
-
//
|
|
309
|
-
|
|
299
|
+
// set was touching
|
|
300
|
+
wasTouching = touching;
|
|
310
301
|
|
|
311
|
-
return
|
|
302
|
+
// must return true so the document will get focus
|
|
303
|
+
return true;
|
|
312
304
|
}
|
|
313
305
|
}
|
|
314
306
|
|
|
@@ -319,13 +311,13 @@ if (isTouchDevice)
|
|
|
319
311
|
let touchGamepadTimer = new Timer, touchGamepadButtons, touchGamepadStick;
|
|
320
312
|
|
|
321
313
|
// create the touch gamepad, called automatically by the engine
|
|
322
|
-
|
|
314
|
+
if (touchGamepadEnable)
|
|
323
315
|
{
|
|
324
316
|
// touch input internal variables
|
|
325
317
|
touchGamepadButtons = [];
|
|
326
318
|
touchGamepadStick = vec2();
|
|
327
319
|
|
|
328
|
-
|
|
320
|
+
const touchHandler = ontouchstart;
|
|
329
321
|
ontouchstart = ontouchmove = ontouchend = (e)=>
|
|
330
322
|
{
|
|
331
323
|
// clear touch gamepad input
|
|
@@ -431,7 +423,7 @@ function touchGamepadRender()
|
|
|
431
423
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
432
424
|
for (let i=4; i--;)
|
|
433
425
|
{
|
|
434
|
-
const pos = rightCenter.add((
|
|
426
|
+
const pos = rightCenter.add(vec2().setAngle(i*PI/2, touchGamepadSize/2));
|
|
435
427
|
overlayContext.fillStyle = touchGamepadButtons[i] ? '#fff' : '#000';
|
|
436
428
|
overlayContext.beginPath();
|
|
437
429
|
overlayContext.arc(pos.x, pos.y, touchGamepadSize/4, 0,9);
|