littlejsengine 1.12.6 → 1.13.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/dist/littlejs.d.ts +387 -177
- package/dist/littlejs.esm.js +5934 -5294
- package/dist/littlejs.esm.min.js +4 -1
- package/dist/littlejs.js +5887 -5263
- package/dist/littlejs.min.js +4 -1
- package/dist/littlejs.release.js +5426 -4829
- package/examples/box2d/game.js +1 -1
- package/examples/box2d/gameObjects.js +7 -6
- package/examples/box2d/index.html +1 -1
- package/examples/box2d/scenes.js +7 -7
- package/examples/breakout/game.js +1 -1
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakout/index.html +1 -1
- package/examples/breakoutTutorial/README.md +2 -2
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/breakoutTutorial/index.html +1 -1
- package/examples/empty/index.html +1 -1
- package/examples/htmlMenu/index.html +1 -1
- package/examples/index.html +396 -153
- package/examples/module/game.js +1 -1
- package/examples/module/index.html +1 -1
- package/examples/platformer/game.js +4 -4
- package/examples/platformer/gameCharacter.js +6 -6
- package/examples/platformer/gameEffects.js +74 -57
- package/examples/platformer/gameLevel.js +61 -70
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/platformer/index.html +1 -1
- package/examples/puzzle/index.html +1 -1
- package/examples/shorts/base.html +23 -3
- package/examples/shorts/box2dCar.js +8 -12
- package/examples/shorts/flappyGame.js +52 -0
- package/examples/shorts/helloWorld.js +6 -3
- package/examples/shorts/hillGlideGame.js +56 -0
- package/examples/shorts/landerGame.js +32 -0
- package/examples/shorts/maze.js +47 -0
- package/examples/shorts/medals.js +42 -0
- package/examples/shorts/nineSlice.js +21 -0
- package/examples/shorts/piano.js +52 -0
- package/examples/shorts/platformer.js +24 -20
- package/examples/shorts/{pong.js → pongGame.js} +1 -1
- package/examples/shorts/raycasting.js +71 -0
- package/examples/shorts/slidingPuzzle.js +42 -0
- package/examples/shorts/spaceGame.js +56 -0
- package/examples/shorts/starfield.js +17 -0
- package/examples/shorts/tileLayer.js +3 -5
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +8 -7
- package/examples/shorts/topDown.js +18 -26
- package/examples/shorts/uiSystem.js +4 -7
- package/examples/starter/build.bat +6 -1
- package/examples/starter/game.js +2 -2
- package/examples/starter/index.html +23 -13
- package/examples/stress/index.html +2 -3
- package/examples/style.css +136 -0
- package/examples/typescript/build.js +1 -2
- package/examples/typescript/game.js +2 -2
- package/examples/typescript/game.ts +1 -1
- package/examples/typescript/index.html +1 -1
- package/examples/uiSystem/game.js +8 -24
- package/examples/uiSystem/index.html +1 -1
- package/package.json +1 -1
- package/plugins/box2d.js +8 -8
- package/plugins/drawUtilities.js +126 -0
- package/plugins/newgrounds.js +1 -1
- package/plugins/pluginExport.js +7 -1
- package/plugins/postProcess.js +2 -2
- package/plugins/uiSystem.js +153 -65
- package/plugins/zzfxm.js +1 -1
- package/reference.md +10 -10
- package/src/engine.js +50 -29
- package/src/engineAudio.js +14 -5
- package/src/engineBuild.bat +6 -1
- package/src/engineBuild.js +20 -5
- package/src/engineDebug.js +53 -26
- package/src/engineDraw.js +108 -57
- package/src/engineExport.js +21 -9
- package/src/engineInput.js +109 -37
- package/src/engineObject.js +68 -67
- package/src/engineParticles.js +26 -9
- package/src/engineSettings.js +15 -16
- package/src/engineTileLayer.js +312 -153
- package/src/engineUtilities.js +182 -130
- package/src/engineWebGL.js +47 -36
package/src/engineBuild.js
CHANGED
|
@@ -26,7 +26,6 @@ const engineSourceFiles =
|
|
|
26
26
|
`${SOURCE_FOLDER}/engineParticles.js`,
|
|
27
27
|
`${SOURCE_FOLDER}/engineMedals.js`,
|
|
28
28
|
`${SOURCE_FOLDER}/engineWebGL.js`,
|
|
29
|
-
`${SOURCE_FOLDER}/engine.js`,
|
|
30
29
|
];
|
|
31
30
|
const enginePluginFiles =
|
|
32
31
|
[
|
|
@@ -35,6 +34,7 @@ const enginePluginFiles =
|
|
|
35
34
|
`${PLUGIN_FOLDER}/zzfxm.js`,
|
|
36
35
|
`${PLUGIN_FOLDER}/uiSystem.js`,
|
|
37
36
|
`${PLUGIN_FOLDER}/box2d.js`,
|
|
37
|
+
`${PLUGIN_FOLDER}/drawUtilities.js`,
|
|
38
38
|
];
|
|
39
39
|
const engineExtraFiles =
|
|
40
40
|
[
|
|
@@ -49,7 +49,7 @@ const asciiArt =`
|
|
|
49
49
|
OOO OOO OO OO OO=OO-oo\\
|
|
50
50
|
`;
|
|
51
51
|
const license = '// LittleJS Engine - MIT License - Copyright 2021 Frank Force\n'+
|
|
52
|
-
'// https://github.com/KilledByAPixel/LittleJS\n';
|
|
52
|
+
'// https://github.com/KilledByAPixel/LittleJS\n\n';
|
|
53
53
|
|
|
54
54
|
console.log(asciiArt);
|
|
55
55
|
console.log('Choo Choo... Building LittleJS Engine!\n');
|
|
@@ -75,6 +75,7 @@ Build
|
|
|
75
75
|
'Build Engine -- all',
|
|
76
76
|
`${BUILD_FOLDER}/${ENGINE_NAME}.js`,
|
|
77
77
|
[
|
|
78
|
+
`${SOURCE_FOLDER}/engine.js`,
|
|
78
79
|
`${SOURCE_FOLDER}/engineDebug.js`,
|
|
79
80
|
...engineSourceFiles,
|
|
80
81
|
...enginePluginFiles
|
|
@@ -87,6 +88,7 @@ Build
|
|
|
87
88
|
'Build Engine -- release',
|
|
88
89
|
`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`,
|
|
89
90
|
[
|
|
91
|
+
`${SOURCE_FOLDER}/engine.js`,
|
|
90
92
|
`${SOURCE_FOLDER}/engineRelease.js`,
|
|
91
93
|
...engineSourceFiles,
|
|
92
94
|
...enginePluginFiles
|
|
@@ -99,7 +101,7 @@ Build
|
|
|
99
101
|
'Build Engine -- minified',
|
|
100
102
|
`${BUILD_FOLDER}/${ENGINE_NAME}.min.js`,
|
|
101
103
|
[`${BUILD_FOLDER}/${ENGINE_NAME}.release.js`],
|
|
102
|
-
[closureCompilerStep, uglifyBuildStep]
|
|
104
|
+
[closureCompilerStep, uglifyBuildStep, addLicenseStep]
|
|
103
105
|
);
|
|
104
106
|
|
|
105
107
|
Build
|
|
@@ -123,7 +125,7 @@ Build
|
|
|
123
125
|
`${SOURCE_FOLDER}/engineExport.js`,
|
|
124
126
|
`${PLUGIN_FOLDER}/pluginExport.js`
|
|
125
127
|
],
|
|
126
|
-
[uglifyBuildStep]
|
|
128
|
+
[uglifyBuildStep, addLicenseStep]
|
|
127
129
|
);
|
|
128
130
|
|
|
129
131
|
console.log(`Engine built in ${((Date.now() - startTime)/1e3).toFixed(2)} seconds!`);
|
|
@@ -141,7 +143,7 @@ function Build(message, outputFile, files=[], buildSteps=[], isPrimaryBuild)
|
|
|
141
143
|
if (isPrimaryBuild)
|
|
142
144
|
{
|
|
143
145
|
// add license and strict mode to top
|
|
144
|
-
buffer += license
|
|
146
|
+
buffer += license;
|
|
145
147
|
buffer += "'use strict';\n\n";
|
|
146
148
|
}
|
|
147
149
|
|
|
@@ -189,6 +191,19 @@ function uglifyBuildStep(filename)
|
|
|
189
191
|
catch (e) { handleError(e,'Failed to run Uglify minification step!'); }
|
|
190
192
|
};
|
|
191
193
|
|
|
194
|
+
// Add license to top of file
|
|
195
|
+
function addLicenseStep(filename)
|
|
196
|
+
{
|
|
197
|
+
try
|
|
198
|
+
{
|
|
199
|
+
// add license to top of minified file
|
|
200
|
+
let fileContent = fs.readFileSync(filename, 'utf8');
|
|
201
|
+
fileContent = license + fileContent;
|
|
202
|
+
fs.writeFileSync(filename, fileContent);
|
|
203
|
+
}
|
|
204
|
+
catch (e) { handleError(e, 'Failed to add license to minified file!'); }
|
|
205
|
+
};
|
|
206
|
+
|
|
192
207
|
// Build TypeScript definitions
|
|
193
208
|
function typeScriptBuildStep(filename)
|
|
194
209
|
{
|
package/src/engineDebug.js
CHANGED
|
@@ -47,19 +47,19 @@ let debugKey = 'Escape';
|
|
|
47
47
|
let debugOverlay = false;
|
|
48
48
|
|
|
49
49
|
// Engine internal variables not exposed to documentation
|
|
50
|
-
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink;
|
|
50
|
+
let debugPrimitives = [], debugPhysics = false, debugRaycast = false, debugParticles = false, debugGamepads = false, debugMedals = false, debugTakeScreenshot, downloadLink, debugCanvas;
|
|
51
51
|
|
|
52
52
|
///////////////////////////////////////////////////////////////////////////////
|
|
53
53
|
// Debug helper functions
|
|
54
54
|
|
|
55
55
|
/** Asserts if the expression is false, does not do anything in release builds
|
|
56
|
-
* @param {boolean} assert
|
|
57
|
-
* @param {Object} [output]
|
|
56
|
+
* @param {boolean} assert
|
|
57
|
+
* @param {...Object} [output] - error message output
|
|
58
58
|
* @memberof Debug */
|
|
59
|
-
function ASSERT(assert, output)
|
|
59
|
+
function ASSERT(assert, ...output)
|
|
60
60
|
{
|
|
61
61
|
if (enableAsserts)
|
|
62
|
-
|
|
62
|
+
console.assert(assert, ...output);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/** Draw a debug rectangle in world space
|
|
@@ -132,16 +132,22 @@ function debugLine(posA, posB, color, thickness=.1, time)
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/** Draw a debug combined axis aligned bounding box in world space
|
|
135
|
-
* @param {Vector2}
|
|
136
|
-
* @param {Vector2}
|
|
137
|
-
* @param {Vector2}
|
|
138
|
-
* @param {Vector2}
|
|
135
|
+
* @param {Vector2} posA
|
|
136
|
+
* @param {Vector2} sizeA
|
|
137
|
+
* @param {Vector2} posB
|
|
138
|
+
* @param {Vector2} sizeB
|
|
139
139
|
* @param {string} [color]
|
|
140
140
|
* @memberof Debug */
|
|
141
|
-
function debugOverlap(
|
|
141
|
+
function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
142
142
|
{
|
|
143
|
-
const minPos = vec2(
|
|
144
|
-
|
|
143
|
+
const minPos = vec2(
|
|
144
|
+
min(posA.x - sizeA.x/2, posB.x - sizeB.x/2),
|
|
145
|
+
min(posA.y - sizeA.y/2, posB.y - sizeB.y/2)
|
|
146
|
+
);
|
|
147
|
+
const maxPos = vec2(
|
|
148
|
+
max(posA.x + sizeA.x/2, posB.x + sizeB.x/2),
|
|
149
|
+
max(posA.y + sizeA.y/2, posB.y + sizeB.y/2)
|
|
150
|
+
);
|
|
145
151
|
debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color);
|
|
146
152
|
}
|
|
147
153
|
|
|
@@ -169,12 +175,25 @@ function debugClear() { debugPrimitives = []; }
|
|
|
169
175
|
function debugScreenshot() { debugTakeScreenshot = 1; }
|
|
170
176
|
|
|
171
177
|
/** Save a canvas to disk
|
|
172
|
-
* @param {HTMLCanvasElement} canvas
|
|
173
|
-
* @param {string}
|
|
174
|
-
* @param {string}
|
|
178
|
+
* @param {HTMLCanvasElement|OffscreenCanvas} canvas
|
|
179
|
+
* @param {string} [filename]
|
|
180
|
+
* @param {string} [type]
|
|
175
181
|
* @memberof Debug */
|
|
176
182
|
function debugSaveCanvas(canvas, filename='screenshot', type='image/png')
|
|
177
|
-
{
|
|
183
|
+
{
|
|
184
|
+
if (canvas instanceof OffscreenCanvas)
|
|
185
|
+
{
|
|
186
|
+
// copy to temporary canvas and save
|
|
187
|
+
if (!debugCanvas)
|
|
188
|
+
debugCanvas = document.createElement('canvas');
|
|
189
|
+
debugCanvas.width = canvas.width;
|
|
190
|
+
debugCanvas.height = canvas.height;
|
|
191
|
+
debugCanvas.getContext('2d').drawImage(canvas, 0, 0);
|
|
192
|
+
debugSaveDataURL(debugCanvas.toDataURL(type), filename);
|
|
193
|
+
}
|
|
194
|
+
else
|
|
195
|
+
debugSaveDataURL(canvas.toDataURL(type), filename);
|
|
196
|
+
}
|
|
178
197
|
|
|
179
198
|
/** Save a text file to disk
|
|
180
199
|
* @param {string} text
|
|
@@ -252,7 +271,8 @@ function debugRender()
|
|
|
252
271
|
if (debugVideoCaptureIsActive())
|
|
253
272
|
return; // don't show debug info when capturing video
|
|
254
273
|
|
|
255
|
-
|
|
274
|
+
// flush any gl sprites before drawing debug info
|
|
275
|
+
glFlush();
|
|
256
276
|
|
|
257
277
|
if (debugTakeScreenshot)
|
|
258
278
|
{
|
|
@@ -329,8 +349,11 @@ function debugRender()
|
|
|
329
349
|
}
|
|
330
350
|
}
|
|
331
351
|
|
|
332
|
-
if (
|
|
333
|
-
|
|
352
|
+
if (tileCollisionTest(mousePos))
|
|
353
|
+
{
|
|
354
|
+
// show floored tile pick for tile collision
|
|
355
|
+
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), rgb(1,1,0,.5));
|
|
356
|
+
}
|
|
334
357
|
mainContext = saveContext;
|
|
335
358
|
}
|
|
336
359
|
|
|
@@ -405,10 +428,10 @@ function debugRender()
|
|
|
405
428
|
mainContext = overlayContext;
|
|
406
429
|
const raycastHitPos = tileCollisionRaycast(debugObject.pos, mousePos);
|
|
407
430
|
raycastHitPos && drawRect(raycastHitPos.floor().add(vec2(.5)), vec2(1), rgb(0,1,1,.3));
|
|
408
|
-
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5)
|
|
431
|
+
drawLine(mousePos, debugObject.pos, .1, raycastHitPos ? rgb(1,0,0,.5) : rgb(0,1,0,.5));
|
|
409
432
|
|
|
410
433
|
const debugText = 'mouse pos = ' + mousePos +
|
|
411
|
-
'\nmouse collision = ' +
|
|
434
|
+
'\nmouse collision = ' + tileCollisionGetData(mousePos) +
|
|
412
435
|
'\n\n--- object info ---\n' +
|
|
413
436
|
debugObject.toString();
|
|
414
437
|
drawTextScreen(debugText, mousePosScreen, 24, rgb(), .05, undefined, 'center', 'monospace');
|
|
@@ -417,20 +440,24 @@ function debugRender()
|
|
|
417
440
|
|
|
418
441
|
{
|
|
419
442
|
// draw debug overlay
|
|
443
|
+
const fontSize = 20;
|
|
444
|
+
const lineHeight = fontSize * 1.2 | 0;
|
|
420
445
|
overlayContext.save();
|
|
421
446
|
overlayContext.fillStyle = '#fff';
|
|
422
447
|
overlayContext.textAlign = 'left';
|
|
423
448
|
overlayContext.textBaseline = 'top';
|
|
424
|
-
overlayContext.font = '
|
|
449
|
+
overlayContext.font = fontSize + 'px monospace';
|
|
425
450
|
overlayContext.shadowColor = '#000';
|
|
426
451
|
overlayContext.shadowBlur = 9;
|
|
427
452
|
|
|
428
|
-
let x = 9, y =
|
|
453
|
+
let x = 9, y = 0, h = lineHeight;
|
|
429
454
|
if (debugOverlay)
|
|
430
455
|
{
|
|
431
|
-
overlayContext.fillText(engineName, x, y += h);
|
|
432
|
-
overlayContext.fillText('Objects: ' + engineObjects.length, x, y += h);
|
|
456
|
+
overlayContext.fillText(engineName, x, y += h/2 );
|
|
433
457
|
overlayContext.fillText('Time: ' + formatTime(time), x, y += h);
|
|
458
|
+
overlayContext.fillText('FPS: ' + averageFPS.toFixed(1), x, y += h);
|
|
459
|
+
overlayContext.fillText('Objects: ' + engineObjects.length, x, y += h);
|
|
460
|
+
overlayContext.fillText('Draw Count: ' + drawCount, x, y += h);
|
|
434
461
|
overlayContext.fillText('---------', x, y += h);
|
|
435
462
|
overlayContext.fillStyle = '#f00';
|
|
436
463
|
overlayContext.fillText('ESC: Debug Overlay', x, y += h);
|
|
@@ -444,7 +471,7 @@ function debugRender()
|
|
|
444
471
|
overlayContext.fillText('4: Debug Raycasts', x, y += h);
|
|
445
472
|
overlayContext.fillStyle = '#fff';
|
|
446
473
|
overlayContext.fillText('5: Save Screenshot', x, y += h);
|
|
447
|
-
overlayContext.fillText('6:
|
|
474
|
+
overlayContext.fillText('6: Toggle Video Capture', x, y += h);
|
|
448
475
|
|
|
449
476
|
let keysPressed = '';
|
|
450
477
|
for(const i in inputData[0])
|
package/src/engineDraw.js
CHANGED
|
@@ -42,6 +42,16 @@ let overlayCanvas;
|
|
|
42
42
|
* @memberof Draw */
|
|
43
43
|
let overlayContext;
|
|
44
44
|
|
|
45
|
+
/** The default canvas to use for drawing, usually mainCanvas
|
|
46
|
+
* @type {HTMLCanvasElement|OffscreenCanvas}
|
|
47
|
+
* @memberof Draw */
|
|
48
|
+
let drawCanvas;
|
|
49
|
+
|
|
50
|
+
/** The default 2d context to use for drawing, usually mainContext
|
|
51
|
+
* @type {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D}
|
|
52
|
+
* @memberof Draw */
|
|
53
|
+
let drawContext;
|
|
54
|
+
|
|
45
55
|
/** The size of the main canvas (and other secondary canvases)
|
|
46
56
|
* @type {Vector2}
|
|
47
57
|
* @memberof Draw */
|
|
@@ -73,31 +83,36 @@ let drawCount;
|
|
|
73
83
|
* tile(vec2(4,8), vec2(30,10)) // a tile at index (4,8) with a size of (30,10)
|
|
74
84
|
* @memberof Draw
|
|
75
85
|
*/
|
|
76
|
-
function tile(pos=
|
|
86
|
+
function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
77
87
|
{
|
|
78
88
|
if (headlessMode)
|
|
79
89
|
return new TileInfo;
|
|
80
90
|
|
|
81
91
|
// if size is a number, make it a vector
|
|
82
|
-
if (typeof size
|
|
92
|
+
if (typeof size == 'number')
|
|
83
93
|
{
|
|
84
94
|
ASSERT(size > 0);
|
|
85
|
-
size =
|
|
95
|
+
size = new Vector2(size, size);
|
|
86
96
|
}
|
|
87
97
|
|
|
88
|
-
//
|
|
98
|
+
// create tile info object
|
|
99
|
+
const tileInfo = new TileInfo(new Vector2, size, textureIndex, padding);
|
|
100
|
+
|
|
101
|
+
// use get the pos of the tile
|
|
89
102
|
const textureInfo = textureInfos[textureIndex];
|
|
90
103
|
ASSERT(!!textureInfo, 'Texture not loaded');
|
|
91
|
-
const
|
|
92
|
-
|
|
104
|
+
const sizePaddedX = size.x + padding*2;
|
|
105
|
+
const sizePaddedY = size.y + padding*2;
|
|
106
|
+
if (typeof pos == 'number')
|
|
93
107
|
{
|
|
94
|
-
const cols = textureInfo.size.x /
|
|
95
|
-
|
|
108
|
+
const cols = textureInfo.size.x / sizePaddedX |0;
|
|
109
|
+
ASSERT(cols>0, 'Tile size is too big for texture');
|
|
110
|
+
const posX = pos % cols, posY = (pos / cols) |0;
|
|
111
|
+
tileInfo.pos.set(posX*sizePaddedX+padding, posY*sizePaddedY+padding);
|
|
96
112
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return new TileInfo(pos, size, textureIndex, padding);
|
|
113
|
+
else
|
|
114
|
+
tileInfo.pos.set(pos.x*sizePaddedX+padding, pos.y*sizePaddedY+padding);
|
|
115
|
+
return tileInfo;
|
|
101
116
|
}
|
|
102
117
|
|
|
103
118
|
/**
|
|
@@ -121,6 +136,8 @@ class TileInfo
|
|
|
121
136
|
this.textureIndex = textureIndex;
|
|
122
137
|
/** @property {number} - How many pixels padding around tiles */
|
|
123
138
|
this.padding = padding;
|
|
139
|
+
/** @property {TextureInfo} - The texture info for this tile */
|
|
140
|
+
this.textureInfo = textureInfos[this.textureIndex];
|
|
124
141
|
}
|
|
125
142
|
|
|
126
143
|
/** Returns a copy of this tile offset by a vector
|
|
@@ -137,14 +154,22 @@ class TileInfo
|
|
|
137
154
|
frame(frame)
|
|
138
155
|
{
|
|
139
156
|
ASSERT(typeof frame == 'number');
|
|
140
|
-
return this.offset(
|
|
157
|
+
return this.offset(new Vector2(frame*(this.size.x+this.padding*2), 0));
|
|
141
158
|
}
|
|
142
159
|
|
|
143
|
-
/**
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
/**
|
|
161
|
+
* Set this tile to use a full image
|
|
162
|
+
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
163
|
+
* @param {WebGLTexture} [glTexture] - webgl texture
|
|
164
|
+
* @return {TileInfo}
|
|
165
|
+
*/
|
|
166
|
+
setFullImage(image, glTexture)
|
|
167
|
+
{
|
|
168
|
+
this.pos = new Vector2;
|
|
169
|
+
this.size = new Vector2(image.width, image.height);
|
|
170
|
+
this.textureInfo = new TextureInfo(image, glTexture);
|
|
171
|
+
return this;
|
|
172
|
+
}
|
|
148
173
|
}
|
|
149
174
|
|
|
150
175
|
/** Texture Info - Stores info about each texture */
|
|
@@ -152,9 +177,10 @@ class TextureInfo
|
|
|
152
177
|
{
|
|
153
178
|
/**
|
|
154
179
|
* Create a TextureInfo, called automatically by the engine
|
|
155
|
-
* @param {HTMLImageElement} image
|
|
180
|
+
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
181
|
+
* @param {WebGLTexture} [glTexture] - webgl texture
|
|
156
182
|
*/
|
|
157
|
-
constructor(image)
|
|
183
|
+
constructor(image, glTexture)
|
|
158
184
|
{
|
|
159
185
|
/** @property {HTMLImageElement} - image source */
|
|
160
186
|
this.image = image;
|
|
@@ -163,7 +189,14 @@ class TextureInfo
|
|
|
163
189
|
/** @property {Vector2} - inverse of the size, cached for rendering */
|
|
164
190
|
this.sizeInverse = vec2(1/image.width, 1/image.height);
|
|
165
191
|
/** @property {WebGLTexture} - webgl texture */
|
|
166
|
-
this.glTexture =
|
|
192
|
+
this.glTexture = glTexture;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
createWebGLTexture()
|
|
196
|
+
{
|
|
197
|
+
ASSERT(!this.glTexture);
|
|
198
|
+
if (glEnable)
|
|
199
|
+
this.glTexture = glCreateTexture(this.image);
|
|
167
200
|
}
|
|
168
201
|
}
|
|
169
202
|
|
|
@@ -175,11 +208,18 @@ class TextureInfo
|
|
|
175
208
|
* @memberof Draw */
|
|
176
209
|
function screenToWorld(screenPos)
|
|
177
210
|
{
|
|
178
|
-
|
|
179
|
-
(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
211
|
+
let cameraPosRelativeX = (screenPos.x - mainCanvasSize.x/2 + .5) / cameraScale;
|
|
212
|
+
let cameraPosRelativeY = (screenPos.y - mainCanvasSize.y/2 + .5) / -cameraScale;
|
|
213
|
+
if (cameraAngle)
|
|
214
|
+
{
|
|
215
|
+
// apply camera rotation
|
|
216
|
+
const cos = Math.cos(-cameraAngle), sin = Math.sin(-cameraAngle);
|
|
217
|
+
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
218
|
+
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
219
|
+
cameraPosRelativeX = rotatedX;
|
|
220
|
+
cameraPosRelativeY = rotatedY;
|
|
221
|
+
}
|
|
222
|
+
return new Vector2(cameraPosRelativeX + cameraPos.x, cameraPosRelativeY + cameraPos.y);
|
|
183
223
|
}
|
|
184
224
|
|
|
185
225
|
/** Convert from world to screen space coordinates
|
|
@@ -188,10 +228,21 @@ function screenToWorld(screenPos)
|
|
|
188
228
|
* @memberof Draw */
|
|
189
229
|
function worldToScreen(worldPos)
|
|
190
230
|
{
|
|
231
|
+
let cameraPosRelativeX = worldPos.x - cameraPos.x;
|
|
232
|
+
let cameraPosRelativeY = worldPos.y - cameraPos.y;
|
|
233
|
+
if (cameraAngle)
|
|
234
|
+
{
|
|
235
|
+
// apply inverse camera rotation
|
|
236
|
+
const cos = Math.cos(cameraAngle), sin = Math.sin(cameraAngle);
|
|
237
|
+
const rotatedX = cameraPosRelativeX * cos - cameraPosRelativeY * sin;
|
|
238
|
+
const rotatedY = cameraPosRelativeX * sin + cameraPosRelativeY * cos;
|
|
239
|
+
cameraPosRelativeX = rotatedX;
|
|
240
|
+
cameraPosRelativeY = rotatedY;
|
|
241
|
+
}
|
|
191
242
|
return new Vector2
|
|
192
243
|
(
|
|
193
|
-
|
|
194
|
-
|
|
244
|
+
cameraPosRelativeX * cameraScale + mainCanvasSize.x/2 - .5,
|
|
245
|
+
cameraPosRelativeY * -cameraScale + mainCanvasSize.y/2 - .5
|
|
195
246
|
);
|
|
196
247
|
}
|
|
197
248
|
|
|
@@ -212,16 +263,16 @@ function getCameraSize() { return mainCanvasSize.scale(1/cameraScale); }
|
|
|
212
263
|
* @param {boolean} [screenSpace=false] - If true the pos and size are in screen space
|
|
213
264
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
214
265
|
* @memberof Draw */
|
|
215
|
-
function drawTile(pos, size=
|
|
266
|
+
function drawTile(pos, size=new Vector2(1), tileInfo, color=new Color,
|
|
216
267
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
217
268
|
{
|
|
218
269
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
219
|
-
ASSERT(
|
|
220
|
-
|
|
221
|
-
ASSERT(
|
|
222
|
-
ASSERT(
|
|
270
|
+
ASSERT(isVector2(pos) && pos.isValid(), 'drawTile pos should be a vec2');
|
|
271
|
+
ASSERT(isVector2(size) && size.isValid(), 'drawTile size should be a vec2');
|
|
272
|
+
ASSERT(isColor(color) && (!additiveColor || isColor(additiveColor)), 'drawTile color is invalid');
|
|
273
|
+
ASSERT(isNumber(angle), 'drawTile angle should be a number');
|
|
223
274
|
|
|
224
|
-
const textureInfo = tileInfo && tileInfo.
|
|
275
|
+
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
225
276
|
if (useWebGL)
|
|
226
277
|
{
|
|
227
278
|
if (screenSpace)
|
|
@@ -265,7 +316,7 @@ function drawTile(pos, size=vec2(1), tileInfo, color=new Color,
|
|
|
265
316
|
{
|
|
266
317
|
// normal canvas 2D rendering method (slower)
|
|
267
318
|
showWatermark && ++drawCount;
|
|
268
|
-
size =
|
|
319
|
+
size = new Vector2(size.x, -size.y); // fix upside down sprites
|
|
269
320
|
drawCanvas2D(pos, size, angle, mirror, (context)=>
|
|
270
321
|
{
|
|
271
322
|
if (textureInfo)
|
|
@@ -325,9 +376,9 @@ function drawLine(posA, posB, thickness=.1, color, useWebGL, screenSpace, contex
|
|
|
325
376
|
* @param {number} [lineWidth=0]
|
|
326
377
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
327
378
|
* @param {boolean} [screenSpace=false]
|
|
328
|
-
* @param {CanvasRenderingContext2D} [context=
|
|
379
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
329
380
|
* @memberof Draw */
|
|
330
|
-
function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=
|
|
381
|
+
function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=drawContext)
|
|
331
382
|
{
|
|
332
383
|
ASSERT(isColor(color) && isColor(lineColor));
|
|
333
384
|
context.fillStyle = color.toString();
|
|
@@ -353,9 +404,9 @@ function drawPoly(points, color=new Color, lineWidth=0, lineColor=new Color(0,0,
|
|
|
353
404
|
* @param {number} [lineWidth=0]
|
|
354
405
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
355
406
|
* @param {boolean} [screenSpace=false]
|
|
356
|
-
* @param {CanvasRenderingContext2D} [context=
|
|
407
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
357
408
|
* @memberof Draw */
|
|
358
|
-
function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=
|
|
409
|
+
function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=drawContext)
|
|
359
410
|
{
|
|
360
411
|
ASSERT(isColor(color) && isColor(lineColor));
|
|
361
412
|
if (!screenSpace)
|
|
@@ -384,21 +435,21 @@ function drawEllipse(pos, width=1, height=1, angle=0, color=new Color, lineWidth
|
|
|
384
435
|
* @param {number} [lineWidth=0]
|
|
385
436
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
386
437
|
* @param {boolean} [screenSpace=false]
|
|
387
|
-
* @param {CanvasRenderingContext2D} [context=
|
|
438
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
388
439
|
* @memberof Draw */
|
|
389
|
-
function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=
|
|
440
|
+
function drawCircle(pos, radius=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), screenSpace, context=drawContext)
|
|
390
441
|
{ drawEllipse(pos, radius, radius, 0, color, lineWidth, lineColor, screenSpace, context); }
|
|
391
442
|
|
|
392
443
|
/** Draw directly to a 2d canvas context in world space
|
|
393
444
|
* @param {Vector2} pos
|
|
394
445
|
* @param {Vector2} size
|
|
395
446
|
* @param {number} angle
|
|
396
|
-
* @param {boolean} mirror
|
|
397
|
-
* @param {Function} drawFunction
|
|
447
|
+
* @param {boolean} [mirror]
|
|
448
|
+
* @param {Function} [drawFunction]
|
|
398
449
|
* @param {boolean} [screenSpace=false]
|
|
399
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
450
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
400
451
|
* @memberof Draw */
|
|
401
|
-
function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, context=
|
|
452
|
+
function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpace=false, context=drawContext)
|
|
402
453
|
{
|
|
403
454
|
if (!screenSpace)
|
|
404
455
|
{
|
|
@@ -408,7 +459,7 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
408
459
|
}
|
|
409
460
|
context.save();
|
|
410
461
|
context.translate(pos.x+.5, pos.y+.5);
|
|
411
|
-
context.rotate(angle);
|
|
462
|
+
context.rotate(angle-cameraAngle);
|
|
412
463
|
context.scale(mirror ? -size.x : size.x, -size.y);
|
|
413
464
|
drawFunction(context);
|
|
414
465
|
context.restore();
|
|
@@ -425,9 +476,9 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, screenSpace, conte
|
|
|
425
476
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
426
477
|
* @param {string} [font=fontDefault]
|
|
427
478
|
* @param {number} [maxWidth]
|
|
428
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=
|
|
479
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
429
480
|
* @memberof Draw */
|
|
430
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=
|
|
481
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=drawContext)
|
|
431
482
|
{
|
|
432
483
|
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
|
|
433
484
|
}
|
|
@@ -473,22 +524,22 @@ function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineCol
|
|
|
473
524
|
context.lineJoin = 'round';
|
|
474
525
|
|
|
475
526
|
const lines = (text+'').split('\n');
|
|
476
|
-
|
|
477
|
-
|
|
527
|
+
let posY = pos.y;
|
|
528
|
+
posY -= (lines.length-1) * size/2; // center text vertically
|
|
478
529
|
lines.forEach(line=>
|
|
479
530
|
{
|
|
480
|
-
lineWidth && context.strokeText(line, pos.x,
|
|
481
|
-
context.fillText(line, pos.x,
|
|
482
|
-
|
|
531
|
+
lineWidth && context.strokeText(line, pos.x, posY, maxWidth);
|
|
532
|
+
context.fillText(line, pos.x, posY, maxWidth);
|
|
533
|
+
posY += size;
|
|
483
534
|
});
|
|
484
535
|
}
|
|
485
536
|
|
|
486
537
|
/** Enable normal or additive blend mode
|
|
487
538
|
* @param {boolean} [additive]
|
|
488
539
|
* @param {boolean} [useWebGL=glEnable]
|
|
489
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context
|
|
540
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context]
|
|
490
541
|
* @memberof Draw */
|
|
491
|
-
function setBlendMode(additive, useWebGL=glEnable, context)
|
|
542
|
+
function setBlendMode(additive=false, useWebGL=glEnable, context)
|
|
492
543
|
{
|
|
493
544
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
494
545
|
if (useWebGL)
|
|
@@ -496,7 +547,7 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
496
547
|
else
|
|
497
548
|
{
|
|
498
549
|
if (!context)
|
|
499
|
-
context =
|
|
550
|
+
context = drawContext;
|
|
500
551
|
context.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
501
552
|
}
|
|
502
553
|
}
|
|
@@ -507,7 +558,7 @@ function setBlendMode(additive, useWebGL=glEnable, context)
|
|
|
507
558
|
function combineCanvases()
|
|
508
559
|
{
|
|
509
560
|
// combine canvases
|
|
510
|
-
glCopyToContext(mainContext
|
|
561
|
+
glCopyToContext(mainContext);
|
|
511
562
|
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
512
563
|
|
|
513
564
|
// clear canvases
|