littlejsengine 1.16.2 → 1.17.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 +200 -166
- package/dist/littlejs.esm.js +1231 -1086
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +888 -735
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +884 -731
- package/examples/box2d/gameObjects.js +1 -1
- package/examples/breakout/game.js +5 -6
- package/examples/electron/index.html +2 -2
- package/examples/electron/tiles.png +0 -0
- package/examples/htmlMenu/tiles.png +0 -0
- package/examples/index.html +1 -1
- package/examples/logo.png +0 -0
- package/examples/module/tiles.png +0 -0
- package/examples/platformer/gameEffects.js +23 -22
- package/examples/platformer/gameLevel.js +24 -25
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/clock.js +3 -3
- package/examples/shorts/fontImage.js +4 -3
- package/examples/shorts/parallax.js +1 -1
- package/examples/shorts/sequencer.js +1 -1
- package/examples/shorts/shapes.js +1 -1
- package/examples/shorts/texture.js +10 -6
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +2 -0
- package/examples/starter/index.html +2 -2
- package/examples/starter/tiles.png +0 -0
- package/examples/typescript/tiles.png +0 -0
- package/examples/uiSystem/game.js +1 -1
- package/examples/uiSystem/tiles.png +0 -0
- package/package.json +1 -1
- package/plugins/postProcess.js +47 -28
- package/plugins/uiSystem.js +15 -15
- package/reference.md +1 -1
- package/src/engine.js +174 -174
- package/src/engineAudio.js +4 -6
- package/src/engineDebug.js +4 -4
- package/src/engineDraw.js +179 -122
- package/src/engineExport.js +326 -334
- package/src/engineFont.png +0 -0
- package/src/engineInput.js +14 -20
- package/src/engineMath.js +15 -104
- package/src/engineObject.js +26 -25
- package/src/engineParticles.js +2 -2
- package/src/engineTileLayer.js +196 -170
- package/src/engineUtilities.js +100 -4
- package/src/engineWebGL.js +123 -72
package/src/engineTileLayer.js
CHANGED
|
@@ -32,7 +32,7 @@ function tileCollisionGetData(pos)
|
|
|
32
32
|
|
|
33
33
|
/** Check if a tile layer collides with another object
|
|
34
34
|
* @param {Vector2} pos
|
|
35
|
-
* @param {Vector2} [size=(
|
|
35
|
+
* @param {Vector2} [size=vec2()]
|
|
36
36
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
37
37
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
38
38
|
* @return {TileCollisionLayer}
|
|
@@ -150,20 +150,20 @@ function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLa
|
|
|
150
150
|
class TileLayerData
|
|
151
151
|
{
|
|
152
152
|
/** Create a tile layer data object, one for each tile in a TileLayer
|
|
153
|
-
* @param {number} [tile]
|
|
153
|
+
* @param {number} [tile] - The tile to use, untextured if undefined
|
|
154
154
|
* @param {number} [direction] - Integer direction of tile, in 90 degree increments
|
|
155
|
-
* @param {boolean} [mirror]
|
|
156
|
-
* @param {Color} [color]
|
|
155
|
+
* @param {boolean} [mirror] - If the tile should be mirrored along the x axis
|
|
156
|
+
* @param {Color} [color] - Color of the tile */
|
|
157
157
|
constructor(tile, direction=0, mirror=false, color=new Color)
|
|
158
158
|
{
|
|
159
|
-
/** @property {number}
|
|
160
|
-
this.tile
|
|
161
|
-
/** @property {number}
|
|
159
|
+
/** @property {number} - The tile to use, untextured if undefined */
|
|
160
|
+
this.tile = tile;
|
|
161
|
+
/** @property {number} - Integer direction of tile, in 90 degree increments */
|
|
162
162
|
this.direction = direction;
|
|
163
163
|
/** @property {boolean} - If the tile should be mirrored along the x axis */
|
|
164
|
-
this.mirror
|
|
165
|
-
/** @property {Color}
|
|
166
|
-
this.color
|
|
164
|
+
this.mirror = mirror;
|
|
165
|
+
/** @property {Color} - Color of the tile */
|
|
166
|
+
this.color = color.copy();
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/** Set this tile to clear, it will not be rendered */
|
|
@@ -174,7 +174,7 @@ class TileLayerData
|
|
|
174
174
|
/**
|
|
175
175
|
* Canvas Layer - cached off screen rendering system
|
|
176
176
|
* - Contains an offscreen canvas that can be rendered to
|
|
177
|
-
* - WebGL rendering is optional, call
|
|
177
|
+
* - WebGL rendering is optional, call updateWebGL to enable/update
|
|
178
178
|
* @extends EngineObject
|
|
179
179
|
* @memberof TileLayers
|
|
180
180
|
* @example
|
|
@@ -188,8 +188,9 @@ class CanvasLayer extends EngineObject
|
|
|
188
188
|
* @param {number} [angle] - Angle the layer is rotated by
|
|
189
189
|
* @param {number} [renderOrder] - Objects sorted by renderOrder
|
|
190
190
|
* @param {Vector2} [canvasSize] - Default size of canvas, can be changed later
|
|
191
|
+
* @param {boolean} [useWebGL] - Should this layer use WebGL for rendering
|
|
191
192
|
*/
|
|
192
|
-
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
193
|
+
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512), useWebGL=glEnable)
|
|
193
194
|
{
|
|
194
195
|
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
195
196
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
@@ -199,13 +200,10 @@ class CanvasLayer extends EngineObject
|
|
|
199
200
|
/** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
|
|
200
201
|
this.context = this.canvas?.getContext('2d');
|
|
201
202
|
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
202
|
-
const useWebGL = false; // do not use webgl by default
|
|
203
203
|
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
204
|
-
/** @property {boolean} - True if WebGL texture needs to be refreshed */
|
|
205
|
-
this.refreshWebGL = false;
|
|
206
204
|
|
|
207
205
|
// disable physics by default
|
|
208
|
-
this.mass =
|
|
206
|
+
this.mass = 0;
|
|
209
207
|
}
|
|
210
208
|
|
|
211
209
|
/** Destroy this canvas layer */
|
|
@@ -220,7 +218,7 @@ class CanvasLayer extends EngineObject
|
|
|
220
218
|
// Render the layer, called automatically by the engine
|
|
221
219
|
render()
|
|
222
220
|
{
|
|
223
|
-
this.draw(this.pos, this.size, this.
|
|
221
|
+
this.draw(this.pos, this.size, this.color, this.angle, this.mirror, this.additiveColor);
|
|
224
222
|
}
|
|
225
223
|
|
|
226
224
|
/** Draw this canvas layer centered in world space, with color applied if using WebGL
|
|
@@ -233,103 +231,54 @@ class CanvasLayer extends EngineObject
|
|
|
233
231
|
* @param {boolean} [screenSpace] - If true the pos and size are in screen space
|
|
234
232
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context] - Canvas 2D context to draw to
|
|
235
233
|
* @memberof Draw */
|
|
236
|
-
draw(pos, size,
|
|
234
|
+
draw(pos, size, color=WHITE, angle=0, mirror=false, additiveColor, screenSpace=false, context)
|
|
237
235
|
{
|
|
238
|
-
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
239
|
-
if (useWebGL && this.refreshWebGL)
|
|
240
|
-
{
|
|
241
|
-
// update the WebGL texture
|
|
242
|
-
this.textureInfo.createWebGLTexture();
|
|
243
|
-
this.refreshWebGL = false;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
236
|
// draw the canvas layer as a single tile that uses the whole texture
|
|
247
237
|
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
238
|
+
const useWebGL = this.hasWebGL();
|
|
248
239
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
249
240
|
}
|
|
250
241
|
|
|
251
|
-
/**
|
|
252
|
-
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
253
|
-
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
254
|
-
* @memberof TileLayers
|
|
255
|
-
*/
|
|
256
|
-
|
|
257
|
-
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
258
|
-
* @param {Vector2} pos
|
|
259
|
-
* @param {Vector2} size
|
|
260
|
-
* @param {number} angle
|
|
261
|
-
* @param {boolean} mirror
|
|
262
|
-
* @param {Canvas2DDrawCallback} drawFunction */
|
|
263
|
-
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
264
|
-
{
|
|
265
|
-
if (!this.context) return;
|
|
266
|
-
|
|
267
|
-
const context = this.context;
|
|
268
|
-
context.save();
|
|
269
|
-
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
270
|
-
size = size.multiply(this.tileInfo.size);
|
|
271
|
-
context.translate(pos.x, this.canvas.height - pos.y);
|
|
272
|
-
context.rotate(angle);
|
|
273
|
-
context.scale(mirror ? -size.x : size.x, size.y);
|
|
274
|
-
drawFunction(context);
|
|
275
|
-
context.restore();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
242
|
/** Draw a tile onto the layer canvas in world space
|
|
279
243
|
* @param {Vector2} pos
|
|
280
|
-
* @param {Vector2} [size=(1
|
|
244
|
+
* @param {Vector2} [size=vec2(1)]
|
|
281
245
|
* @param {TileInfo} [tileInfo]
|
|
282
|
-
* @param {Color} [color=
|
|
283
|
-
* @param {number} [angle
|
|
284
|
-
* @param {boolean} [mirror
|
|
285
|
-
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle, mirror)
|
|
246
|
+
* @param {Color} [color=WHITE]
|
|
247
|
+
* @param {number} [angle]
|
|
248
|
+
* @param {boolean} [mirror] */
|
|
249
|
+
drawTile(pos, size=vec2(1), tileInfo, color=new Color, angle=0, mirror=false)
|
|
286
250
|
{
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// untextured
|
|
301
|
-
context.fillStyle = color.toString();
|
|
302
|
-
context.fillRect(-.5, -.5, 1, 1);
|
|
303
|
-
}
|
|
304
|
-
});
|
|
251
|
+
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
252
|
+
size = size.multiply(this.tileInfo.size);
|
|
253
|
+
pos.y = this.canvas.height - pos.y;
|
|
254
|
+
|
|
255
|
+
// draw the tile onto the layer canvas
|
|
256
|
+
const oldMainCanvasSize = mainCanvasSize;
|
|
257
|
+
mainCanvasSize = vec2(this.canvas.width, this.canvas.height);
|
|
258
|
+
const useWebGL = this.hasWebGL();
|
|
259
|
+
useWebGL && glSetRenderTarget(this.textureInfo.glTexture);
|
|
260
|
+
const drawContext = useWebGL ? undefined : this.context;
|
|
261
|
+
drawTile(pos, size, tileInfo, color, angle, mirror, undefined, useWebGL, true, drawContext);
|
|
262
|
+
useWebGL && glSetRenderTarget();
|
|
263
|
+
mainCanvasSize = oldMainCanvasSize;
|
|
305
264
|
}
|
|
306
265
|
|
|
307
266
|
/** Draw a rectangle onto the layer canvas in world space
|
|
308
267
|
* @param {Vector2} pos
|
|
309
|
-
* @param {Vector2} [size=(1
|
|
310
|
-
* @param {Color} [color=
|
|
311
|
-
* @param {number} [angle
|
|
268
|
+
* @param {Vector2} [size=vec2(1)]
|
|
269
|
+
* @param {Color} [color=WHITE]
|
|
270
|
+
* @param {number} [angle] */
|
|
312
271
|
drawRect(pos, size, color, angle)
|
|
313
272
|
{ this.drawTile(pos, size, undefined, color, angle); }
|
|
314
273
|
|
|
315
|
-
/** Create
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
*/
|
|
319
|
-
useWebGL(enable=true, immediate=false)
|
|
320
|
-
{
|
|
321
|
-
if (!immediate && enable && this.textureInfo.hasWebGL())
|
|
322
|
-
{
|
|
323
|
-
// refresh the texture when needed
|
|
324
|
-
this.refreshWebGL = true;
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
274
|
+
/** Create WebGL texture if necessary and copy layer canvas to it */
|
|
275
|
+
updateWebGL()
|
|
276
|
+
{ this.textureInfo.createWebGLTexture(); }
|
|
327
277
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
}
|
|
278
|
+
/** Check if this layer is using WebGL
|
|
279
|
+
* @return {boolean} */
|
|
280
|
+
hasWebGL()
|
|
281
|
+
{ return glEnable && this.textureInfo.hasWebGL(); }
|
|
333
282
|
}
|
|
334
283
|
|
|
335
284
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -348,50 +297,67 @@ class CanvasLayer extends EngineObject
|
|
|
348
297
|
class TileLayer extends CanvasLayer
|
|
349
298
|
{
|
|
350
299
|
/** Create a tile layer object
|
|
351
|
-
* @param {Vector2} position
|
|
352
|
-
* @param {Vector2} size
|
|
353
|
-
* @param {TileInfo} [tileInfo]
|
|
300
|
+
* @param {Vector2} position - World space position
|
|
301
|
+
* @param {Vector2} size - World space size
|
|
302
|
+
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
354
303
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
355
304
|
*/
|
|
356
305
|
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
357
306
|
{
|
|
358
307
|
const canvasSize = tileInfo ? size.multiply(tileInfo.size) : size;
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
308
|
+
const useWebGL = true;
|
|
309
|
+
super(position, size, 0, renderOrder, canvasSize, useWebGL);
|
|
310
|
+
|
|
311
|
+
/** @property {TileInfo} - Default tile info for layer */
|
|
312
|
+
this.tileInfo = undefined;
|
|
313
|
+
/** @property {Array<TileLayerData>} - Default tile info for layer */
|
|
365
314
|
this.data = [];
|
|
366
|
-
|
|
367
|
-
|
|
315
|
+
/** @property {boolean} - Is this layer using a webgl texture? */
|
|
316
|
+
this.isUsingWebGL = false;
|
|
368
317
|
|
|
369
318
|
if (headlessMode)
|
|
370
319
|
{
|
|
371
|
-
// disable rendering
|
|
372
|
-
this.
|
|
373
|
-
this.
|
|
374
|
-
this.redrawStart
|
|
375
|
-
this.redrawEnd
|
|
376
|
-
this.drawTileData
|
|
377
|
-
this.
|
|
378
|
-
this.
|
|
320
|
+
// disable rendering in headless mode
|
|
321
|
+
this.render = () => {};
|
|
322
|
+
this.redraw = () => {};
|
|
323
|
+
this.redrawStart = () => {};
|
|
324
|
+
this.redrawEnd = () => {};
|
|
325
|
+
this.drawTileData = () => {};
|
|
326
|
+
this.redrawTileData = () => {};
|
|
327
|
+
this.drawLayerTile = () => {};
|
|
328
|
+
this.drawLayerRect = () => {};
|
|
329
|
+
this.clearLayerRect = () => {};
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (tileInfo)
|
|
334
|
+
{
|
|
335
|
+
// set tile info
|
|
336
|
+
this.tileInfo = tileInfo.frame(0);
|
|
337
|
+
this.tileInfo.bleed = 0; // disable bleed for tile layers
|
|
379
338
|
}
|
|
339
|
+
|
|
340
|
+
// init tile data
|
|
341
|
+
for (let j = this.size.area(); j--;)
|
|
342
|
+
this.data.push(new TileLayerData);
|
|
380
343
|
}
|
|
381
344
|
|
|
382
345
|
/** Set data at a given position in the array
|
|
383
346
|
* @param {Vector2} layerPos - Local position in array
|
|
384
|
-
* @param {TileLayerData} data
|
|
347
|
+
* @param {TileLayerData} data - Data to set
|
|
385
348
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
386
349
|
setData(layerPos, data, redraw=false)
|
|
387
350
|
{
|
|
351
|
+
layerPos = layerPos.floor();
|
|
388
352
|
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
389
353
|
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
354
|
+
|
|
355
|
+
if (!layerPos.arrayCheck(this.size)) return;
|
|
356
|
+
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
357
|
+
|
|
358
|
+
if (!redraw) return;
|
|
359
|
+
const isRedraw = drawContext === this.context;
|
|
360
|
+
isRedraw ? this.drawTileData(layerPos) : this.redrawTileData(layerPos);
|
|
395
361
|
}
|
|
396
362
|
|
|
397
363
|
/** Get data at a given position in the array
|
|
@@ -400,7 +366,18 @@ class TileLayer extends CanvasLayer
|
|
|
400
366
|
getData(layerPos)
|
|
401
367
|
{
|
|
402
368
|
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
403
|
-
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
369
|
+
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Update the tile layer, refresh texture if needed
|
|
373
|
+
update()
|
|
374
|
+
{
|
|
375
|
+
if (!glEnable && this.isUsingWebGL)
|
|
376
|
+
{
|
|
377
|
+
// redraw the layer if webgl was disabled or context lost
|
|
378
|
+
this.isUsingWebGL = false;
|
|
379
|
+
this.redraw();
|
|
380
|
+
}
|
|
404
381
|
}
|
|
405
382
|
|
|
406
383
|
// Render the tile layer, called automatically by the engine
|
|
@@ -408,40 +385,35 @@ class TileLayer extends CanvasLayer
|
|
|
408
385
|
{
|
|
409
386
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
410
387
|
|
|
411
|
-
if (this.refreshWebGL)
|
|
412
|
-
{
|
|
413
|
-
// update the WebGL texture
|
|
414
|
-
this.textureInfo.createWebGLTexture();
|
|
415
|
-
this.refreshWebGL = false;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// draw the tile layer as a single tile
|
|
419
|
-
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
420
388
|
const size = this.drawSize || this.size;
|
|
421
389
|
const pos = this.pos.add(size.scale(.5));
|
|
422
|
-
|
|
423
|
-
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
390
|
+
this.draw(pos, this.size, this.color, this.angle, this.mirror, this.additiveColor);
|
|
424
391
|
}
|
|
425
392
|
|
|
393
|
+
/** Called after this layer is redrawn, does nothing by default */
|
|
394
|
+
onRedraw() {}
|
|
395
|
+
|
|
426
396
|
/** Draw all the tile data to an offscreen canvas
|
|
427
|
-
* - This may be slow
|
|
397
|
+
* - This may be slow if not using webgl but only needs to be done once */
|
|
428
398
|
redraw()
|
|
429
399
|
{
|
|
430
400
|
this.redrawStart(true);
|
|
431
401
|
for (let x = this.size.x; x--;)
|
|
432
402
|
for (let y = this.size.y; y--;)
|
|
433
403
|
this.drawTileData(vec2(x,y), false);
|
|
404
|
+
this.hasWebGL() && glFlush();
|
|
405
|
+
this.onRedraw();
|
|
434
406
|
this.redrawEnd();
|
|
435
|
-
this.useWebGL();
|
|
436
407
|
}
|
|
437
408
|
|
|
438
409
|
/** Call to start the redraw process
|
|
439
|
-
* - This can be used to manually update
|
|
410
|
+
* - This can be used to manually update parts of the level
|
|
440
411
|
* @param {boolean} [clear] - Should it clear the canvas before drawing */
|
|
441
412
|
redrawStart(clear=false)
|
|
442
413
|
{
|
|
443
414
|
if (!this.context) return;
|
|
444
|
-
|
|
415
|
+
ASSERT(drawContext !== this.context);
|
|
416
|
+
|
|
445
417
|
// save current render settings
|
|
446
418
|
/** @type {[CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number, Color]} */
|
|
447
419
|
this.savedRenderSettings = [drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor];
|
|
@@ -449,33 +421,37 @@ class TileLayer extends CanvasLayer
|
|
|
449
421
|
// set the draw canvas and context to this layer
|
|
450
422
|
// use camera settings to match this layer's canvas
|
|
451
423
|
drawContext = this.context;
|
|
452
|
-
|
|
453
|
-
const tileSize = this.tileInfo ? this.tileInfo.size : vec2(1);
|
|
454
|
-
cameraScale = tileSize.x;
|
|
455
|
-
canvasClearColor = CLEAR_BLACK;
|
|
424
|
+
const tileSize = this.tileInfo?.size ?? vec2(1);
|
|
456
425
|
mainCanvasSize = this.size.multiply(tileSize);
|
|
457
|
-
|
|
426
|
+
canvasClearColor = CLEAR_BLACK;
|
|
427
|
+
cameraPos = this.size.multiply(tileSize).scale(.5);
|
|
428
|
+
cameraScale = 1;
|
|
429
|
+
|
|
430
|
+
// set render target to this layer
|
|
431
|
+
this.isUsingWebGL = this.hasWebGL();
|
|
432
|
+
if (this.isUsingWebGL)
|
|
433
|
+
glSetRenderTarget(this.textureInfo.glTexture, clear);
|
|
434
|
+
else
|
|
458
435
|
{
|
|
459
|
-
//
|
|
460
|
-
this.
|
|
461
|
-
|
|
436
|
+
// disable smoothing for pixel art
|
|
437
|
+
this.context.imageSmoothingEnabled = !tilesPixelated;
|
|
438
|
+
if (clear)
|
|
439
|
+
{
|
|
440
|
+
// clear and set size
|
|
441
|
+
this.canvas.width = mainCanvasSize.x;
|
|
442
|
+
this.canvas.height = mainCanvasSize.y;
|
|
443
|
+
}
|
|
462
444
|
}
|
|
463
|
-
|
|
464
|
-
// disable smoothing for pixel art
|
|
465
|
-
drawContext.imageSmoothingEnabled = !tilesPixelated;
|
|
466
|
-
|
|
467
|
-
// setup gl rendering if enabled
|
|
468
|
-
glPreRender();
|
|
469
445
|
}
|
|
470
446
|
|
|
471
447
|
/** Call to end the redraw process */
|
|
472
448
|
redrawEnd()
|
|
473
449
|
{
|
|
474
450
|
if (!this.context) return;
|
|
451
|
+
ASSERT(drawContext === this.context);
|
|
475
452
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
//saveCanvas(this.canvas);
|
|
453
|
+
if (glEnable && this.textureInfo.glTexture)
|
|
454
|
+
glSetRenderTarget();
|
|
479
455
|
|
|
480
456
|
// set stuff back to normal
|
|
481
457
|
[drawContext, mainCanvasSize, cameraPos, cameraScale, canvasClearColor] = this.savedRenderSettings;
|
|
@@ -490,24 +466,75 @@ class TileLayer extends CanvasLayer
|
|
|
490
466
|
drawTileData(layerPos, clear=true)
|
|
491
467
|
{
|
|
492
468
|
if (!this.context) return;
|
|
469
|
+
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
493
470
|
|
|
494
|
-
// clear out where the tile was, for
|
|
495
|
-
const
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const pos = layerPos.multiply(s);
|
|
499
|
-
this.context.clearRect(pos.x, this.canvas.height-pos.y, s.x, -s.y);
|
|
500
|
-
}
|
|
471
|
+
// clear out where the tile was, can be skipped for fully opaque tiles
|
|
472
|
+
const drawSize = this.tileInfo?.size ?? vec2(1);
|
|
473
|
+
const drawPos = layerPos.multiply(drawSize);
|
|
474
|
+
clear && this.clearLayerRect(drawPos, drawSize);
|
|
501
475
|
|
|
502
476
|
// draw the tile if it has layer data
|
|
503
477
|
const d = this.getData(layerPos);
|
|
504
|
-
if (d.tile
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
478
|
+
if (!d.tile) return;
|
|
479
|
+
|
|
480
|
+
const tileInfo = this.tileInfo && this.tileInfo.tile(d.tile);
|
|
481
|
+
this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Draw the tile at a given position in the tile grid
|
|
485
|
+
* This can be used to clear tiles when they are destroyed
|
|
486
|
+
* For better performance use drawTileData inside a redrawStart/End block
|
|
487
|
+
* @param {Vector2} layerPos
|
|
488
|
+
* @param {boolean} [clear] - should the old tile be cleared
|
|
489
|
+
*/
|
|
490
|
+
redrawTileData(layerPos, clear=true)
|
|
491
|
+
{
|
|
492
|
+
if (!this.context) return;
|
|
493
|
+
ASSERT(drawContext !== this.context, 'redrawStart() should not be active when calling redrawTileData(), instead use drawTileData()');
|
|
494
|
+
|
|
495
|
+
this.redrawStart();
|
|
496
|
+
this.drawTileData(layerPos, clear);
|
|
497
|
+
this.redrawEnd();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Draw textured tile in layer space
|
|
501
|
+
* @param {Vector2} pos - Position in pixel coordinates
|
|
502
|
+
* @param {Vector2} [size=vec2(1)] - Size of the tile
|
|
503
|
+
* @param {TileInfo} [tileInfo] - Tile info to use, untextured if undefined
|
|
504
|
+
* @param {Color} [color=WHITE] - Color to modulate with
|
|
505
|
+
* @param {number} [angle] - Angle to rotate by
|
|
506
|
+
* @param {boolean} [mirror] - Is image flipped along the Y axis?
|
|
507
|
+
* @param {Color} [additiveColor] - Additive color to be applied if any */
|
|
508
|
+
drawLayerTile(pos, size=vec2(1), tileInfo, color=WHITE,
|
|
509
|
+
angle=0, mirror, additiveColor)
|
|
510
|
+
{
|
|
511
|
+
const drawPos = pos.add(size.scale(.5));
|
|
512
|
+
drawTile(drawPos, size, tileInfo, color, angle, mirror, additiveColor);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/** Clear a rectangle in layer space
|
|
516
|
+
* @param {Vector2} pos
|
|
517
|
+
* @param {Vector2} size
|
|
518
|
+
* @param {Color} [color=WHITE] - Color to modulate with
|
|
519
|
+
* @param {number} [angle] - Angle to rotate by
|
|
520
|
+
*/
|
|
521
|
+
drawLayerRect(pos, size, color, angle=0)
|
|
522
|
+
{ this.drawLayerTile(pos, size, undefined, color, angle); }
|
|
523
|
+
|
|
524
|
+
/** Clear a rectangle in layer space
|
|
525
|
+
* @param {Vector2} pos - position in pixel coordinates
|
|
526
|
+
* @param {Vector2} size
|
|
527
|
+
*/
|
|
528
|
+
clearLayerRect(pos, size)
|
|
529
|
+
{
|
|
530
|
+
ASSERT(drawContext === this.context, 'must call redrawStart() before clearing tiles');
|
|
531
|
+
|
|
532
|
+
const x = pos.x, y = this.canvas.height - pos.y - size.y;
|
|
533
|
+
const useWebGL = this.hasWebGL();
|
|
534
|
+
if (useWebGL)
|
|
535
|
+
glClearRect(x, y, size.x, size.y);
|
|
536
|
+
else
|
|
537
|
+
this.context.clearRect(x, y, size.x, size.y);
|
|
511
538
|
}
|
|
512
539
|
}
|
|
513
540
|
|
|
@@ -516,7 +543,6 @@ class TileLayer extends CanvasLayer
|
|
|
516
543
|
* Tile Collision Layer - a tile layer with collision
|
|
517
544
|
* - adds collision data and functions to TileLayer
|
|
518
545
|
* - there can be multiple tile collision layers
|
|
519
|
-
* - tile collision layers should not overlap each other
|
|
520
546
|
* @extends TileLayer
|
|
521
547
|
* @memberof TileLayers
|
|
522
548
|
*/
|
|
@@ -588,7 +614,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
588
614
|
|
|
589
615
|
/** Check if collision with another object should occur
|
|
590
616
|
* @param {Vector2} pos
|
|
591
|
-
* @param {Vector2} [size=(
|
|
617
|
+
* @param {Vector2} [size=vec2()]
|
|
592
618
|
* @param {EngineObject} [object]
|
|
593
619
|
* @return {boolean} */
|
|
594
620
|
collisionTest(pos, size=new Vector2, object)
|
package/src/engineUtilities.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LittleJS Utility Classes and Functions
|
|
3
|
-
* - General purpose
|
|
4
|
-
* - Vector2 - fast, simple, easy 2D vector class
|
|
5
|
-
* - Color - holds a rgba color with some math functions
|
|
3
|
+
* - General purpose utilities
|
|
6
4
|
* - Timer - tracks time automatically
|
|
7
|
-
* - RandomGenerator - seeded random number generator
|
|
8
5
|
* @namespace Utilities
|
|
9
6
|
*/
|
|
10
7
|
|
|
@@ -70,6 +67,9 @@ function saveCanvas(canvas, filename='screenshot', type='image/png')
|
|
|
70
67
|
* @memberof Utilities */
|
|
71
68
|
function saveDataURL(url, filename='download', revokeTime)
|
|
72
69
|
{
|
|
70
|
+
ASSERT(isString(url), 'saveDataURL requires url string');
|
|
71
|
+
ASSERT(isString(filename), 'saveDataURL requires filename string');
|
|
72
|
+
|
|
73
73
|
// create link for saving screenshots
|
|
74
74
|
const link = document.createElement('a');
|
|
75
75
|
link.download = filename;
|
|
@@ -77,4 +77,100 @@ function saveDataURL(url, filename='download', revokeTime)
|
|
|
77
77
|
link.click();
|
|
78
78
|
if (revokeTime !== undefined)
|
|
79
79
|
setTimeout(()=> URL.revokeObjectURL(url), revokeTime);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Share content using the native share dialog if available
|
|
83
|
+
* @param {string} title - title of the share
|
|
84
|
+
* @param {string} url - url to share
|
|
85
|
+
* @param {Function} [callback] - Called when share is complete
|
|
86
|
+
* @memberof Utilities */
|
|
87
|
+
function shareURL(title, url, callback)
|
|
88
|
+
{
|
|
89
|
+
ASSERT(isString(title), 'shareURL requires title string');
|
|
90
|
+
ASSERT(isString(url), 'shareURL requires url string');
|
|
91
|
+
navigator.share?.({title, url}).then(()=>callback?.());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Timer object tracks how long has passed since it was set
|
|
98
|
+
* @memberof Engine
|
|
99
|
+
* @example
|
|
100
|
+
* let a = new Timer; // creates a timer that is not set
|
|
101
|
+
* a.set(3); // sets the timer to 3 seconds
|
|
102
|
+
*
|
|
103
|
+
* let b = new Timer(1); // creates a timer with 1 second left
|
|
104
|
+
* b.unset(); // unset the timer
|
|
105
|
+
*/
|
|
106
|
+
class Timer
|
|
107
|
+
{
|
|
108
|
+
/** Create a timer object set time passed in
|
|
109
|
+
* @param {number} [timeLeft] - How much time left before the timer
|
|
110
|
+
* @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
|
|
111
|
+
constructor(timeLeft, useRealTime=false)
|
|
112
|
+
{
|
|
113
|
+
ASSERT(timeLeft === undefined || isNumber(timeLeft), 'Constructed Timer is invalid.', timeLeft);
|
|
114
|
+
this.useRealTime = useRealTime;
|
|
115
|
+
const globalTime = this.getGlobalTime();
|
|
116
|
+
this.time = timeLeft === undefined ? undefined : globalTime + timeLeft;
|
|
117
|
+
this.setTime = timeLeft;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Set the timer with seconds passed in
|
|
121
|
+
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds */
|
|
122
|
+
set(timeLeft=0)
|
|
123
|
+
{
|
|
124
|
+
ASSERT(isNumber(timeLeft), 'Timer is invalid.', timeLeft);
|
|
125
|
+
const globalTime = this.getGlobalTime();
|
|
126
|
+
this.time = globalTime + timeLeft;
|
|
127
|
+
this.setTime = timeLeft;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Set if the timer should keep running even when the game is paused
|
|
131
|
+
* @param {boolean} [useRealTime] */
|
|
132
|
+
setUseRealTime(useRealTime=true)
|
|
133
|
+
{
|
|
134
|
+
ASSERT(!this.isSet(), 'Cannot change global time setting while timer is set.');
|
|
135
|
+
this.useRealTime = useRealTime;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Unset the timer */
|
|
139
|
+
unset() { this.time = undefined; }
|
|
140
|
+
|
|
141
|
+
/** Returns true if set
|
|
142
|
+
* @return {boolean} */
|
|
143
|
+
isSet() { return this.time !== undefined; }
|
|
144
|
+
|
|
145
|
+
/** Returns true if set and has not elapsed
|
|
146
|
+
* @return {boolean} */
|
|
147
|
+
active() { return this.getGlobalTime() < this.time; }
|
|
148
|
+
|
|
149
|
+
/** Returns true if set and elapsed
|
|
150
|
+
* @return {boolean} */
|
|
151
|
+
elapsed() { return this.getGlobalTime() >= this.time; }
|
|
152
|
+
|
|
153
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
154
|
+
* @return {number} */
|
|
155
|
+
get() { return this.isSet()? this.getGlobalTime() - this.time : 0; }
|
|
156
|
+
|
|
157
|
+
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
158
|
+
* @return {number} */
|
|
159
|
+
getPercent() { return this.isSet()? 1-percent(this.time - this.getGlobalTime(), 0, this.setTime) : 0; }
|
|
160
|
+
|
|
161
|
+
/** Get the time this timer was set to, returns 0 if not set
|
|
162
|
+
* @return {number} */
|
|
163
|
+
getSetTime() { return this.isSet() ? this.setTime : 0; }
|
|
164
|
+
|
|
165
|
+
/** Get the current global time this timer is based on
|
|
166
|
+
* @return {number} */
|
|
167
|
+
getGlobalTime() { return this.useRealTime ? timeReal : time; }
|
|
168
|
+
|
|
169
|
+
/** Returns this timer expressed as a string
|
|
170
|
+
* @return {string} */
|
|
171
|
+
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
172
|
+
|
|
173
|
+
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
174
|
+
* @return {number} */
|
|
175
|
+
valueOf() { return this.get(); }
|
|
80
176
|
}
|