littlejsengine 1.14.11 → 1.14.19
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 +303 -137
- package/dist/littlejs.esm.js +936 -463
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +934 -462
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +859 -417
- package/examples/box2d/game.js +3 -2
- package/examples/box2d/gameObjects.js +2 -2
- package/examples/box2d/tiles.png +0 -0
- package/examples/breakout/game.js +5 -5
- package/examples/breakout/gameObjects.js +2 -2
- package/examples/breakoutTutorial/README.md +32 -32
- package/examples/breakoutTutorial/game.js +1 -1
- package/examples/electron/game.js +4 -4
- package/examples/electron/index.html +2 -2
- package/examples/electron/package.json +1 -8
- package/examples/index.html +67 -56
- package/examples/module/game.js +4 -4
- package/examples/platformer/gameEffects.js +5 -5
- package/examples/platformer/gameLevel.js +1 -1
- package/examples/platformer/gameObjects.js +4 -4
- package/examples/puzzle/game.js +1 -1
- package/examples/shorts/animation.js +1 -1
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/blending.js +8 -14
- package/examples/shorts/box2d.js +7 -3
- package/examples/shorts/box2dCar.js +2 -1
- package/examples/shorts/empty.js +30 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +10 -4
- package/examples/shorts/landerGame.js +11 -8
- package/examples/shorts/medals.js +4 -4
- package/examples/shorts/music.js +29 -56
- package/examples/shorts/musicPlayer.js +135 -0
- package/examples/shorts/nineSlice.js +34 -15
- package/examples/shorts/parallax.js +5 -4
- package/examples/shorts/particles.js +15 -15
- package/examples/shorts/piano.js +15 -21
- package/examples/shorts/pongGame.js +6 -4
- package/examples/shorts/raycasting.js +9 -4
- package/examples/shorts/sequencer.js +122 -0
- package/examples/shorts/shader.js +29 -0
- package/examples/shorts/shapes.js +7 -4
- package/examples/shorts/slidingPuzzle.js +4 -2
- package/examples/shorts/sound.js +18 -9
- package/examples/shorts/spaceGame.js +12 -8
- package/examples/shorts/spriteAtlas.js +7 -7
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/systemFont.js +2 -2
- package/examples/shorts/texture.js +2 -2
- package/examples/shorts/tileLayer.js +10 -10
- package/examples/shorts/tiltedView.js +24 -5
- package/examples/shorts/timers.js +10 -0
- package/examples/shorts/topDown.js +4 -1
- package/examples/shorts/uiSystem.js +8 -5
- package/examples/starter/game.js +4 -4
- package/examples/starter/index.html +2 -2
- package/examples/style.css +1 -0
- package/examples/typescript/game.js +4 -4
- package/examples/typescript/game.ts +4 -4
- package/examples/uiSystem/game.js +4 -3
- package/package.json +4 -2
- package/plugins/box2d.js +17 -2
- package/plugins/newgrounds.js +7 -5
- package/plugins/postProcess.js +76 -53
- package/plugins/uiSystem.js +192 -53
- package/plugins/zzfxm.js +5 -1
- package/reference.md +1 -1
- package/src/engine.js +60 -23
- package/src/engineAudio.js +44 -17
- package/src/engineDebug.js +75 -45
- package/src/engineDraw.js +94 -59
- package/src/engineExport.js +2 -1
- package/src/engineMedals.js +10 -2
- package/src/engineObject.js +14 -10
- package/src/engineParticles.js +60 -47
- package/src/engineSettings.js +7 -7
- package/src/engineTileLayer.js +73 -56
- package/src/engineUtilities.js +67 -20
- package/src/engineWebGL.js +134 -63
package/dist/littlejs.release.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.14.
|
|
36
|
+
const engineVersion = '1.14.19';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -94,29 +94,60 @@ let frameTimeLastMS = 0, frameTimeBufferMS = 0, averageFPS = 0;
|
|
|
94
94
|
///////////////////////////////////////////////////////////////////////////////
|
|
95
95
|
// plugin hooks
|
|
96
96
|
|
|
97
|
-
const
|
|
97
|
+
const pluginList = [];
|
|
98
|
+
class EnginePlugin
|
|
99
|
+
{
|
|
100
|
+
constructor(update, render, glContextLost, glContextRestored)
|
|
101
|
+
{
|
|
102
|
+
this.update = update;
|
|
103
|
+
this.render = render;
|
|
104
|
+
this.glContextLost = glContextLost;
|
|
105
|
+
this.glContextRestored = glContextRestored;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @callback PluginCallback - Update or render function for a plugin
|
|
111
|
+
* @memberof Engine
|
|
112
|
+
*/
|
|
98
113
|
|
|
99
114
|
/** Add a new update function for a plugin
|
|
100
|
-
* @param {
|
|
101
|
-
* @param {
|
|
115
|
+
* @param {PluginCallback} [update]
|
|
116
|
+
* @param {PluginCallback} [render]
|
|
117
|
+
* @param {PluginCallback} [glContextLost]
|
|
118
|
+
* @param {PluginCallback} [glContextRestored]
|
|
102
119
|
* @memberof Engine */
|
|
103
|
-
function engineAddPlugin(
|
|
120
|
+
function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
104
121
|
{
|
|
105
|
-
|
|
106
|
-
ASSERT(!
|
|
107
|
-
|
|
108
|
-
|
|
122
|
+
// make sure plugin functions are unique
|
|
123
|
+
ASSERT(!pluginList.find(p=>
|
|
124
|
+
p.update === update && p.render === render &&
|
|
125
|
+
p.glContextLost === glContextLost &&
|
|
126
|
+
p.glContextRestored === glContextRestored));
|
|
127
|
+
|
|
128
|
+
const plugin = new EnginePlugin(update, render, glContextLost, glContextRestored);
|
|
129
|
+
pluginList.push(plugin);
|
|
109
130
|
}
|
|
110
131
|
|
|
111
132
|
///////////////////////////////////////////////////////////////////////////////
|
|
112
133
|
// Main Engine Functions
|
|
113
134
|
|
|
135
|
+
/**
|
|
136
|
+
* @callback GameInitCallback - Called after the engine starts, can be async
|
|
137
|
+
* @returns {void|Promise<void>}
|
|
138
|
+
* @memberof Engine
|
|
139
|
+
*/
|
|
140
|
+
/**
|
|
141
|
+
* @callback GameCallback - Update or render function for the game
|
|
142
|
+
* @memberof Engine
|
|
143
|
+
*/
|
|
144
|
+
|
|
114
145
|
/** Startup LittleJS engine with your callback functions
|
|
115
|
-
* @param {
|
|
116
|
-
* @param {
|
|
117
|
-
* @param {
|
|
118
|
-
* @param {
|
|
119
|
-
* @param {
|
|
146
|
+
* @param {GameInitCallback} gameInit - Called once after the engine starts up, can be async for loading
|
|
147
|
+
* @param {GameCallback} gameUpdate - Called every frame before objects are updated (60fps), use for game logic
|
|
148
|
+
* @param {GameCallback} gameUpdatePost - Called after physics and objects are updated, even when paused, use for UI updates
|
|
149
|
+
* @param {GameCallback} gameRender - Called before objects are rendered, use for drawing backgrounds/world elements
|
|
150
|
+
* @param {GameCallback} gameRenderPost - Called after objects are rendered, use for drawing UI/overlays
|
|
120
151
|
* @param {Array<string>} [imageSources=[]] - List of image file paths to preload (e.g., ['player.png', 'tiles.png'])
|
|
121
152
|
* @param {HTMLElement} [rootElement] - Root DOM element to attach canvas to, defaults to document.body
|
|
122
153
|
* @example
|
|
@@ -180,7 +211,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
180
211
|
wasUpdated = true;
|
|
181
212
|
updateCanvas();
|
|
182
213
|
inputUpdate();
|
|
183
|
-
|
|
214
|
+
pluginList.forEach(plugin=>plugin.update?.());
|
|
184
215
|
|
|
185
216
|
// update object transforms even when paused
|
|
186
217
|
for (const o of engineObjects)
|
|
@@ -213,7 +244,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
213
244
|
updateCanvas();
|
|
214
245
|
inputUpdate();
|
|
215
246
|
gameUpdate();
|
|
216
|
-
|
|
247
|
+
pluginList.forEach(plugin=>plugin.update?.());
|
|
217
248
|
engineObjectsUpdate();
|
|
218
249
|
|
|
219
250
|
// do post update
|
|
@@ -247,7 +278,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
247
278
|
for (const o of engineObjects)
|
|
248
279
|
o.destroyed || o.render();
|
|
249
280
|
gameRenderPost();
|
|
250
|
-
|
|
281
|
+
pluginList.forEach(plugin=>plugin.render?.());
|
|
251
282
|
touchGamepadRender();
|
|
252
283
|
debugRender();
|
|
253
284
|
glFlush();
|
|
@@ -368,11 +399,12 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
368
399
|
const promises = imageSources.map((src, textureIndex)=>
|
|
369
400
|
new Promise(resolve =>
|
|
370
401
|
{
|
|
402
|
+
ASSERT(isString(src), 'imageSources must be an array of strings');
|
|
403
|
+
|
|
371
404
|
const image = new Image;
|
|
372
405
|
image.onerror = image.onload = ()=>
|
|
373
406
|
{
|
|
374
407
|
const textureInfo = new TextureInfo(image);
|
|
375
|
-
textureInfo.createWebGLTexture();
|
|
376
408
|
textureInfos[textureIndex] = textureInfo;
|
|
377
409
|
resolve();
|
|
378
410
|
}
|
|
@@ -388,7 +420,6 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
388
420
|
{
|
|
389
421
|
const textureInfo = new TextureInfo(new Image);
|
|
390
422
|
textureInfos[0] = textureInfo;
|
|
391
|
-
textureInfo.createWebGLTexture();
|
|
392
423
|
resolve();
|
|
393
424
|
}));
|
|
394
425
|
}
|
|
@@ -399,7 +430,7 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
399
430
|
promises.push(new Promise(resolve =>
|
|
400
431
|
{
|
|
401
432
|
let t = 0;
|
|
402
|
-
|
|
433
|
+
console.log(`${engineName} Engine v${engineVersion}`);
|
|
403
434
|
updateSplash();
|
|
404
435
|
function updateSplash()
|
|
405
436
|
{
|
|
@@ -483,10 +514,16 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
483
514
|
return collectedObjects;
|
|
484
515
|
}
|
|
485
516
|
|
|
517
|
+
/**
|
|
518
|
+
* @callback ObjectCallbackFunction - Function that processes an object
|
|
519
|
+
* @param {EngineObject} uiObjects
|
|
520
|
+
* @memberof Engine
|
|
521
|
+
*/
|
|
522
|
+
|
|
486
523
|
/** Triggers a callback for each object within a given area
|
|
487
|
-
* @param {Vector2} [pos]
|
|
488
|
-
* @param {Vector2|number} [size]
|
|
489
|
-
* @param {
|
|
524
|
+
* @param {Vector2} [pos] - Center of test area, or undefined for all objects
|
|
525
|
+
* @param {Vector2|number} [size] - Radius of circle if float, rectangle size if Vector2
|
|
526
|
+
* @param {ObjectCallbackFunction} [callbackFunction] - Calls this function on every object that passes the test
|
|
490
527
|
* @param {Array<EngineObject>} [objects=engineObjects] - List of objects to check
|
|
491
528
|
* @memberof Engine */
|
|
492
529
|
function engineObjectsCallback(pos, size, callbackFunction, objects=engineObjects)
|
|
@@ -950,6 +987,8 @@ function formatTime(t)
|
|
|
950
987
|
async function fetchJSON(url)
|
|
951
988
|
{
|
|
952
989
|
const response = await fetch(url);
|
|
990
|
+
if (!response.ok)
|
|
991
|
+
throw new Error(`Failed to fetch JSON from ${url}: ${response.status} ${response.statusText}`);
|
|
953
992
|
return response.json();
|
|
954
993
|
}
|
|
955
994
|
|
|
@@ -960,6 +999,13 @@ async function fetchJSON(url)
|
|
|
960
999
|
* @memberof Utilities */
|
|
961
1000
|
function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
962
1001
|
|
|
1002
|
+
/**
|
|
1003
|
+
* Check if object is a valid string or can be converted to one
|
|
1004
|
+
* @param {any} s
|
|
1005
|
+
* @return {boolean}
|
|
1006
|
+
* @memberof Utilities */
|
|
1007
|
+
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
1008
|
+
|
|
963
1009
|
///////////////////////////////////////////////////////////////////////////////
|
|
964
1010
|
|
|
965
1011
|
/** Random global functions
|
|
@@ -1022,6 +1068,7 @@ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
|
1022
1068
|
/**
|
|
1023
1069
|
* Seeded random number generator
|
|
1024
1070
|
* - Can be used to create a deterministic random number sequence
|
|
1071
|
+
* @memberof Engine
|
|
1025
1072
|
* @example
|
|
1026
1073
|
* let r = new RandomGenerator(123); // random number generator with seed 123
|
|
1027
1074
|
* let a = r.float(); // random value between 0 and 1
|
|
@@ -1118,6 +1165,7 @@ function ASSERT_VECTOR2_NORMAL(v)
|
|
|
1118
1165
|
/**
|
|
1119
1166
|
* 2D Vector object with vector math library
|
|
1120
1167
|
* - Functions do not change this so they can be chained together
|
|
1168
|
+
* @memberof Engine
|
|
1121
1169
|
* @example
|
|
1122
1170
|
* let a = new Vector2(2, 3); // vector with coordinates (2, 3)
|
|
1123
1171
|
* let b = new Vector2; // vector with coordinates (0, 0)
|
|
@@ -1258,9 +1306,10 @@ class Vector2
|
|
|
1258
1306
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
1259
1307
|
}
|
|
1260
1308
|
|
|
1261
|
-
/**
|
|
1309
|
+
/** Sets this this vector to point in the specified integer direction (0-3), corresponding to multiples of 90 degree rotation
|
|
1262
1310
|
* @param {number} [direction]
|
|
1263
|
-
* @param {number} [length]
|
|
1311
|
+
* @param {number} [length]
|
|
1312
|
+
* @return {Vector2} */
|
|
1264
1313
|
setDirection(direction, length=1)
|
|
1265
1314
|
{
|
|
1266
1315
|
ASSERT_NUMBER_VALID(direction);
|
|
@@ -1268,8 +1317,10 @@ class Vector2
|
|
|
1268
1317
|
direction = mod(direction, 4);
|
|
1269
1318
|
ASSERT(direction===0 || direction===1 || direction===2 || direction===3,
|
|
1270
1319
|
'Vector2.setDirection() direction must be an integer between 0 and 3.');
|
|
1271
|
-
|
|
1272
|
-
|
|
1320
|
+
|
|
1321
|
+
this.x = direction%2 ? direction-1 ? -length : length : 0;
|
|
1322
|
+
this.y = direction%2 ? 0 : direction ? -length : length;
|
|
1323
|
+
return this;
|
|
1273
1324
|
}
|
|
1274
1325
|
|
|
1275
1326
|
/** Returns the integer direction of this vector, corresponding to multiples of 90 degree rotation (0-3)
|
|
@@ -1365,6 +1416,7 @@ function ASSERT_COLOR_VALID(c) { ASSERT(isColor(c), 'Color is invalid.', c); }
|
|
|
1365
1416
|
|
|
1366
1417
|
/**
|
|
1367
1418
|
* Color object (red, green, blue, alpha) with some helpful functions
|
|
1419
|
+
* @memberof Engine
|
|
1368
1420
|
* @example
|
|
1369
1421
|
* let a = new Color; // white
|
|
1370
1422
|
* let b = new Color(1, 0, 0); // red
|
|
@@ -1542,7 +1594,8 @@ class Color
|
|
|
1542
1594
|
* @return {Color} */
|
|
1543
1595
|
setHex(hex)
|
|
1544
1596
|
{
|
|
1545
|
-
ASSERT(
|
|
1597
|
+
ASSERT(isString(hex), 'Color hex code must be a string');
|
|
1598
|
+
ASSERT(hex[0] === '#', 'Color hex code must start with #');
|
|
1546
1599
|
ASSERT([4,5,7,9].includes(hex.length), 'Invalid hex');
|
|
1547
1600
|
|
|
1548
1601
|
if (hex.length < 6)
|
|
@@ -1584,77 +1637,78 @@ class Color
|
|
|
1584
1637
|
}
|
|
1585
1638
|
|
|
1586
1639
|
///////////////////////////////////////////////////////////////////////////////
|
|
1587
|
-
//
|
|
1640
|
+
// Default Colors
|
|
1588
1641
|
|
|
1589
1642
|
/** Color - White #ffffff
|
|
1590
1643
|
* @type {Color}
|
|
1591
1644
|
* @memberof Utilities */
|
|
1592
|
-
const WHITE = rgb();
|
|
1645
|
+
const WHITE = protectEngineConstant(rgb());
|
|
1593
1646
|
|
|
1594
|
-
/** Color - Clear White #
|
|
1647
|
+
/** Color - Clear White #757474ff with 0 alpha
|
|
1595
1648
|
* @type {Color}
|
|
1596
1649
|
* @memberof Utilities */
|
|
1597
|
-
const CLEAR_WHITE = rgb(1,1,1,0);
|
|
1650
|
+
const CLEAR_WHITE = protectEngineConstant(rgb(1,1,1,0));
|
|
1598
1651
|
|
|
1599
1652
|
/** Color - Black #000000
|
|
1600
1653
|
* @type {Color}
|
|
1601
1654
|
* @memberof Utilities */
|
|
1602
|
-
const BLACK = rgb(0,0,0);
|
|
1655
|
+
const BLACK = protectEngineConstant(rgb(0,0,0));
|
|
1603
1656
|
|
|
1604
1657
|
/** Color - Clear Black #000000 with 0 alpha
|
|
1605
1658
|
* @type {Color}
|
|
1606
1659
|
* @memberof Utilities */
|
|
1607
|
-
const CLEAR_BLACK = rgb(0,0,0,0);
|
|
1660
|
+
const CLEAR_BLACK = protectEngineConstant(rgb(0,0,0,0));
|
|
1608
1661
|
|
|
1609
1662
|
/** Color - Gray #808080
|
|
1610
1663
|
* @type {Color}
|
|
1611
1664
|
* @memberof Utilities */
|
|
1612
|
-
const GRAY = rgb(.5,.5,.5);
|
|
1665
|
+
const GRAY = protectEngineConstant(rgb(.5,.5,.5));
|
|
1613
1666
|
|
|
1614
1667
|
/** Color - Red #ff0000
|
|
1615
1668
|
* @type {Color}
|
|
1616
1669
|
* @memberof Utilities */
|
|
1617
|
-
const RED = rgb(1,0,0);
|
|
1670
|
+
const RED = protectEngineConstant(rgb(1,0,0));
|
|
1618
1671
|
|
|
1619
1672
|
/** Color - Orange #ff8000
|
|
1620
1673
|
* @type {Color}
|
|
1621
1674
|
* @memberof Utilities */
|
|
1622
|
-
const ORANGE = rgb(1,.5,0);
|
|
1675
|
+
const ORANGE = protectEngineConstant(rgb(1,.5,0));
|
|
1623
1676
|
|
|
1624
1677
|
/** Color - Yellow #ffff00
|
|
1625
1678
|
* @type {Color}
|
|
1626
1679
|
* @memberof Utilities */
|
|
1627
|
-
const YELLOW = rgb(1,1,0);
|
|
1680
|
+
const YELLOW = protectEngineConstant(rgb(1,1,0));
|
|
1628
1681
|
|
|
1629
1682
|
/** Color - Green #00ff00
|
|
1630
1683
|
* @type {Color}
|
|
1631
1684
|
* @memberof Utilities */
|
|
1632
|
-
const GREEN = rgb(0,1,0);
|
|
1685
|
+
const GREEN = protectEngineConstant(rgb(0,1,0));
|
|
1633
1686
|
|
|
1634
1687
|
/** Color - Cyan #00ffff
|
|
1635
1688
|
* @type {Color}
|
|
1636
1689
|
* @memberof Utilities */
|
|
1637
|
-
const CYAN = rgb(0,1,1);
|
|
1690
|
+
const CYAN = protectEngineConstant(rgb(0,1,1));
|
|
1638
1691
|
|
|
1639
1692
|
/** Color - Blue #0000ff
|
|
1640
1693
|
* @type {Color}
|
|
1641
1694
|
* @memberof Utilities */
|
|
1642
|
-
const BLUE = rgb(0,0,1);
|
|
1695
|
+
const BLUE = protectEngineConstant(rgb(0,0,1));
|
|
1643
1696
|
|
|
1644
1697
|
/** Color - Purple #8000ff
|
|
1645
1698
|
* @type {Color}
|
|
1646
1699
|
* @memberof Utilities */
|
|
1647
|
-
const PURPLE = rgb(.5,0,1);
|
|
1700
|
+
const PURPLE = protectEngineConstant(rgb(.5,0,1));
|
|
1648
1701
|
|
|
1649
1702
|
/** Color - Magenta #ff00ff
|
|
1650
1703
|
* @type {Color}
|
|
1651
1704
|
* @memberof Utilities */
|
|
1652
|
-
const MAGENTA = rgb(1,0,1);
|
|
1705
|
+
const MAGENTA = protectEngineConstant(rgb(1,0,1));
|
|
1653
1706
|
|
|
1654
1707
|
///////////////////////////////////////////////////////////////////////////////
|
|
1655
1708
|
|
|
1656
1709
|
/**
|
|
1657
1710
|
* Timer object tracks how long has passed since it was set
|
|
1711
|
+
* @memberof Engine
|
|
1658
1712
|
* @example
|
|
1659
1713
|
* let a = new Timer; // creates a timer that is not set
|
|
1660
1714
|
* a.set(3); // sets the timer to 3 seconds
|
|
@@ -1716,6 +1770,36 @@ class Timer
|
|
|
1716
1770
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1717
1771
|
* @return {number} */
|
|
1718
1772
|
valueOf() { return this.get(); }
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1776
|
+
// Helper functions used by the engine
|
|
1777
|
+
|
|
1778
|
+
// make color constants immutable with debug assertions
|
|
1779
|
+
function protectEngineConstant(obj)
|
|
1780
|
+
{
|
|
1781
|
+
if (debug)
|
|
1782
|
+
{
|
|
1783
|
+
// get properties and store original values
|
|
1784
|
+
const props = Object.keys(obj), values = {};
|
|
1785
|
+
props.forEach(prop => values[prop] = obj[prop]);
|
|
1786
|
+
|
|
1787
|
+
// replace with getters/setters that assert
|
|
1788
|
+
props.forEach(prop =>
|
|
1789
|
+
{
|
|
1790
|
+
Object.defineProperty(obj, prop, {
|
|
1791
|
+
get: () => values[prop],
|
|
1792
|
+
set: (value) =>
|
|
1793
|
+
{
|
|
1794
|
+
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
1795
|
+
},
|
|
1796
|
+
enumerable: true
|
|
1797
|
+
});
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// freeze the object to prevent adding new properties
|
|
1802
|
+
return Object.freeze(obj);
|
|
1719
1803
|
}
|
|
1720
1804
|
/**
|
|
1721
1805
|
* LittleJS Engine Settings
|
|
@@ -2023,7 +2107,7 @@ let medalsPreventUnlock = false;
|
|
|
2023
2107
|
/** Set position of camera in world space
|
|
2024
2108
|
* @param {Vector2} pos
|
|
2025
2109
|
* @memberof Settings */
|
|
2026
|
-
function setCameraPos(pos) { cameraPos = pos; }
|
|
2110
|
+
function setCameraPos(pos) { cameraPos = pos.copy(); }
|
|
2027
2111
|
|
|
2028
2112
|
/** Set angle of camera in world space
|
|
2029
2113
|
* @param {number} angle
|
|
@@ -2046,17 +2130,17 @@ function setCanvasColorTiles(colorTiles) { canvasColorTiles = colorTiles; }
|
|
|
2046
2130
|
/** Set color to clear the canvas to before render
|
|
2047
2131
|
* @param {Color} color
|
|
2048
2132
|
* @memberof Settings */
|
|
2049
|
-
function setCanvasClearColor(color) { canvasClearColor = color; }
|
|
2133
|
+
function setCanvasClearColor(color) { canvasClearColor = color.copy(); }
|
|
2050
2134
|
|
|
2051
2135
|
/** Set max size of the canvas
|
|
2052
2136
|
* @param {Vector2} size
|
|
2053
2137
|
* @memberof Settings */
|
|
2054
|
-
function setCanvasMaxSize(size) { canvasMaxSize = size; }
|
|
2138
|
+
function setCanvasMaxSize(size) { canvasMaxSize = size.copy(); }
|
|
2055
2139
|
|
|
2056
2140
|
/** Set fixed size of the canvas
|
|
2057
2141
|
* @param {Vector2} size
|
|
2058
2142
|
* @memberof Settings */
|
|
2059
|
-
function setCanvasFixedSize(size) { canvasFixedSize = size; }
|
|
2143
|
+
function setCanvasFixedSize(size) { canvasFixedSize = size.copy(); }
|
|
2060
2144
|
|
|
2061
2145
|
/** Use nearest scaling algorithm for canvas for more pixelated look
|
|
2062
2146
|
* - If enabled sets css image-rendering:pixelated
|
|
@@ -2121,7 +2205,7 @@ function setGLCircleSides(sides) { glCircleSides = sides; }
|
|
|
2121
2205
|
/** Set default size of tiles in pixels
|
|
2122
2206
|
* @param {Vector2} size
|
|
2123
2207
|
* @memberof Settings */
|
|
2124
|
-
function setTileSizeDefault(size) { tileSizeDefault = size; }
|
|
2208
|
+
function setTileSizeDefault(size) { tileSizeDefault = size.copy(); }
|
|
2125
2209
|
|
|
2126
2210
|
/** Set to prevent tile bleeding from neighbors in pixels
|
|
2127
2211
|
* @param {number} scale
|
|
@@ -2166,7 +2250,7 @@ function setObjectMaxSpeed(speed) { objectMaxSpeed = speed; }
|
|
|
2166
2250
|
/** Set how much gravity to apply to objects
|
|
2167
2251
|
* @param {Vector2} newGravity
|
|
2168
2252
|
* @memberof Settings */
|
|
2169
|
-
function setGravity(newGravity) { gravity = newGravity; }
|
|
2253
|
+
function setGravity(newGravity) { gravity = newGravity.copy(); }
|
|
2170
2254
|
|
|
2171
2255
|
/** Set to scales emit rate of particles
|
|
2172
2256
|
* @param {number} scale
|
|
@@ -2256,7 +2340,7 @@ function setMedalDisplaySlideTime(time) { medalDisplaySlideTime = time; }
|
|
|
2256
2340
|
/** Set size of medal display
|
|
2257
2341
|
* @param {Vector2} size
|
|
2258
2342
|
* @memberof Settings */
|
|
2259
|
-
function setMedalDisplaySize(size) { medalDisplaySize = size; }
|
|
2343
|
+
function setMedalDisplaySize(size) { medalDisplaySize = size.copy(); }
|
|
2260
2344
|
|
|
2261
2345
|
/** Set to stop medals from being unlockable
|
|
2262
2346
|
* @param {boolean} preventUnlock
|
|
@@ -2296,6 +2380,7 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2296
2380
|
* - Collision for objects can be set to be solid to block other objects
|
|
2297
2381
|
* - Objects may get pushed into overlapping other solid objects, if so they will push away
|
|
2298
2382
|
* - Solid objects are more performance intensive and should be used sparingly
|
|
2383
|
+
* @memberof Engine
|
|
2299
2384
|
* @example
|
|
2300
2385
|
* // create an engine object, normally you would first extend the class with your own
|
|
2301
2386
|
* const pos = vec2(2,3);
|
|
@@ -2304,18 +2389,18 @@ function setDebugKey(key) { debugKey = key; }
|
|
|
2304
2389
|
class EngineObject
|
|
2305
2390
|
{
|
|
2306
2391
|
/** Create an engine object and adds it to the list of objects
|
|
2307
|
-
* @param {Vector2} [pos=(0,0)]
|
|
2308
|
-
* @param {Vector2} [size=(1,1)]
|
|
2309
|
-
* @param {TileInfo} [tileInfo]
|
|
2310
|
-
* @param {number} [angle]
|
|
2311
|
-
* @param {Color} [color=
|
|
2312
|
-
* @param {number} [renderOrder]
|
|
2392
|
+
* @param {Vector2} [pos=(0,0)] - World space position of the object
|
|
2393
|
+
* @param {Vector2} [size=(1,1)] - World space size of the object
|
|
2394
|
+
* @param {TileInfo} [tileInfo] - Tile info to render object (undefined is untextured)
|
|
2395
|
+
* @param {number} [angle] - Angle the object is rotated by
|
|
2396
|
+
* @param {Color} [color=WHITE] - Color to apply to tile when rendered
|
|
2397
|
+
* @param {number} [renderOrder] - Objects sorted by renderOrder before being rendered
|
|
2313
2398
|
*/
|
|
2314
|
-
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=
|
|
2399
|
+
constructor(pos=vec2(), size=vec2(1), tileInfo, angle=0, color=WHITE, renderOrder=0)
|
|
2315
2400
|
{
|
|
2316
2401
|
// check passed in params
|
|
2317
|
-
ASSERT(isVector2(pos), 'object pos
|
|
2318
|
-
ASSERT(isVector2(size), 'object size
|
|
2402
|
+
ASSERT(isVector2(pos), 'object pos must be a vec2');
|
|
2403
|
+
ASSERT(isVector2(size), 'object size must be a vec2');
|
|
2319
2404
|
ASSERT(!tileInfo || tileInfo instanceof TileInfo, 'object tileInfo should be a TileInfo or undefined');
|
|
2320
2405
|
ASSERT(typeof angle === 'number' && isFinite(angle), 'object angle should be a number');
|
|
2321
2406
|
ASSERT(isColor(color), 'object color should be a valid rgba color');
|
|
@@ -2600,7 +2685,7 @@ class EngineObject
|
|
|
2600
2685
|
drawTile(this.pos, this.drawSize || this.size, this.tileInfo, this.color, this.angle, this.mirror, this.additiveColor);
|
|
2601
2686
|
}
|
|
2602
2687
|
|
|
2603
|
-
/** Destroy this object, destroy its children, detach
|
|
2688
|
+
/** Destroy this object, destroy its children, detach its parent, and mark it for removal */
|
|
2604
2689
|
destroy()
|
|
2605
2690
|
{
|
|
2606
2691
|
if (this.destroyed)
|
|
@@ -2674,6 +2759,8 @@ class EngineObject
|
|
|
2674
2759
|
addChild(child, localPos=vec2(), localAngle=0)
|
|
2675
2760
|
{
|
|
2676
2761
|
ASSERT(!child.parent && !this.children.includes(child));
|
|
2762
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
2763
|
+
ASSERT(child !== this, 'cannot add self as child');
|
|
2677
2764
|
this.children.push(child);
|
|
2678
2765
|
child.parent = this;
|
|
2679
2766
|
child.localPos = localPos.copy();
|
|
@@ -2685,6 +2772,7 @@ class EngineObject
|
|
|
2685
2772
|
removeChild(child)
|
|
2686
2773
|
{
|
|
2687
2774
|
ASSERT(child.parent === this && this.children.includes(child));
|
|
2775
|
+
ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
|
|
2688
2776
|
this.children.splice(this.children.indexOf(child), 1);
|
|
2689
2777
|
child.parent = 0;
|
|
2690
2778
|
}
|
|
@@ -2823,7 +2911,7 @@ let drawCount;
|
|
|
2823
2911
|
* Create a tile info object using a grid based system
|
|
2824
2912
|
* - This can take vecs or floats for easier use and conversion
|
|
2825
2913
|
* - If an index is passed in, the tile size and index will determine the position
|
|
2826
|
-
* @param {Vector2|number} [pos=0] -
|
|
2914
|
+
* @param {Vector2|number} [pos=0] - Position of the tile in pixels, or tile index
|
|
2827
2915
|
* @param {Vector2|number} [size=tileSizeDefault] - Size of tile in pixels
|
|
2828
2916
|
* @param {number} [textureIndex] - Texture index to use
|
|
2829
2917
|
* @param {number} [padding] - How many pixels padding around tiles
|
|
@@ -2868,6 +2956,7 @@ function tile(pos=new Vector2, size=tileSizeDefault, textureIndex=0, padding=0)
|
|
|
2868
2956
|
|
|
2869
2957
|
/**
|
|
2870
2958
|
* Tile Info - Stores info about how to draw a tile
|
|
2959
|
+
* @memberof Draw
|
|
2871
2960
|
*/
|
|
2872
2961
|
class TileInfo
|
|
2873
2962
|
{
|
|
@@ -2912,48 +3001,54 @@ class TileInfo
|
|
|
2912
3001
|
}
|
|
2913
3002
|
|
|
2914
3003
|
/**
|
|
2915
|
-
* Set this tile to use a full image
|
|
2916
|
-
* @param {
|
|
2917
|
-
* @param {WebGLTexture} [glTexture] - WebGL texture
|
|
3004
|
+
* Set this tile to use a full image in a texture info
|
|
3005
|
+
* @param {TextureInfo} textureInfo
|
|
2918
3006
|
* @return {TileInfo}
|
|
2919
3007
|
*/
|
|
2920
|
-
setFullImage(
|
|
3008
|
+
setFullImage(textureInfo)
|
|
2921
3009
|
{
|
|
2922
3010
|
this.pos = new Vector2;
|
|
2923
|
-
this.size =
|
|
2924
|
-
this.textureInfo =
|
|
3011
|
+
this.size = textureInfo.size.copy();
|
|
3012
|
+
this.textureInfo = textureInfo;
|
|
2925
3013
|
// do not use padding or bleed
|
|
2926
3014
|
this.bleedScale = this.padding = 0;
|
|
2927
3015
|
return this;
|
|
2928
3016
|
}
|
|
2929
3017
|
}
|
|
2930
3018
|
|
|
2931
|
-
/**
|
|
3019
|
+
/**
|
|
3020
|
+
* Tile Info - Stores info about each texture
|
|
3021
|
+
* @memberof Draw
|
|
3022
|
+
*/
|
|
2932
3023
|
class TextureInfo
|
|
2933
3024
|
{
|
|
2934
3025
|
/**
|
|
2935
3026
|
* Create a TextureInfo, called automatically by the engine
|
|
2936
3027
|
* @param {HTMLImageElement|OffscreenCanvas} image
|
|
2937
|
-
* @param {
|
|
3028
|
+
* @param {boolean} [useWebGL] - Should use WebGL if available?
|
|
2938
3029
|
*/
|
|
2939
|
-
constructor(image,
|
|
3030
|
+
constructor(image, useWebGL=true)
|
|
2940
3031
|
{
|
|
2941
|
-
/** @property {HTMLImageElement} - image source */
|
|
3032
|
+
/** @property {HTMLImageElement|OffscreenCanvas} - image source */
|
|
2942
3033
|
this.image = image;
|
|
2943
3034
|
/** @property {Vector2} - size of the image */
|
|
2944
|
-
this.size = vec2(image.width, image.height);
|
|
3035
|
+
this.size = image ? vec2(image.width, image.height) : vec2();
|
|
2945
3036
|
/** @property {Vector2} - inverse of the size, cached for rendering */
|
|
2946
|
-
this.sizeInverse = vec2(1/image.width, 1/image.height);
|
|
3037
|
+
this.sizeInverse = image ? vec2(1/image.width, 1/image.height) : vec2();
|
|
2947
3038
|
/** @property {WebGLTexture} - WebGL texture */
|
|
2948
|
-
this.glTexture =
|
|
3039
|
+
this.glTexture = undefined;
|
|
3040
|
+
useWebGL && this.createWebGLTexture();
|
|
2949
3041
|
}
|
|
2950
3042
|
|
|
2951
|
-
|
|
2952
|
-
{
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
3043
|
+
/** Creates the WebGL texture, updates if already created */
|
|
3044
|
+
createWebGLTexture() { glRegisterTextureInfo(this); }
|
|
3045
|
+
|
|
3046
|
+
/** Destroys the WebGL texture */
|
|
3047
|
+
destroyWebGLTexture() { glUnregisterTextureInfo(this); }
|
|
3048
|
+
|
|
3049
|
+
/** Check if the texture is webgl enabled
|
|
3050
|
+
* @return {boolean} */
|
|
3051
|
+
hasWebGL() { return !!this.glTexture; }
|
|
2957
3052
|
}
|
|
2958
3053
|
|
|
2959
3054
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2974,10 +3069,11 @@ class TextureInfo
|
|
|
2974
3069
|
function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
|
|
2975
3070
|
angle=0, mirror, additiveColor, useWebGL=glEnable, screenSpace, context)
|
|
2976
3071
|
{
|
|
2977
|
-
ASSERT(isVector2(pos), '
|
|
2978
|
-
ASSERT(isVector2(size), '
|
|
2979
|
-
ASSERT(isColor(color)
|
|
2980
|
-
ASSERT(isNumber(angle), '
|
|
3072
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3073
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3074
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3075
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3076
|
+
ASSERT(!additiveColor || isColor(additiveColor), 'additiveColor must be a color');
|
|
2981
3077
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
2982
3078
|
|
|
2983
3079
|
const textureInfo = tileInfo && tileInfo.textureInfo;
|
|
@@ -3072,10 +3168,10 @@ function drawRect(pos, size, color, angle, useWebGL, screenSpace, context)
|
|
|
3072
3168
|
* @memberof Draw */
|
|
3073
3169
|
function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3074
3170
|
{
|
|
3075
|
-
ASSERT(isVector2(pos), '
|
|
3076
|
-
ASSERT(isVector2(size), '
|
|
3077
|
-
ASSERT(isColor(colorTop) && isColor(colorBottom), '
|
|
3078
|
-
ASSERT(isNumber(angle), '
|
|
3171
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3172
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3173
|
+
ASSERT(isColor(colorTop) && isColor(colorBottom), 'color is invalid');
|
|
3174
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3079
3175
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3080
3176
|
if (useWebGL)
|
|
3081
3177
|
{
|
|
@@ -3133,11 +3229,11 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
3133
3229
|
* @memberof Draw */
|
|
3134
3230
|
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
3135
3231
|
{
|
|
3136
|
-
ASSERT(Array.isArray(points), '
|
|
3137
|
-
ASSERT(isNumber(width), '
|
|
3138
|
-
ASSERT(isColor(color), '
|
|
3139
|
-
ASSERT(isVector2(pos), '
|
|
3140
|
-
ASSERT(isNumber(angle), '
|
|
3232
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3233
|
+
ASSERT(isNumber(width), 'width must be a number');
|
|
3234
|
+
ASSERT(isColor(color), 'color is invalid');
|
|
3235
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3236
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3141
3237
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3142
3238
|
if (useWebGL)
|
|
3143
3239
|
{
|
|
@@ -3208,8 +3304,8 @@ function drawLine(posA, posB, width=.1, color, pos=vec2(), angle=0, useWebGL, sc
|
|
|
3208
3304
|
* @memberof Draw */
|
|
3209
3305
|
function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, lineColor=BLACK, angle=0, useWebGL=glEnable, screenSpace=false, context)
|
|
3210
3306
|
{
|
|
3211
|
-
ASSERT(isVector2(size), '
|
|
3212
|
-
ASSERT(isNumber(sides), '
|
|
3307
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3308
|
+
ASSERT(isNumber(sides), 'sides must be a number');
|
|
3213
3309
|
|
|
3214
3310
|
// build regular polygon points
|
|
3215
3311
|
const points = [];
|
|
@@ -3235,12 +3331,13 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
3235
3331
|
* @memberof Draw */
|
|
3236
3332
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
3237
3333
|
{
|
|
3238
|
-
ASSERT(isVector2(pos), '
|
|
3239
|
-
ASSERT(Array.isArray(points), '
|
|
3240
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3241
|
-
ASSERT(isNumber(lineWidth), '
|
|
3242
|
-
ASSERT(isNumber(angle), '
|
|
3334
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3335
|
+
ASSERT(Array.isArray(points), 'points must be an array');
|
|
3336
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3337
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3338
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3243
3339
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3340
|
+
|
|
3244
3341
|
if (useWebGL)
|
|
3245
3342
|
{
|
|
3246
3343
|
let scale = 1;
|
|
@@ -3287,13 +3384,14 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
|
|
|
3287
3384
|
* @memberof Draw */
|
|
3288
3385
|
function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3289
3386
|
{
|
|
3290
|
-
ASSERT(isVector2(pos), '
|
|
3291
|
-
ASSERT(isVector2(size), '
|
|
3292
|
-
ASSERT(isColor(color) && isColor(lineColor), '
|
|
3293
|
-
ASSERT(isNumber(angle), '
|
|
3294
|
-
ASSERT(isNumber(lineWidth), '
|
|
3295
|
-
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, '
|
|
3387
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3388
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3389
|
+
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
3390
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3391
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3392
|
+
ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, 'invalid lineWidth');
|
|
3296
3393
|
ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
|
|
3394
|
+
|
|
3297
3395
|
if (useWebGL)
|
|
3298
3396
|
{
|
|
3299
3397
|
// draw as a regular polygon
|
|
@@ -3330,21 +3428,32 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
|
|
|
3330
3428
|
* @memberof Draw */
|
|
3331
3429
|
function drawCircle(pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, useWebGL=glEnable, screenSpace=false, context)
|
|
3332
3430
|
{
|
|
3333
|
-
ASSERT(isNumber(size), '
|
|
3431
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3334
3432
|
drawEllipse(pos, vec2(size), color, 0, lineWidth, lineColor, useWebGL, screenSpace, context);
|
|
3335
3433
|
}
|
|
3336
3434
|
|
|
3435
|
+
/**
|
|
3436
|
+
* @callback Canvas2DDrawFunction - A function that draws to a 2D canvas context
|
|
3437
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
3438
|
+
* @memberof Draw
|
|
3439
|
+
*/
|
|
3440
|
+
|
|
3337
3441
|
/** Draw directly to a 2d canvas context in world space
|
|
3338
3442
|
* @param {Vector2} pos
|
|
3339
3443
|
* @param {Vector2} size
|
|
3340
3444
|
* @param {number} angle
|
|
3341
3445
|
* @param {boolean} [mirror]
|
|
3342
|
-
* @param {
|
|
3446
|
+
* @param {Canvas2DDrawFunction} [drawFunction]
|
|
3343
3447
|
* @param {boolean} [screenSpace=false]
|
|
3344
3448
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3345
3449
|
* @memberof Draw */
|
|
3346
3450
|
function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpace=false, context=drawContext)
|
|
3347
3451
|
{
|
|
3452
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3453
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
3454
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
3455
|
+
ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
|
|
3456
|
+
|
|
3348
3457
|
if (!screenSpace)
|
|
3349
3458
|
{
|
|
3350
3459
|
// transform from world space to screen space
|
|
@@ -3372,12 +3481,13 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
3372
3481
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3373
3482
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
3374
3483
|
* @param {string} [font=fontDefault]
|
|
3484
|
+
* @param {string} [fontStyle]
|
|
3375
3485
|
* @param {number} [maxWidth]
|
|
3376
3486
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3377
3487
|
* @memberof Draw */
|
|
3378
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth, context=drawContext)
|
|
3488
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, context=drawContext)
|
|
3379
3489
|
{
|
|
3380
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, maxWidth, context);
|
|
3490
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, context);
|
|
3381
3491
|
}
|
|
3382
3492
|
|
|
3383
3493
|
/** Draw text on overlay canvas in world space
|
|
@@ -3390,11 +3500,12 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
3390
3500
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3391
3501
|
* @param {CanvasTextAlign} [textAlign='center']
|
|
3392
3502
|
* @param {string} [font=fontDefault]
|
|
3503
|
+
* @param {string} [fontStyle]
|
|
3393
3504
|
* @param {number} [maxWidth]
|
|
3394
3505
|
* @memberof Draw */
|
|
3395
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, maxWidth)
|
|
3506
|
+
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth)
|
|
3396
3507
|
{
|
|
3397
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, maxWidth, overlayContext);
|
|
3508
|
+
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, overlayContext);
|
|
3398
3509
|
}
|
|
3399
3510
|
|
|
3400
3511
|
/** Draw text on overlay canvas in screen space
|
|
@@ -3407,16 +3518,27 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
3407
3518
|
* @param {Color} [lineColor=(0,0,0,1)]
|
|
3408
3519
|
* @param {CanvasTextAlign} [textAlign]
|
|
3409
3520
|
* @param {string} [font=fontDefault]
|
|
3521
|
+
* @param {string} [fontStyle]
|
|
3410
3522
|
* @param {number} [maxWidth]
|
|
3411
3523
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
3412
3524
|
* @memberof Draw */
|
|
3413
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, maxWidth, context=overlayContext)
|
|
3414
|
-
{
|
|
3525
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, context=overlayContext)
|
|
3526
|
+
{
|
|
3527
|
+
ASSERT(isString(text), 'text must be a string');
|
|
3528
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
3529
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
3530
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
3531
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
3532
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
3533
|
+
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
3534
|
+
ASSERT(isString(font), 'font must be a string');
|
|
3535
|
+
ASSERT(isString(fontStyle), 'fontStyle must be a string');
|
|
3536
|
+
|
|
3415
3537
|
context.fillStyle = color.toString();
|
|
3416
3538
|
context.strokeStyle = lineColor.toString();
|
|
3417
3539
|
context.lineWidth = lineWidth;
|
|
3418
3540
|
context.textAlign = textAlign;
|
|
3419
|
-
context.font = size + 'px '+ font;
|
|
3541
|
+
context.font = fontStyle + ' ' + size + 'px '+ font;
|
|
3420
3542
|
context.textBaseline = 'middle';
|
|
3421
3543
|
|
|
3422
3544
|
const lines = (text+'').split('\n');
|
|
@@ -3613,6 +3735,7 @@ let engineFontImage;
|
|
|
3613
3735
|
* - 96 characters (from space to tilde) are stored in an image
|
|
3614
3736
|
* - Uses a default 8x8 font if none is supplied
|
|
3615
3737
|
* - You can also use fonts from the main tile sheet
|
|
3738
|
+
* @memberof Draw
|
|
3616
3739
|
* @example
|
|
3617
3740
|
* // use built in font
|
|
3618
3741
|
* const font = new FontImage;
|
|
@@ -3623,11 +3746,11 @@ let engineFontImage;
|
|
|
3623
3746
|
class FontImage
|
|
3624
3747
|
{
|
|
3625
3748
|
/** Create an image font
|
|
3626
|
-
* @param {HTMLImageElement} [image]
|
|
3627
|
-
* @param {Vector2} [tileSize=(8,8)]
|
|
3628
|
-
* @param {Vector2} [paddingSize=(0,1)] - How much
|
|
3749
|
+
* @param {HTMLImageElement} [image] - Image for the font, default if undefined
|
|
3750
|
+
* @param {Vector2} [tileSize=(8,8)] - Size of the font source tiles
|
|
3751
|
+
* @param {Vector2} [paddingSize=(0,1)] - How much space between characters
|
|
3629
3752
|
*/
|
|
3630
|
-
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1)
|
|
3753
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1))
|
|
3631
3754
|
{
|
|
3632
3755
|
// load default font image
|
|
3633
3756
|
if (!engineFontImage)
|
|
@@ -3669,7 +3792,7 @@ class FontImage
|
|
|
3669
3792
|
* @param {boolean} [center]
|
|
3670
3793
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
3671
3794
|
*/
|
|
3672
|
-
drawTextScreen(text, pos, scale=4, center, context=overlayContext)
|
|
3795
|
+
drawTextScreen(text, pos, scale=4, center=true, context=overlayContext)
|
|
3673
3796
|
{
|
|
3674
3797
|
context.save();
|
|
3675
3798
|
const size = this.tileSize;
|
|
@@ -4363,6 +4486,7 @@ function audioInit()
|
|
|
4363
4486
|
* Sound Object - Stores a sound for later use and can be played positionally
|
|
4364
4487
|
*
|
|
4365
4488
|
* <a href=https://killedbyapixel.github.io/ZzFX/>Create sounds using the ZzFX Sound Designer.</a>
|
|
4489
|
+
* @memberof Audio
|
|
4366
4490
|
* @example
|
|
4367
4491
|
* // create a sound
|
|
4368
4492
|
* const sound_example = new Sound([.5,.5]);
|
|
@@ -4457,12 +4581,12 @@ class Sound
|
|
|
4457
4581
|
|
|
4458
4582
|
/** Play the sound as a musical note with a semitone offset
|
|
4459
4583
|
* This can be used to play music with chromatic scales
|
|
4460
|
-
* @param {number} semitoneOffset - How many semitones to offset pitch
|
|
4584
|
+
* @param {number} [semitoneOffset=0] - How many semitones to offset pitch
|
|
4461
4585
|
* @param {Vector2} [pos] - World space position to play the sound if any
|
|
4462
4586
|
* @param {number} [volume=1] - How much to scale volume by
|
|
4463
4587
|
* @return {SoundInstance} - The audio source node
|
|
4464
4588
|
*/
|
|
4465
|
-
playNote(semitoneOffset, pos, volume)
|
|
4589
|
+
playNote(semitoneOffset=0, pos, volume)
|
|
4466
4590
|
{
|
|
4467
4591
|
const pitch = getNoteFrequency(semitoneOffset, 1);
|
|
4468
4592
|
return this.play(pos, volume, pitch, 0);
|
|
@@ -4485,6 +4609,8 @@ class Sound
|
|
|
4485
4609
|
/**
|
|
4486
4610
|
* Sound Wave Object - Stores a wave sound for later use and can be played positionally
|
|
4487
4611
|
* - this can be used to play wave, mp3, and ogg files
|
|
4612
|
+
* @extends Sound
|
|
4613
|
+
* @memberof Audio
|
|
4488
4614
|
* @example
|
|
4489
4615
|
* // create a sound
|
|
4490
4616
|
* const sound_example = new SoundWave('sound.mp3');
|
|
@@ -4494,22 +4620,29 @@ class Sound
|
|
|
4494
4620
|
*/
|
|
4495
4621
|
class SoundWave extends Sound
|
|
4496
4622
|
{
|
|
4623
|
+
/**
|
|
4624
|
+
* @callback SoundLoadCallback - Function called when sound is loaded
|
|
4625
|
+
* @param {SoundWave} sound
|
|
4626
|
+
* @memberof Audio
|
|
4627
|
+
*/
|
|
4628
|
+
|
|
4497
4629
|
/** Create a sound object and cache the wave file for later use
|
|
4498
4630
|
* @param {string} filename - Filename of audio file to load
|
|
4499
4631
|
* @param {number} [randomness] - How much to randomize frequency each time sound plays
|
|
4500
4632
|
* @param {number} [range=soundDefaultRange] - World space max range of sound
|
|
4501
4633
|
* @param {number} [taper=soundDefaultTaper] - At what percentage of range should it start tapering
|
|
4502
|
-
* @param {
|
|
4634
|
+
* @param {SoundLoadCallback} [onloadCallback] - callback function to call when sound is loaded
|
|
4503
4635
|
*/
|
|
4504
4636
|
constructor(filename, randomness=0, range, taper, onloadCallback)
|
|
4505
4637
|
{
|
|
4506
4638
|
super(undefined, range, taper);
|
|
4507
4639
|
if (!soundEnable || headlessMode) return;
|
|
4640
|
+
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
4508
4641
|
|
|
4509
|
-
/** @property {
|
|
4642
|
+
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
4510
4643
|
this.onloadCallback = onloadCallback;
|
|
4511
4644
|
this.randomness = randomness;
|
|
4512
|
-
this.loadSound(filename);
|
|
4645
|
+
filename && this.loadSound(filename);
|
|
4513
4646
|
}
|
|
4514
4647
|
|
|
4515
4648
|
/** Loads a sound from a URL and decodes it into sample data. Must be used with await!
|
|
@@ -4518,6 +4651,8 @@ class SoundWave extends Sound
|
|
|
4518
4651
|
async loadSound(filename)
|
|
4519
4652
|
{
|
|
4520
4653
|
const response = await fetch(filename);
|
|
4654
|
+
if (!response.ok)
|
|
4655
|
+
throw new Error(`Failed to load sound from ${filename}: ${response.status} ${response.statusText}`);
|
|
4521
4656
|
const arrayBuffer = await response.arrayBuffer();
|
|
4522
4657
|
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
|
|
4523
4658
|
|
|
@@ -4553,7 +4688,7 @@ class SoundWave extends Sound
|
|
|
4553
4688
|
this.sampleChannels = sampleChannels;
|
|
4554
4689
|
this.loadedPercent = 1;
|
|
4555
4690
|
if (this.onloadCallback)
|
|
4556
|
-
this.onloadCallback();
|
|
4691
|
+
this.onloadCallback(this);
|
|
4557
4692
|
}
|
|
4558
4693
|
}
|
|
4559
4694
|
|
|
@@ -4562,6 +4697,7 @@ class SoundWave extends Sound
|
|
|
4562
4697
|
/**
|
|
4563
4698
|
* Sound Instance - Wraps an AudioBufferSourceNode for individual sound control
|
|
4564
4699
|
* Represents a single playing instance of a sound with pause/resume capabilities
|
|
4700
|
+
* @memberof Audio
|
|
4565
4701
|
* @example
|
|
4566
4702
|
* // Play a sound and get an instance for control
|
|
4567
4703
|
* const jumpSound = new Sound([.5,.5,220]);
|
|
@@ -4627,8 +4763,16 @@ class SoundInstance
|
|
|
4627
4763
|
this.stop();
|
|
4628
4764
|
this.gainNode = audioContext.createGain();
|
|
4629
4765
|
this.source = playSamples(this.sound.sampleChannels, this.volume, this.rate, this.pan, this.loop, this.sound.sampleRate, this.gainNode, offset, this.onendedCallback);
|
|
4630
|
-
this.
|
|
4631
|
-
|
|
4766
|
+
if (this.source)
|
|
4767
|
+
{
|
|
4768
|
+
this.startTime = audioContext.currentTime - offset;
|
|
4769
|
+
this.pausedTime = undefined;
|
|
4770
|
+
}
|
|
4771
|
+
else
|
|
4772
|
+
{
|
|
4773
|
+
this.startTime = undefined;
|
|
4774
|
+
this.pausedTime = 0;
|
|
4775
|
+
}
|
|
4632
4776
|
}
|
|
4633
4777
|
|
|
4634
4778
|
/** Set the volume of this sound instance
|
|
@@ -4761,6 +4905,12 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
4761
4905
|
|
|
4762
4906
|
///////////////////////////////////////////////////////////////////////////////
|
|
4763
4907
|
|
|
4908
|
+
/**
|
|
4909
|
+
* @callback AudioEndedCallback - Function called when a sound ends
|
|
4910
|
+
* @param {AudioBufferSourceNode} source
|
|
4911
|
+
* @memberof Audio
|
|
4912
|
+
*/
|
|
4913
|
+
|
|
4764
4914
|
/** Play cached audio samples with given settings
|
|
4765
4915
|
* @param {Array} sampleChannels - Array of arrays of samples to play (for stereo playback)
|
|
4766
4916
|
* @param {number} [volume] - How much to scale volume by
|
|
@@ -4770,13 +4920,20 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
4770
4920
|
* @param {number} [sampleRate=44100] - Sample rate for the sound
|
|
4771
4921
|
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
4772
4922
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
4773
|
-
* @param {
|
|
4774
|
-
* @return {AudioBufferSourceNode} - The
|
|
4923
|
+
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
4924
|
+
* @return {AudioBufferSourceNode} - The source node of the sound played, may be undefined if play fails
|
|
4775
4925
|
* @memberof Audio */
|
|
4776
4926
|
function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sampleRate=audioDefaultSampleRate, gainNode, offset=0, onended)
|
|
4777
4927
|
{
|
|
4778
4928
|
if (!soundEnable || headlessMode) return;
|
|
4779
4929
|
|
|
4930
|
+
if (!audioIsRunning())
|
|
4931
|
+
{
|
|
4932
|
+
// fix stalled audio, this sound won't be able to play
|
|
4933
|
+
audioContext.resume();
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
|
|
4780
4937
|
// create buffer and source
|
|
4781
4938
|
const channelCount = sampleChannels.length;
|
|
4782
4939
|
const sampleLength = sampleChannels[0].length;
|
|
@@ -4802,13 +4959,6 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=false, sample
|
|
|
4802
4959
|
if (onended)
|
|
4803
4960
|
source.addEventListener('ended', ()=> onended(source));
|
|
4804
4961
|
|
|
4805
|
-
if (!audioIsRunning())
|
|
4806
|
-
{
|
|
4807
|
-
// fix stalled audio, this sound won't be able to play
|
|
4808
|
-
audioContext.resume();
|
|
4809
|
-
return;
|
|
4810
|
-
}
|
|
4811
|
-
|
|
4812
4962
|
// play and return sound
|
|
4813
4963
|
const startOffset = offset * rate;
|
|
4814
4964
|
source.start(0, startOffset);
|
|
@@ -4976,7 +5126,7 @@ function zzfxG
|
|
|
4976
5126
|
* - Unlimited numbers of layers, allocates canvases as needed
|
|
4977
5127
|
* - Tile layers can be drawn to using their context with canvas2d
|
|
4978
5128
|
* - Tile layers can also have collision with EngineObjects
|
|
4979
|
-
* @namespace
|
|
5129
|
+
* @namespace TileLayers
|
|
4980
5130
|
*/
|
|
4981
5131
|
|
|
4982
5132
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4984,13 +5134,13 @@ function zzfxG
|
|
|
4984
5134
|
|
|
4985
5135
|
/** Keep track of all tile layers with collision
|
|
4986
5136
|
* @type {Array<TileCollisionLayer>}
|
|
4987
|
-
* @memberof
|
|
5137
|
+
* @memberof TileLayers */
|
|
4988
5138
|
const tileCollisionLayers = [];
|
|
4989
5139
|
|
|
4990
5140
|
/** Get tile collision data for a given cell in the grid
|
|
4991
5141
|
* @param {Vector2} pos
|
|
4992
5142
|
* @return {number}
|
|
4993
|
-
* @memberof
|
|
5143
|
+
* @memberof TileLayers */
|
|
4994
5144
|
function tileCollisionGetData(pos)
|
|
4995
5145
|
{
|
|
4996
5146
|
// check all tile collision layers
|
|
@@ -5006,7 +5156,7 @@ function tileCollisionGetData(pos)
|
|
|
5006
5156
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5007
5157
|
* @param {boolean} [solidOnly] - Only check solid layers if true
|
|
5008
5158
|
* @return {TileCollisionLayer}
|
|
5009
|
-
* @memberof
|
|
5159
|
+
* @memberof TileLayers */
|
|
5010
5160
|
function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
5011
5161
|
{
|
|
5012
5162
|
for (const layer of tileCollisionLayers)
|
|
@@ -5024,7 +5174,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
5024
5174
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
5025
5175
|
* @param {boolean} [solidOnly=true] - Only check solid layers if true
|
|
5026
5176
|
* @return {Vector2}
|
|
5027
|
-
* @memberof
|
|
5177
|
+
* @memberof TileLayers */
|
|
5028
5178
|
function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
5029
5179
|
{
|
|
5030
5180
|
for (const layer of tileCollisionLayers)
|
|
@@ -5047,8 +5197,8 @@ function tileCollisionRaycast(posStart, posEnd, object, solidOnly=true)
|
|
|
5047
5197
|
* @param {number} [collisionLayer] - Layer to use for collision if any
|
|
5048
5198
|
* @param {boolean} [draw] - Should the layer be drawn automatically
|
|
5049
5199
|
* @return {Array<TileCollisionLayer>}
|
|
5050
|
-
* @memberof
|
|
5051
|
-
function
|
|
5200
|
+
* @memberof TileLayers */
|
|
5201
|
+
function tileLayersLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisionLayer, draw=true)
|
|
5052
5202
|
{
|
|
5053
5203
|
if (!tileMapData)
|
|
5054
5204
|
{
|
|
@@ -5079,9 +5229,9 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5079
5229
|
tileLayers[layerIndex] = tileLayer;
|
|
5080
5230
|
|
|
5081
5231
|
// apply layer color
|
|
5082
|
-
const layerColor = dataLayer.
|
|
5083
|
-
|
|
5084
|
-
|
|
5232
|
+
const layerColor = dataLayer.tintcolor ?
|
|
5233
|
+
new Color().setHex(dataLayer.tintcolor) :
|
|
5234
|
+
dataLayer.color || WHITE;
|
|
5085
5235
|
ASSERT(isColor(layerColor), 'layer color is not a color');
|
|
5086
5236
|
|
|
5087
5237
|
for (let x=levelSize.x; x--;)
|
|
@@ -5108,6 +5258,7 @@ function tileCollisionLoad(tileMapData, tileInfo=tile(), renderOrder=0, collisio
|
|
|
5108
5258
|
///////////////////////////////////////////////////////////////////////////////
|
|
5109
5259
|
/**
|
|
5110
5260
|
* Tile layer data object stores info about how to draw a tile
|
|
5261
|
+
* @memberof TileLayers
|
|
5111
5262
|
* @example
|
|
5112
5263
|
* // create tile layer data with tile index 0 and random orientation and color
|
|
5113
5264
|
* const tileIndex = 0;
|
|
@@ -5145,6 +5296,7 @@ class TileLayerData
|
|
|
5145
5296
|
* - Contains an offscreen canvas that can be rendered to
|
|
5146
5297
|
* - WebGL rendering is optional, call useWebGL to enable
|
|
5147
5298
|
* @extends EngineObject
|
|
5299
|
+
* @memberof TileLayers
|
|
5148
5300
|
* @example
|
|
5149
5301
|
* const canvasLayer = new CanvasLayer(vec2(), vec2(200,100));
|
|
5150
5302
|
*/
|
|
@@ -5159,15 +5311,19 @@ class CanvasLayer extends EngineObject
|
|
|
5159
5311
|
*/
|
|
5160
5312
|
constructor(position, size, angle=0, renderOrder=0, canvasSize=vec2(512))
|
|
5161
5313
|
{
|
|
5314
|
+
ASSERT(isVector2(canvasSize), 'canvasSize must be a Vector2');
|
|
5162
5315
|
super(position, size, undefined, angle, WHITE, renderOrder);
|
|
5163
5316
|
|
|
5164
5317
|
/** @property {HTMLCanvasElement} - The canvas used by this layer */
|
|
5165
5318
|
this.canvas = headlessMode ? undefined : new OffscreenCanvas(canvasSize.x, canvasSize.y);
|
|
5166
5319
|
/** @property {OffscreenCanvasRenderingContext2D} - The 2D canvas context used by this layer */
|
|
5167
|
-
this.context =
|
|
5168
|
-
/** @property {
|
|
5169
|
-
|
|
5170
|
-
this.
|
|
5320
|
+
this.context = this.canvas?.getContext('2d');
|
|
5321
|
+
/** @property {TextureInfo} - Texture info to use for this object rendering */
|
|
5322
|
+
const useWebGL = false; // do not use webgl by default
|
|
5323
|
+
this.textureInfo = new TextureInfo(this.canvas, useWebGL);
|
|
5324
|
+
|
|
5325
|
+
// disable physics by default
|
|
5326
|
+
this.mass = this.gravityScale = this.friction = this.restitution = 0;
|
|
5171
5327
|
}
|
|
5172
5328
|
|
|
5173
5329
|
/** Destroy this canvas layer */
|
|
@@ -5176,9 +5332,7 @@ class CanvasLayer extends EngineObject
|
|
|
5176
5332
|
if (this.destroyed)
|
|
5177
5333
|
return;
|
|
5178
5334
|
|
|
5179
|
-
|
|
5180
|
-
if (this.glTexture)
|
|
5181
|
-
glDeleteTexture(this.glTexture);
|
|
5335
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5182
5336
|
super.destroy();
|
|
5183
5337
|
}
|
|
5184
5338
|
|
|
@@ -5201,19 +5355,27 @@ class CanvasLayer extends EngineObject
|
|
|
5201
5355
|
draw(pos, size, angle=0, color=WHITE, mirror=false, additiveColor, screenSpace=false, context)
|
|
5202
5356
|
{
|
|
5203
5357
|
// draw the canvas layer as a single tile that uses the whole texture
|
|
5204
|
-
const useWebGL = glEnable && this.
|
|
5205
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
5358
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5359
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5206
5360
|
drawTile(pos, size, tileInfo, color, angle, mirror, additiveColor, useWebGL, screenSpace, context);
|
|
5207
5361
|
}
|
|
5208
5362
|
|
|
5363
|
+
/**
|
|
5364
|
+
* @callback Canvas2DDrawCallback - Function that draws to a canvas 2D context
|
|
5365
|
+
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} context
|
|
5366
|
+
* @memberof TileLayers
|
|
5367
|
+
*/
|
|
5368
|
+
|
|
5209
5369
|
/** Draw onto the layer canvas in world space (bypass WebGL)
|
|
5210
5370
|
* @param {Vector2} pos
|
|
5211
5371
|
* @param {Vector2} size
|
|
5212
5372
|
* @param {number} angle
|
|
5213
5373
|
* @param {boolean} mirror
|
|
5214
|
-
* @param {
|
|
5374
|
+
* @param {Canvas2DDrawCallback} drawFunction */
|
|
5215
5375
|
drawCanvas2D(pos, size, angle, mirror, drawFunction)
|
|
5216
5376
|
{
|
|
5377
|
+
if (!this.context) return;
|
|
5378
|
+
|
|
5217
5379
|
const context = this.context;
|
|
5218
5380
|
context.save();
|
|
5219
5381
|
pos = pos.subtract(this.pos).multiply(this.tileInfo.size);
|
|
@@ -5248,7 +5410,7 @@ class CanvasLayer extends EngineObject
|
|
|
5248
5410
|
else
|
|
5249
5411
|
{
|
|
5250
5412
|
// untextured
|
|
5251
|
-
context.fillStyle = color;
|
|
5413
|
+
context.fillStyle = color.toString();
|
|
5252
5414
|
context.fillRect(-.5, -.5, 1, 1);
|
|
5253
5415
|
}
|
|
5254
5416
|
});
|
|
@@ -5266,15 +5428,10 @@ class CanvasLayer extends EngineObject
|
|
|
5266
5428
|
* @param {boolean} [enable] - enable WebGL rendering and update the texture */
|
|
5267
5429
|
useWebGL(enable=true)
|
|
5268
5430
|
{
|
|
5269
|
-
if (
|
|
5270
|
-
|
|
5271
|
-
if (this.glTexture)
|
|
5272
|
-
glSetTextureData(this.glTexture, this.canvas);
|
|
5273
|
-
else
|
|
5274
|
-
this.glTexture = glCreateTexture(this.canvas);
|
|
5275
|
-
}
|
|
5431
|
+
if (enable)
|
|
5432
|
+
this.textureInfo.createWebGLTexture();
|
|
5276
5433
|
else
|
|
5277
|
-
this.
|
|
5434
|
+
this.textureInfo.destroyWebGLTexture();
|
|
5278
5435
|
}
|
|
5279
5436
|
}
|
|
5280
5437
|
|
|
@@ -5285,7 +5442,9 @@ class CanvasLayer extends EngineObject
|
|
|
5285
5442
|
* - To allow dynamic modifications, layers are rendered using canvas 2d
|
|
5286
5443
|
* - Some devices like mobile phones are limited to 4k texture resolution
|
|
5287
5444
|
* - For with 16x16 tiles this limits layers to 256x256 on mobile devices
|
|
5445
|
+
* - Tile layers are centered on their corner, so normal levels are at (0,0)
|
|
5288
5446
|
* @extends CanvasLayer
|
|
5447
|
+
* @memberof TileLayers
|
|
5289
5448
|
* @example
|
|
5290
5449
|
* const tileLayer = new TileLayer(vec2(), vec2(200,100));
|
|
5291
5450
|
*/
|
|
@@ -5295,26 +5454,15 @@ class TileLayer extends CanvasLayer
|
|
|
5295
5454
|
* @param {Vector2} position - World space position
|
|
5296
5455
|
* @param {Vector2} size - World space size
|
|
5297
5456
|
* @param {TileInfo} [tileInfo] - Default tile info for layer (used for size and texture)
|
|
5298
|
-
* @param {Vector2} [scale=(1,1)] - How much to scale this layer when rendered
|
|
5299
5457
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5300
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5301
5458
|
*/
|
|
5302
|
-
constructor(position, size, tileInfo=tile(),
|
|
5459
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
5303
5460
|
{
|
|
5304
|
-
super(position, size, 0, renderOrder, size);
|
|
5305
|
-
this.tileInfo = tileInfo;
|
|
5306
|
-
|
|
5307
5461
|
const canvasSize = size.multiply(tileInfo.size);
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
this.
|
|
5312
|
-
/** @property {WebGLTexture} - Texture if using WebGL for this layer */
|
|
5313
|
-
this.glTexture = useWebGL ? glCreateTexture(this.canvas) : undefined;
|
|
5314
|
-
// set no friction by default, applied friction is max of both objects
|
|
5315
|
-
this.friction = 0;
|
|
5316
|
-
// set no restitution by default, applied restitution is max of both objects
|
|
5317
|
-
this.restitution = 0;
|
|
5462
|
+
super(position, size, 0, renderOrder, canvasSize);
|
|
5463
|
+
|
|
5464
|
+
// set tile info
|
|
5465
|
+
this.tileInfo = tileInfo;
|
|
5318
5466
|
|
|
5319
5467
|
// init tile data
|
|
5320
5468
|
this.data = [];
|
|
@@ -5340,6 +5488,8 @@ class TileLayer extends CanvasLayer
|
|
|
5340
5488
|
* @param {boolean} [redraw] - Force the tile to redraw if true */
|
|
5341
5489
|
setData(layerPos, data, redraw=false)
|
|
5342
5490
|
{
|
|
5491
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
5492
|
+
ASSERT(data instanceof TileLayerData, 'data must be a TileLayerData');
|
|
5343
5493
|
if (layerPos.arrayCheck(this.size))
|
|
5344
5494
|
{
|
|
5345
5495
|
this.data[(layerPos.y|0)*this.size.x+layerPos.x|0] = data;
|
|
@@ -5351,7 +5501,10 @@ class TileLayer extends CanvasLayer
|
|
|
5351
5501
|
* @param {Vector2} layerPos - Local position in array
|
|
5352
5502
|
* @return {TileLayerData} */
|
|
5353
5503
|
getData(layerPos)
|
|
5354
|
-
{
|
|
5504
|
+
{
|
|
5505
|
+
ASSERT(isVector2(layerPos), 'layerPos must be a Vector2');
|
|
5506
|
+
return layerPos.arrayCheck(this.size) && this.data[(layerPos.y|0)*this.size.x+layerPos.x|0];
|
|
5507
|
+
}
|
|
5355
5508
|
|
|
5356
5509
|
// Render the tile layer, called automatically by the engine
|
|
5357
5510
|
render()
|
|
@@ -5359,10 +5512,11 @@ class TileLayer extends CanvasLayer
|
|
|
5359
5512
|
ASSERT(drawContext !== this.context, 'must call redrawEnd() after drawing tiles!');
|
|
5360
5513
|
|
|
5361
5514
|
// draw the tile layer as a single tile
|
|
5362
|
-
const tileInfo = new TileInfo().setFullImage(this.
|
|
5363
|
-
const
|
|
5364
|
-
const
|
|
5365
|
-
|
|
5515
|
+
const tileInfo = new TileInfo().setFullImage(this.textureInfo);
|
|
5516
|
+
const size = this.drawSize || this.size;
|
|
5517
|
+
const pos = this.pos.add(size.scale(.5));
|
|
5518
|
+
const useWebGL = glEnable && this.textureInfo.hasWebGL();
|
|
5519
|
+
drawTile(pos, size, tileInfo, WHITE, 0, false, CLEAR_BLACK, useWebGL);
|
|
5366
5520
|
}
|
|
5367
5521
|
|
|
5368
5522
|
/** Draw all the tile data to an offscreen canvas
|
|
@@ -5374,8 +5528,7 @@ class TileLayer extends CanvasLayer
|
|
|
5374
5528
|
for (let y = this.size.y; y--;)
|
|
5375
5529
|
this.drawTileData(vec2(x,y), false);
|
|
5376
5530
|
this.redrawEnd();
|
|
5377
|
-
|
|
5378
|
-
this.useWebGL(); // update WebGL texture
|
|
5531
|
+
this.useWebGL();
|
|
5379
5532
|
}
|
|
5380
5533
|
|
|
5381
5534
|
/** Call to start the redraw process
|
|
@@ -5383,6 +5536,8 @@ class TileLayer extends CanvasLayer
|
|
|
5383
5536
|
* @param {boolean} [clear] - Should it clear the canvas before drawing */
|
|
5384
5537
|
redrawStart(clear=false)
|
|
5385
5538
|
{
|
|
5539
|
+
if (!this.context) return;
|
|
5540
|
+
|
|
5386
5541
|
// save current render settings
|
|
5387
5542
|
/** @type {[HTMLCanvasElement|OffscreenCanvas, CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D, Vector2, Vector2, number]} */
|
|
5388
5543
|
this.savedRenderSettings = [drawCanvas, drawContext, mainCanvasSize, cameraPos, cameraScale];
|
|
@@ -5411,6 +5566,8 @@ class TileLayer extends CanvasLayer
|
|
|
5411
5566
|
/** Call to end the redraw process */
|
|
5412
5567
|
redrawEnd()
|
|
5413
5568
|
{
|
|
5569
|
+
if (!this.context) return;
|
|
5570
|
+
|
|
5414
5571
|
ASSERT(drawContext === this.context, 'must call redrawStart() before drawing tiles');
|
|
5415
5572
|
glCopyToContext(drawContext);
|
|
5416
5573
|
//debugSaveCanvas(this.canvas);
|
|
@@ -5427,6 +5584,8 @@ class TileLayer extends CanvasLayer
|
|
|
5427
5584
|
*/
|
|
5428
5585
|
drawTileData(layerPos, clear=true)
|
|
5429
5586
|
{
|
|
5587
|
+
if (!this.context) return;
|
|
5588
|
+
|
|
5430
5589
|
// clear out where the tile was, for full opaque tiles this can be skipped
|
|
5431
5590
|
const s = this.tileInfo.size;
|
|
5432
5591
|
if (clear)
|
|
@@ -5454,6 +5613,7 @@ class TileLayer extends CanvasLayer
|
|
|
5454
5613
|
* - there can be multiple tile collision layers
|
|
5455
5614
|
* - tile collision layers should not overlap each other
|
|
5456
5615
|
* @extends TileLayer
|
|
5616
|
+
* @memberof TileLayers
|
|
5457
5617
|
*/
|
|
5458
5618
|
class TileCollisionLayer extends TileLayer
|
|
5459
5619
|
{
|
|
@@ -5462,12 +5622,10 @@ class TileCollisionLayer extends TileLayer
|
|
|
5462
5622
|
* @param {Vector2} size - World space size
|
|
5463
5623
|
* @param {TileInfo} [tileInfo] - Tile info for layer
|
|
5464
5624
|
* @param {number} [renderOrder] - Objects are sorted by renderOrder
|
|
5465
|
-
* @param {boolean} [useWebGL=glEnable] - Use accelerated WebGL rendering
|
|
5466
5625
|
*/
|
|
5467
|
-
constructor(position, size, tileInfo=tile(), renderOrder=0
|
|
5626
|
+
constructor(position, size, tileInfo=tile(), renderOrder=0)
|
|
5468
5627
|
{
|
|
5469
|
-
|
|
5470
|
-
super(position, size.floor(), tileInfo, scale, renderOrder, useWebGL);
|
|
5628
|
+
super(position, size.floor(), tileInfo, renderOrder);
|
|
5471
5629
|
|
|
5472
5630
|
/** @property {Array<number>} - The tile collision grid */
|
|
5473
5631
|
this.collisionData = [];
|
|
@@ -5497,6 +5655,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5497
5655
|
* @param {Vector2} size - width and height of tile collision 2d grid */
|
|
5498
5656
|
initCollision(size)
|
|
5499
5657
|
{
|
|
5658
|
+
ASSERT(isVector2(size), 'size must be a Vector2');
|
|
5500
5659
|
this.size = size.floor();
|
|
5501
5660
|
this.collisionData = [];
|
|
5502
5661
|
this.collisionData.length = size.area();
|
|
@@ -5508,6 +5667,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5508
5667
|
* @param {number} [data] */
|
|
5509
5668
|
setCollisionData(gridPos, data=1)
|
|
5510
5669
|
{
|
|
5670
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
5511
5671
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
5512
5672
|
gridPos.arrayCheck(this.size) && (this.collisionData[i] = data);
|
|
5513
5673
|
}
|
|
@@ -5517,6 +5677,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5517
5677
|
* @return {number} */
|
|
5518
5678
|
getCollisionData(gridPos)
|
|
5519
5679
|
{
|
|
5680
|
+
ASSERT(isVector2(gridPos), 'gridPos must be a Vector2');
|
|
5520
5681
|
const i = (gridPos.y|0)*this.size.x + gridPos.x|0;
|
|
5521
5682
|
return gridPos.arrayCheck(this.size) ? this.collisionData[i] : 0;
|
|
5522
5683
|
}
|
|
@@ -5528,6 +5689,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
5528
5689
|
* @return {boolean} */
|
|
5529
5690
|
collisionTest(pos, size=new Vector2, object)
|
|
5530
5691
|
{
|
|
5692
|
+
ASSERT(isVector2(pos) && isVector2(size), 'pos and size must be Vector2s');
|
|
5693
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
5694
|
+
|
|
5531
5695
|
// transform to local layer space
|
|
5532
5696
|
const posX = pos.x - this.pos.x;
|
|
5533
5697
|
const posY = pos.y - this.pos.y;
|
|
@@ -5558,6 +5722,9 @@ class TileCollisionLayer extends TileLayer
|
|
|
5558
5722
|
* @return {Vector2} */
|
|
5559
5723
|
collisionRaycast(posStart, posEnd, object)
|
|
5560
5724
|
{
|
|
5725
|
+
ASSERT(isVector2(posStart) && isVector2(posEnd), 'positions must be Vector2s');
|
|
5726
|
+
ASSERT(!object || object instanceof EngineObject, 'object must be an EngineObject');
|
|
5727
|
+
|
|
5561
5728
|
// transform to local layer space
|
|
5562
5729
|
const posStartX = posStart.x - this.pos.x;
|
|
5563
5730
|
const posStartY = posStart.y - this.pos.y;
|
|
@@ -5607,9 +5774,16 @@ class TileCollisionLayer extends TileLayer
|
|
|
5607
5774
|
* LittleJS Particle System
|
|
5608
5775
|
*/
|
|
5609
5776
|
|
|
5777
|
+
/**
|
|
5778
|
+
* @callback ParticleCallbackFunction - Function that processes a particle
|
|
5779
|
+
* @param {Particle} particle
|
|
5780
|
+
* @memberof Engine
|
|
5781
|
+
*/
|
|
5782
|
+
|
|
5610
5783
|
/**
|
|
5611
5784
|
* Particle Emitter - Spawns particles with the given settings
|
|
5612
5785
|
* @extends EngineObject
|
|
5786
|
+
* @memberof Engine
|
|
5613
5787
|
* @example
|
|
5614
5788
|
* // create a particle emitter
|
|
5615
5789
|
* let pos = vec2(2,3);
|
|
@@ -5619,7 +5793,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
5619
5793
|
* tile(0, 16), // tileInfo
|
|
5620
5794
|
* rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
|
|
5621
5795
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
5622
|
-
*
|
|
5796
|
+
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
5623
5797
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
5624
5798
|
* .5, 1 // randomness, collide, additive, randomColorLinear, renderOrder
|
|
5625
5799
|
* );
|
|
@@ -5634,10 +5808,10 @@ class ParticleEmitter extends EngineObject
|
|
|
5634
5808
|
* @param {number} [emitRate] - How many particles per second to spawn, does not emit if 0
|
|
5635
5809
|
* @param {number} [emitConeAngle=PI] - Local angle to apply velocity to particles from emitter
|
|
5636
5810
|
* @param {TileInfo} [tileInfo] - Tile info to render particles (undefined is untextured)
|
|
5637
|
-
* @param {Color} [colorStartA=
|
|
5638
|
-
* @param {Color} [colorStartB=
|
|
5639
|
-
* @param {Color} [colorEndA=
|
|
5640
|
-
* @param {Color} [colorEndB=
|
|
5811
|
+
* @param {Color} [colorStartA=WHITE] - Color at start of life 1, randomized between start colors
|
|
5812
|
+
* @param {Color} [colorStartB=WHITE] - Color at start of life 2, randomized between start colors
|
|
5813
|
+
* @param {Color} [colorEndA=CLEAR_WHITE] - Color at end of life 1, randomized between end colors
|
|
5814
|
+
* @param {Color} [colorEndB=CLEAR_WHITE] - Color at end of life 2, randomized between end colors
|
|
5641
5815
|
* @param {number} [particleTime] - How long particles live
|
|
5642
5816
|
* @param {number} [sizeStart] - How big are particles at start
|
|
5643
5817
|
* @param {number} [sizeEnd] - How big are particles at end
|
|
@@ -5664,10 +5838,10 @@ class ParticleEmitter extends EngineObject
|
|
|
5664
5838
|
emitRate = 100,
|
|
5665
5839
|
emitConeAngle = PI,
|
|
5666
5840
|
tileInfo,
|
|
5667
|
-
colorStartA =
|
|
5668
|
-
colorStartB =
|
|
5669
|
-
colorEndA =
|
|
5670
|
-
colorEndB =
|
|
5841
|
+
colorStartA = WHITE,
|
|
5842
|
+
colorStartB = WHITE,
|
|
5843
|
+
colorEndA = CLEAR_WHITE,
|
|
5844
|
+
colorEndB = CLEAR_WHITE,
|
|
5671
5845
|
particleTime = .5,
|
|
5672
5846
|
sizeStart = .1,
|
|
5673
5847
|
sizeEnd = 1,
|
|
@@ -5690,7 +5864,8 @@ class ParticleEmitter extends EngineObject
|
|
|
5690
5864
|
|
|
5691
5865
|
// emitter settings
|
|
5692
5866
|
/** @property {number|Vector2} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
5693
|
-
this.emitSize = emitSize
|
|
5867
|
+
this.emitSize = emitSize instanceof Vector2 ?
|
|
5868
|
+
emitSize.copy() : emitSize;
|
|
5694
5869
|
/** @property {number} - How long to stay alive (0 is forever) */
|
|
5695
5870
|
this.emitTime = emitTime;
|
|
5696
5871
|
/** @property {number} - How many particles per second to spawn, does not emit if 0 */
|
|
@@ -5700,13 +5875,13 @@ class ParticleEmitter extends EngineObject
|
|
|
5700
5875
|
|
|
5701
5876
|
// color settings
|
|
5702
5877
|
/** @property {Color} - Color at start of life 1, randomized between start colors */
|
|
5703
|
-
this.colorStartA = colorStartA;
|
|
5878
|
+
this.colorStartA = colorStartA.copy();
|
|
5704
5879
|
/** @property {Color} - Color at start of life 2, randomized between start colors */
|
|
5705
|
-
this.colorStartB = colorStartB;
|
|
5880
|
+
this.colorStartB = colorStartB.copy();
|
|
5706
5881
|
/** @property {Color} - Color at end of life 1, randomized between end colors */
|
|
5707
|
-
this.colorEndA = colorEndA;
|
|
5882
|
+
this.colorEndA = colorEndA.copy();
|
|
5708
5883
|
/** @property {Color} - Color at end of life 2, randomized between end colors */
|
|
5709
|
-
this.colorEndB = colorEndB;
|
|
5884
|
+
this.colorEndB = colorEndB.copy();
|
|
5710
5885
|
/** @property {boolean} - Should color be randomized linearly or across each component */
|
|
5711
5886
|
this.randomColorLinear = randomColorLinear;
|
|
5712
5887
|
|
|
@@ -5741,9 +5916,9 @@ class ParticleEmitter extends EngineObject
|
|
|
5741
5916
|
this.localSpace = localSpace;
|
|
5742
5917
|
/** @property {number} - If non zero the particle is drawn as a trail, stretched in the direction of velocity */
|
|
5743
5918
|
this.trailScale = 0;
|
|
5744
|
-
/** @property {
|
|
5919
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is destroyed */
|
|
5745
5920
|
this.particleDestroyCallback = undefined;
|
|
5746
|
-
/** @property {
|
|
5921
|
+
/** @property {ParticleCallbackFunction} - Callback when particle is created */
|
|
5747
5922
|
this.particleCreateCallback = undefined;
|
|
5748
5923
|
/** @property {number} - Track particle emit time */
|
|
5749
5924
|
this.emitTimeBuffer = 0;
|
|
@@ -5844,6 +6019,7 @@ class ParticleEmitter extends EngineObject
|
|
|
5844
6019
|
/**
|
|
5845
6020
|
* Particle Object - Created automatically by Particle Emitters
|
|
5846
6021
|
* @extends EngineObject
|
|
6022
|
+
* @memberof Engine
|
|
5847
6023
|
*/
|
|
5848
6024
|
class Particle extends EngineObject
|
|
5849
6025
|
{
|
|
@@ -5862,7 +6038,7 @@ class Particle extends EngineObject
|
|
|
5862
6038
|
* @param {boolean} additive - Does it use additive blend mode
|
|
5863
6039
|
* @param {number} trailScale - If a trail, how long to make it
|
|
5864
6040
|
* @param {ParticleEmitter} [localSpaceEmitter] - Parent emitter if local space
|
|
5865
|
-
* @param {
|
|
6041
|
+
* @param {ParticleCallbackFunction} [destroyCallback] - Callback when particle dies
|
|
5866
6042
|
*/
|
|
5867
6043
|
constructor(position, tileInfo, angle, colorStart, colorEnd, lifeTime, sizeStart, sizeEnd, fadeRate, additive, trailScale, localSpaceEmitter, destroyCallback
|
|
5868
6044
|
)
|
|
@@ -5871,14 +6047,14 @@ class Particle extends EngineObject
|
|
|
5871
6047
|
|
|
5872
6048
|
/** @property {Color} - Color at start of life */
|
|
5873
6049
|
this.colorStart = colorStart;
|
|
5874
|
-
/** @property {Color} -
|
|
5875
|
-
this.
|
|
6050
|
+
/** @property {Color} - Color at end of life */
|
|
6051
|
+
this.colorEnd = colorEnd;
|
|
5876
6052
|
/** @property {number} - How long to live for */
|
|
5877
6053
|
this.lifeTime = lifeTime;
|
|
5878
6054
|
/** @property {number} - Size at start of life */
|
|
5879
6055
|
this.sizeStart = sizeStart;
|
|
5880
|
-
/** @property {number} -
|
|
5881
|
-
this.
|
|
6056
|
+
/** @property {number} - Size at end of life */
|
|
6057
|
+
this.sizeEnd = sizeEnd;
|
|
5882
6058
|
/** @property {number} - How quick to fade in/out */
|
|
5883
6059
|
this.fadeRate = fadeRate;
|
|
5884
6060
|
/** @property {boolean} - Is it additive */
|
|
@@ -5887,7 +6063,7 @@ class Particle extends EngineObject
|
|
|
5887
6063
|
this.trailScale = trailScale;
|
|
5888
6064
|
/** @property {ParticleEmitter} - Parent emitter if local space */
|
|
5889
6065
|
this.localSpaceEmitter = localSpaceEmitter;
|
|
5890
|
-
/** @property {
|
|
6066
|
+
/** @property {ParticleCallbackFunction} - Called when particle dies */
|
|
5891
6067
|
this.destroyCallback = destroyCallback;
|
|
5892
6068
|
// particles do not clamp speed by default
|
|
5893
6069
|
this.clampSpeed = false;
|
|
@@ -5914,54 +6090,58 @@ class Particle extends EngineObject
|
|
|
5914
6090
|
/** Render the particle, automatically called each frame, sorted by renderOrder */
|
|
5915
6091
|
render()
|
|
5916
6092
|
{
|
|
5917
|
-
//
|
|
5918
|
-
const
|
|
5919
|
-
const radius = this.sizeStart +
|
|
5920
|
-
|
|
6093
|
+
// lerp color and size
|
|
6094
|
+
const p1 = this.lifeTime > 0 ? min((time - this.spawnTime) / this.lifeTime, 1) : 1, p2 = 1-p1;
|
|
6095
|
+
const radius = p2 * this.sizeStart + p1 * this.sizeEnd;
|
|
6096
|
+
this.size.x = this.size.y = radius;
|
|
6097
|
+
this.color.r = p2 * this.colorStart.r + p1 * this.colorEnd.r;
|
|
6098
|
+
this.color.g = p2 * this.colorStart.g + p1 * this.colorEnd.g;
|
|
6099
|
+
this.color.b = p2 * this.colorStart.b + p1 * this.colorEnd.b;
|
|
6100
|
+
this.color.a = p2 * this.colorStart.a + p1 * this.colorEnd.a;
|
|
6101
|
+
|
|
6102
|
+
// fade alpha
|
|
5921
6103
|
const fadeRate = this.fadeRate/2;
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
this.colorStart.g + p * this.colorEndDelta.g,
|
|
5925
|
-
this.colorStart.b + p * this.colorEndDelta.b,
|
|
5926
|
-
(this.colorStart.a + p * this.colorEndDelta.a) *
|
|
5927
|
-
(p < fadeRate ? p/fadeRate : p > 1-fadeRate ? (1-p)/fadeRate : 1)); // fade alpha
|
|
6104
|
+
this.color.a *= p1 < fadeRate ? p1/fadeRate :
|
|
6105
|
+
p1 > 1-fadeRate ? (1-p1)/fadeRate : 1;
|
|
5928
6106
|
|
|
5929
6107
|
// draw the particle
|
|
5930
6108
|
this.additive && setBlendMode(true);
|
|
5931
6109
|
|
|
6110
|
+
// update the position and angle for drawing
|
|
5932
6111
|
let pos = this.pos, angle = this.angle;
|
|
5933
6112
|
if (this.localSpaceEmitter)
|
|
5934
6113
|
{
|
|
5935
6114
|
// in local space of emitter
|
|
5936
|
-
|
|
6115
|
+
const a = this.localSpaceEmitter.angle;
|
|
6116
|
+
const c = Math.cos(a), s = Math.sin(a);
|
|
6117
|
+
pos = this.localSpaceEmitter.pos.add(
|
|
6118
|
+
new Vector2(pos.x*c - pos.y*s, pos.x*s + pos.y*c));
|
|
5937
6119
|
angle += this.localSpaceEmitter.angle;
|
|
5938
6120
|
}
|
|
5939
6121
|
if (this.trailScale)
|
|
5940
6122
|
{
|
|
5941
6123
|
// trail style particles
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
const speed =
|
|
6124
|
+
const direction = this.localSpaceEmitter ?
|
|
6125
|
+
this.velocity.rotate(-this.localSpaceEmitter.angle) :
|
|
6126
|
+
this.velocity;
|
|
6127
|
+
const speed = direction.length();
|
|
5946
6128
|
if (speed)
|
|
5947
6129
|
{
|
|
5948
|
-
|
|
6130
|
+
// stretch in direction of motion
|
|
5949
6131
|
const trailLength = speed * this.trailScale;
|
|
5950
|
-
size.y = max(size.x, trailLength);
|
|
5951
|
-
angle = direction.
|
|
5952
|
-
drawTile(pos
|
|
6132
|
+
this.size.y = max(this.size.x, trailLength);
|
|
6133
|
+
angle = Math.atan2(direction.x, direction.y);
|
|
6134
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
5953
6135
|
}
|
|
5954
6136
|
}
|
|
5955
6137
|
else
|
|
5956
|
-
drawTile(pos, size, this.tileInfo, color, angle, this.mirror);
|
|
6138
|
+
drawTile(pos, this.size, this.tileInfo, this.color, angle, this.mirror);
|
|
5957
6139
|
this.additive && setBlendMode();
|
|
5958
|
-
debugParticles && debugRect(pos, size, '#f005', 0, angle);
|
|
6140
|
+
debugParticles && debugRect(pos, this.size, '#f005', 0, angle);
|
|
5959
6141
|
|
|
5960
|
-
if (
|
|
6142
|
+
if (p1 === 1)
|
|
5961
6143
|
{
|
|
5962
|
-
// destroy particle when
|
|
5963
|
-
this.color = color;
|
|
5964
|
-
this.size = size;
|
|
6144
|
+
// destroy particle when its time runs out
|
|
5965
6145
|
this.destroyCallback && this.destroyCallback(this);
|
|
5966
6146
|
this.destroyed = 1;
|
|
5967
6147
|
}
|
|
@@ -6026,8 +6206,14 @@ function medalsInit(saveName)
|
|
|
6026
6206
|
}
|
|
6027
6207
|
}
|
|
6028
6208
|
|
|
6209
|
+
/**
|
|
6210
|
+
* @callback MedalCallbackFunction - Function that processes a medal
|
|
6211
|
+
* @param {Medal} medal
|
|
6212
|
+
* @memberof Medals
|
|
6213
|
+
*/
|
|
6214
|
+
|
|
6029
6215
|
/** Calls a function for each medal
|
|
6030
|
-
* @param {
|
|
6216
|
+
* @param {MedalCallbackFunction} callback
|
|
6031
6217
|
* @memberof Medals */
|
|
6032
6218
|
function medalsForEach(callback)
|
|
6033
6219
|
{ Object.values(medals).forEach(medal=>callback(medal)); }
|
|
@@ -6036,6 +6222,7 @@ function medalsForEach(callback)
|
|
|
6036
6222
|
|
|
6037
6223
|
/**
|
|
6038
6224
|
* Medal - Tracks an unlockable medal
|
|
6225
|
+
* @memberof Medals
|
|
6039
6226
|
* @example
|
|
6040
6227
|
* // create a medal
|
|
6041
6228
|
* const medal_example = new Medal(0, 'Example Medal', 'More info about the medal goes here.', '🎖️');
|
|
@@ -6104,11 +6291,12 @@ class Medal
|
|
|
6104
6291
|
const height = medalDisplaySize.y;
|
|
6105
6292
|
const x = overlayCanvas.width - width;
|
|
6106
6293
|
const y = -height*hidePercent;
|
|
6294
|
+
const backgroundColor = hsl(0,0,.9);
|
|
6107
6295
|
|
|
6108
6296
|
// draw containing rect and clip to that region
|
|
6109
6297
|
context.save();
|
|
6110
6298
|
context.beginPath();
|
|
6111
|
-
context.fillStyle =
|
|
6299
|
+
context.fillStyle = backgroundColor.toString();
|
|
6112
6300
|
context.strokeStyle = BLACK.toString();
|
|
6113
6301
|
context.lineWidth = 3;
|
|
6114
6302
|
context.rect(x, y, width, height);
|
|
@@ -6179,7 +6367,7 @@ let glContext;
|
|
|
6179
6367
|
let glAntialias = true;
|
|
6180
6368
|
|
|
6181
6369
|
// WebGL internal variables not exposed to documentation
|
|
6182
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount;
|
|
6370
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos;
|
|
6183
6371
|
|
|
6184
6372
|
// WebGL internal constants
|
|
6185
6373
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -6195,6 +6383,9 @@ const gl_MAX_POLY_VERTEXES = gl_ARRAY_BUFFER_SIZE / gl_POLY_VERTEX_BYTE_STRIDE |
|
|
|
6195
6383
|
// Initialize WebGL, called automatically by the engine
|
|
6196
6384
|
function glInit()
|
|
6197
6385
|
{
|
|
6386
|
+
// keep set of texture infos so they can be restored if context is lost
|
|
6387
|
+
glTextureInfos = new Set;
|
|
6388
|
+
|
|
6198
6389
|
if (!glEnable || headlessMode) return;
|
|
6199
6390
|
|
|
6200
6391
|
// create the canvas and textures
|
|
@@ -6212,68 +6403,101 @@ function glInit()
|
|
|
6212
6403
|
// create the WebGL canvas
|
|
6213
6404
|
const rootElement = mainCanvas.parentElement;
|
|
6214
6405
|
rootElement.appendChild(glCanvas);
|
|
6406
|
+
|
|
6407
|
+
// startup webgl
|
|
6408
|
+
initWebGL();
|
|
6409
|
+
|
|
6410
|
+
// setup context lost and restore handlers
|
|
6411
|
+
glCanvas.addEventListener('webglcontextlost', (e)=>
|
|
6412
|
+
{
|
|
6413
|
+
glEnable = false; // disable WebGL rendering
|
|
6414
|
+
glCanvas.style.display = 'none'; // hide the gl canvas
|
|
6415
|
+
e.preventDefault(); // prevent default to allow restoration
|
|
6416
|
+
LOG('WebGL context lost! Switching to Canvas2d rendering.');
|
|
6417
|
+
|
|
6418
|
+
// remove WebGL textures
|
|
6419
|
+
for (const info of glTextureInfos)
|
|
6420
|
+
info.glTexture = undefined;
|
|
6421
|
+
glActiveTexture = undefined;
|
|
6422
|
+
pluginList.forEach(plugin=>plugin.glContextLost?.());
|
|
6423
|
+
});
|
|
6424
|
+
glCanvas.addEventListener('webglcontextrestored', ()=>
|
|
6425
|
+
{
|
|
6426
|
+
glEnable = true; // disable WebGL rendering
|
|
6427
|
+
glCanvas.style.display = ''; // show the gl canvas
|
|
6428
|
+
LOG('WebGL context restored, reinitializing...');
|
|
6215
6429
|
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
6223
|
-
'in float r;'+ // in: rotation
|
|
6224
|
-
'out vec2 v;'+ // out: uv
|
|
6225
|
-
'out vec4 d,e;'+ // out: color, additiveColor
|
|
6226
|
-
'void main(){'+ // shader entry point
|
|
6227
|
-
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
6228
|
-
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
6229
|
-
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
6230
|
-
'd=c;e=a;'+ // pass colors to fragment shader
|
|
6231
|
-
'}' // end of shader
|
|
6232
|
-
,
|
|
6233
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
6234
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
6235
|
-
'uniform sampler2D s;'+ // texture
|
|
6236
|
-
'in vec2 v;'+ // in: uv
|
|
6237
|
-
'in vec4 d,e;'+ // in: color, additiveColor
|
|
6238
|
-
'out vec4 c;'+ // out: color
|
|
6239
|
-
'void main(){'+ // shader entry point
|
|
6240
|
-
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
6241
|
-
'}' // end of shader
|
|
6242
|
-
);
|
|
6430
|
+
// reinit WebGL and restore textures
|
|
6431
|
+
initWebGL();
|
|
6432
|
+
for (const info of glTextureInfos)
|
|
6433
|
+
info.glTexture = glCreateTexture(info.image);
|
|
6434
|
+
pluginList.forEach(plugin=>plugin.glContextRestored?.());
|
|
6435
|
+
});
|
|
6243
6436
|
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
|
|
6253
|
-
|
|
6254
|
-
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6437
|
+
function initWebGL()
|
|
6438
|
+
{
|
|
6439
|
+
// setup instanced rendering shader program
|
|
6440
|
+
glShader = glCreateProgram(
|
|
6441
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6442
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6443
|
+
'uniform mat4 m;'+ // transform matrix
|
|
6444
|
+
'in vec2 g;'+ // in: geometry
|
|
6445
|
+
'in vec4 p,u,c,a;'+ // in: position/size, uvs, color, additiveColor
|
|
6446
|
+
'in float r;'+ // in: rotation
|
|
6447
|
+
'out vec2 v;'+ // out: uv
|
|
6448
|
+
'out vec4 d,e;'+ // out: color, additiveColor
|
|
6449
|
+
'void main(){'+ // shader entry point
|
|
6450
|
+
'vec2 s=(g-.5)*p.zw;'+ // get size offset
|
|
6451
|
+
'gl_Position=m*vec4(p.xy+s*cos(r)-vec2(-s.y,s)*sin(r),1,1);'+ // transform position
|
|
6452
|
+
'v=mix(u.xw,u.zy,g);'+ // pass uv to fragment shader
|
|
6453
|
+
'd=c;e=a;'+ // pass colors to fragment shader
|
|
6454
|
+
'}' // end of shader
|
|
6455
|
+
,
|
|
6456
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6457
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6458
|
+
'uniform sampler2D s;'+ // texture
|
|
6459
|
+
'in vec2 v;'+ // in: uv
|
|
6460
|
+
'in vec4 d,e;'+ // in: color, additiveColor
|
|
6461
|
+
'out vec4 c;'+ // out: color
|
|
6462
|
+
'void main(){'+ // shader entry point
|
|
6463
|
+
'c=texture(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
6464
|
+
'}' // end of shader
|
|
6465
|
+
);
|
|
6466
|
+
|
|
6467
|
+
// setup poly rendering shaders
|
|
6468
|
+
glPolyShader = glCreateProgram(
|
|
6469
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6470
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6471
|
+
'uniform mat4 m;'+ // transform matrix
|
|
6472
|
+
'in vec2 p;'+ // in: position
|
|
6473
|
+
'in vec4 c;'+ // in: color
|
|
6474
|
+
'out vec4 d;'+ // out: color
|
|
6475
|
+
'void main(){'+ // shader entry point
|
|
6476
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
6477
|
+
'd=c;'+ // pass color to fragment shader
|
|
6478
|
+
'}' // end of shader
|
|
6479
|
+
,
|
|
6480
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
6481
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
6482
|
+
'in vec4 d;'+ // in: color
|
|
6483
|
+
'out vec4 c;'+ // out: color
|
|
6484
|
+
'void main(){'+ // shader entry point
|
|
6485
|
+
'c=d;'+ // set color
|
|
6486
|
+
'}' // end of shader
|
|
6487
|
+
);
|
|
6265
6488
|
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6489
|
+
// init buffers
|
|
6490
|
+
const glInstanceData = new ArrayBuffer(gl_ARRAY_BUFFER_SIZE);
|
|
6491
|
+
glPositionData = new Float32Array(glInstanceData);
|
|
6492
|
+
glColorData = new Uint32Array(glInstanceData);
|
|
6493
|
+
glArrayBuffer = glContext.createBuffer();
|
|
6494
|
+
glGeometryBuffer = glContext.createBuffer();
|
|
6272
6495
|
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6496
|
+
// create the geometry buffer, triangle strip square
|
|
6497
|
+
const geometry = new Float32Array([glBatchCount=0,0,1,0,0,1,1,1]);
|
|
6498
|
+
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
6499
|
+
glContext.bufferData(glContext.ARRAY_BUFFER, geometry, glContext.STATIC_DRAW);
|
|
6500
|
+
}
|
|
6277
6501
|
}
|
|
6278
6502
|
|
|
6279
6503
|
function glSetInstancedMode()
|
|
@@ -6380,7 +6604,7 @@ function glPreRender()
|
|
|
6380
6604
|
// start with additive blending off
|
|
6381
6605
|
glAdditive = glBatchAdditive = false;
|
|
6382
6606
|
|
|
6383
|
-
// force it to
|
|
6607
|
+
// force it to set instanced mode by first setting poly mode true
|
|
6384
6608
|
glPolyMode = true;
|
|
6385
6609
|
glSetInstancedMode();
|
|
6386
6610
|
}
|
|
@@ -6521,6 +6745,41 @@ function glSetTextureData(texture, image)
|
|
|
6521
6745
|
glContext.bindTexture(glContext.TEXTURE_2D, glActiveTexture); // rebind active texture
|
|
6522
6746
|
}
|
|
6523
6747
|
|
|
6748
|
+
/** Tells WebGL to create or update the glTexture and start tracking it
|
|
6749
|
+
* @param {TextureInfo} textureInfo
|
|
6750
|
+
* @memberof WebGL */
|
|
6751
|
+
function glRegisterTextureInfo(textureInfo)
|
|
6752
|
+
{
|
|
6753
|
+
if (headlessMode) return;
|
|
6754
|
+
|
|
6755
|
+
// add texture info to tracking list even if gl is not enabled
|
|
6756
|
+
glTextureInfos.add(textureInfo);
|
|
6757
|
+
|
|
6758
|
+
if (!glContext) return;
|
|
6759
|
+
|
|
6760
|
+
// create or set the texture data
|
|
6761
|
+
if (textureInfo.glTexture)
|
|
6762
|
+
glSetTextureData(textureInfo.glTexture, textureInfo.image);
|
|
6763
|
+
else
|
|
6764
|
+
textureInfo.glTexture = glCreateTexture(textureInfo.image);
|
|
6765
|
+
}
|
|
6766
|
+
|
|
6767
|
+
/** Tells WebGL to destroy the glTexture and stop tracking it
|
|
6768
|
+
* @param {TextureInfo} textureInfo
|
|
6769
|
+
* @memberof WebGL */
|
|
6770
|
+
function glUnregisterTextureInfo(textureInfo)
|
|
6771
|
+
{
|
|
6772
|
+
if (headlessMode) return;
|
|
6773
|
+
|
|
6774
|
+
// delete texture info from tracking list even if gl is not enabled
|
|
6775
|
+
glTextureInfos.delete(textureInfo);
|
|
6776
|
+
|
|
6777
|
+
// unset and destroy the texture
|
|
6778
|
+
const glTexture = textureInfo.glTexture;
|
|
6779
|
+
textureInfo.glTexture = undefined;
|
|
6780
|
+
glDeleteTexture(glTexture);
|
|
6781
|
+
}
|
|
6782
|
+
|
|
6524
6783
|
/** Draw all sprites and clear out the buffer, called automatically by the system whenever necessary
|
|
6525
6784
|
* @memberof WebGL */
|
|
6526
6785
|
function glFlush()
|
|
@@ -6910,24 +7169,25 @@ function glPolyStrip(points)
|
|
|
6910
7169
|
return strip;
|
|
6911
7170
|
}
|
|
6912
7171
|
/**
|
|
6913
|
-
* LittleJS Newgrounds
|
|
7172
|
+
* LittleJS Newgrounds Plugin
|
|
6914
7173
|
* - NewgroundsMedal extends Medal with Newgrounds API functionality
|
|
6915
|
-
* - Call new NewgroundsPlugin() to setup Newgrounds
|
|
7174
|
+
* - Call new NewgroundsPlugin(app_id) to setup Newgrounds
|
|
6916
7175
|
* - Uses CryptoJS for encryption if optional cipher is provided
|
|
7176
|
+
* - provides functions to interact with medals scoreboards
|
|
6917
7177
|
* - Keeps connection alive and logs views
|
|
6918
|
-
*
|
|
6919
|
-
* - Functions to unlock medals
|
|
7178
|
+
* @namespace Newgrounds
|
|
6920
7179
|
*/
|
|
6921
7180
|
|
|
6922
7181
|
/** Global Newgrounds object
|
|
6923
7182
|
* @type {NewgroundsPlugin}
|
|
6924
|
-
* @memberof
|
|
7183
|
+
* @memberof Newgrounds */
|
|
6925
7184
|
let newgrounds;
|
|
6926
7185
|
|
|
6927
7186
|
///////////////////////////////////////////////////////////////////////////////
|
|
6928
7187
|
/**
|
|
6929
7188
|
* Newgrounds medal auto unlocks in newgrounds API
|
|
6930
7189
|
* @extends Medal
|
|
7190
|
+
* @memberof Newgrounds
|
|
6931
7191
|
*/
|
|
6932
7192
|
class NewgroundsMedal extends Medal
|
|
6933
7193
|
{
|
|
@@ -6952,6 +7212,7 @@ class NewgroundsMedal extends Medal
|
|
|
6952
7212
|
///////////////////////////////////////////////////////////////////////////////
|
|
6953
7213
|
/**
|
|
6954
7214
|
* Newgrounds API object
|
|
7215
|
+
* @memberof Newgrounds
|
|
6955
7216
|
*/
|
|
6956
7217
|
class NewgroundsPlugin
|
|
6957
7218
|
{
|
|
@@ -7087,99 +7348,119 @@ class NewgroundsPlugin
|
|
|
7087
7348
|
/**
|
|
7088
7349
|
* LittleJS Post Processing Plugin
|
|
7089
7350
|
* - Supports shadertoy style post processing shaders
|
|
7090
|
-
* - call new
|
|
7351
|
+
* - call new PostProcessPlugin() to setup post processing
|
|
7091
7352
|
* - can be enabled to pass other canvases through a final shader
|
|
7353
|
+
* @namespace PostProcess
|
|
7092
7354
|
*/
|
|
7093
7355
|
|
|
7094
7356
|
///////////////////////////////////////////////////////////////////////////////
|
|
7095
7357
|
|
|
7096
7358
|
/** Global Post Process plugin object
|
|
7097
|
-
* @type {PostProcessPlugin}
|
|
7359
|
+
* @type {PostProcessPlugin}
|
|
7360
|
+
* @memberof PostProcess */
|
|
7098
7361
|
let postProcess;
|
|
7099
7362
|
|
|
7100
7363
|
/////////////////////////////////////////////////////////////////////////
|
|
7101
7364
|
/**
|
|
7102
7365
|
* UI System Global Object
|
|
7366
|
+
* @memberof PostProcess
|
|
7103
7367
|
*/
|
|
7104
7368
|
class PostProcessPlugin
|
|
7105
7369
|
{
|
|
7106
7370
|
/** Create global post processing shader
|
|
7107
7371
|
* @param {string} shaderCode
|
|
7108
7372
|
* @param {boolean} [includeOverlay]
|
|
7373
|
+
* @param {boolean} [includeMainCanvas]
|
|
7109
7374
|
* @example
|
|
7110
7375
|
* // create the post process plugin object
|
|
7111
7376
|
* new PostProcessPlugin(shaderCode);
|
|
7112
7377
|
*/
|
|
7113
|
-
constructor(shaderCode, includeOverlay=false)
|
|
7378
|
+
constructor(shaderCode, includeOverlay=false, includeMainCanvas=true)
|
|
7114
7379
|
{
|
|
7115
7380
|
ASSERT(!postProcess, 'Post process already initialized');
|
|
7116
7381
|
postProcess = this;
|
|
7117
7382
|
|
|
7118
|
-
if (headlessMode) return;
|
|
7119
|
-
|
|
7120
|
-
if (!glEnable)
|
|
7121
|
-
{
|
|
7122
|
-
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7123
|
-
return;
|
|
7124
|
-
}
|
|
7125
|
-
|
|
7126
7383
|
if (!shaderCode) // default shader pass through
|
|
7127
7384
|
shaderCode = 'void mainImage(out vec4 c,vec2 p){c=texture(iChannel0,p/iResolution.xy);}';
|
|
7128
7385
|
|
|
7129
7386
|
/** @property {WebGLProgram} - Shader for post processing */
|
|
7130
|
-
this.shader =
|
|
7131
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7132
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7133
|
-
'in vec2 p;'+ // position
|
|
7134
|
-
'void main(){'+ // shader entry point
|
|
7135
|
-
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7136
|
-
'}' // end of shader
|
|
7137
|
-
,
|
|
7138
|
-
'#version 300 es\n' + // specify GLSL ES version
|
|
7139
|
-
'precision highp float;'+ // use highp for better accuracy
|
|
7140
|
-
'uniform sampler2D iChannel0;'+ // input texture
|
|
7141
|
-
'uniform vec3 iResolution;'+ // size of output texture
|
|
7142
|
-
'uniform float iTime;'+ // time
|
|
7143
|
-
'out vec4 c;'+ // out color
|
|
7144
|
-
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
7145
|
-
'void main(){'+ // shader entry point
|
|
7146
|
-
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
7147
|
-
'c.a=1.;'+ // always use full alpha
|
|
7148
|
-
'}' // end of shader
|
|
7149
|
-
);
|
|
7387
|
+
this.shader = undefined;
|
|
7150
7388
|
|
|
7151
7389
|
/** @property {WebGLTexture} - Texture for post processing */
|
|
7152
|
-
this.texture =
|
|
7390
|
+
this.texture = undefined;
|
|
7153
7391
|
|
|
7154
|
-
|
|
7155
|
-
|
|
7392
|
+
// setup the post processing plugin
|
|
7393
|
+
initPostProcess();
|
|
7394
|
+
engineAddPlugin(undefined, postProcessRender, postProcessContextLost, postProcessContextRestored);
|
|
7156
7395
|
|
|
7157
|
-
|
|
7158
|
-
engineAddPlugin(undefined, postProcessRender);
|
|
7159
|
-
function postProcessRender()
|
|
7396
|
+
function initPostProcess()
|
|
7160
7397
|
{
|
|
7161
7398
|
if (headlessMode) return;
|
|
7162
|
-
|
|
7163
|
-
|
|
7164
|
-
if (glEnable)
|
|
7165
|
-
{
|
|
7166
|
-
glFlush(); // clear out the buffer
|
|
7167
|
-
mainContext.drawImage(glCanvas, 0, 0); // copy to the main canvas
|
|
7168
|
-
}
|
|
7169
|
-
else
|
|
7399
|
+
|
|
7400
|
+
if (!glEnable)
|
|
7170
7401
|
{
|
|
7171
|
-
|
|
7172
|
-
|
|
7402
|
+
console.warn('PostProcessPlugin: WebGL not enabled!');
|
|
7403
|
+
return;
|
|
7173
7404
|
}
|
|
7174
7405
|
|
|
7175
|
-
|
|
7406
|
+
// create resources
|
|
7407
|
+
postProcess.texture = glCreateTexture();
|
|
7408
|
+
postProcess.shader = glCreateProgram(
|
|
7409
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7410
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7411
|
+
'in vec2 p;'+ // position
|
|
7412
|
+
'void main(){'+ // shader entry point
|
|
7413
|
+
'gl_Position=vec4(p+p-1.,1,1);'+ // set position
|
|
7414
|
+
'}' // end of shader
|
|
7415
|
+
,
|
|
7416
|
+
'#version 300 es\n' + // specify GLSL ES version
|
|
7417
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
7418
|
+
'uniform sampler2D iChannel0;'+ // input texture
|
|
7419
|
+
'uniform vec3 iResolution;'+ // size of output texture
|
|
7420
|
+
'uniform float iTime;'+ // time
|
|
7421
|
+
'out vec4 c;'+ // out color
|
|
7422
|
+
'\n' + shaderCode + '\n'+ // insert custom shader code
|
|
7423
|
+
'void main(){'+ // shader entry point
|
|
7424
|
+
'mainImage(c,gl_FragCoord.xy);'+ // call post process function
|
|
7425
|
+
'c.a=1.;'+ // always use full alpha
|
|
7426
|
+
'}' // end of shader
|
|
7427
|
+
);
|
|
7428
|
+
}
|
|
7429
|
+
function postProcessContextLost()
|
|
7430
|
+
{
|
|
7431
|
+
postProcess.shader = undefined;
|
|
7432
|
+
postProcess.texture = undefined;
|
|
7433
|
+
LOG('PostProcessPlugin: WebGL context lost');
|
|
7434
|
+
}
|
|
7435
|
+
function postProcessContextRestored()
|
|
7436
|
+
{
|
|
7437
|
+
initPostProcess();
|
|
7438
|
+
LOG('PostProcessPlugin: WebGL context restored');
|
|
7439
|
+
}
|
|
7440
|
+
function postProcessRender()
|
|
7441
|
+
{
|
|
7442
|
+
if (headlessMode) return;
|
|
7443
|
+
|
|
7444
|
+
if (!glEnable)
|
|
7445
|
+
return;
|
|
7446
|
+
|
|
7447
|
+
// clear out the buffer
|
|
7448
|
+
glFlush();
|
|
7449
|
+
|
|
7450
|
+
if (includeMainCanvas || includeOverlay)
|
|
7176
7451
|
{
|
|
7177
|
-
// copy
|
|
7178
|
-
mainContext.drawImage(
|
|
7179
|
-
|
|
7452
|
+
// copy WebGL to the main canvas
|
|
7453
|
+
mainContext.drawImage(glCanvas, 0, 0);
|
|
7454
|
+
|
|
7455
|
+
if (includeOverlay)
|
|
7456
|
+
{
|
|
7457
|
+
// copy overlay canvas so it will be included in post processing
|
|
7458
|
+
mainContext.drawImage(overlayCanvas, 0, 0);
|
|
7459
|
+
overlayCanvas.width |= 0; // clear overlay canvas
|
|
7460
|
+
}
|
|
7180
7461
|
}
|
|
7181
7462
|
|
|
7182
|
-
// setup shader program to draw
|
|
7463
|
+
// setup shader program to draw a quad
|
|
7183
7464
|
glContext.useProgram(postProcess.shader);
|
|
7184
7465
|
glContext.bindBuffer(glContext.ARRAY_BUFFER, glGeometryBuffer);
|
|
7185
7466
|
glContext.pixelStorei(glContext.UNPACK_FLIP_Y_WEBGL, 1);
|
|
@@ -7188,7 +7469,10 @@ class PostProcessPlugin
|
|
|
7188
7469
|
// set textures, pass in the 2d canvas and gl canvas in separate texture channels
|
|
7189
7470
|
glContext.activeTexture(glContext.TEXTURE0);
|
|
7190
7471
|
glContext.bindTexture(glContext.TEXTURE_2D, postProcess.texture);
|
|
7191
|
-
|
|
7472
|
+
if (includeMainCanvas || includeOverlay)
|
|
7473
|
+
{
|
|
7474
|
+
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, mainCanvas);
|
|
7475
|
+
}
|
|
7192
7476
|
|
|
7193
7477
|
// set vertex position attribute
|
|
7194
7478
|
const vertexByteStride = 8;
|
|
@@ -7207,12 +7491,15 @@ class PostProcessPlugin
|
|
|
7207
7491
|
}
|
|
7208
7492
|
/**
|
|
7209
7493
|
* LittleJS ZzFXM Plugin
|
|
7494
|
+
* @namespace ZzFXM
|
|
7210
7495
|
*/
|
|
7211
7496
|
|
|
7212
7497
|
/**
|
|
7213
7498
|
* Music Object - Stores a zzfx music track for later use
|
|
7214
7499
|
*
|
|
7215
7500
|
* <a href=https://keithclark.github.io/ZzFXM/>Create music with the ZzFXM tracker.</a>
|
|
7501
|
+
* @extends Sound
|
|
7502
|
+
* @memberof ZzFXM
|
|
7216
7503
|
* @example
|
|
7217
7504
|
* // create some music
|
|
7218
7505
|
* const music_example = new Music(
|
|
@@ -7271,7 +7558,8 @@ class ZzFXMusic extends Sound
|
|
|
7271
7558
|
* @param {Array} patterns - Array of pattern data
|
|
7272
7559
|
* @param {Array} sequence - Array of pattern indexes
|
|
7273
7560
|
* @param {number} [BPM] - Playback speed of the song in BPM
|
|
7274
|
-
* @return {Array} - Left and right channel sample data
|
|
7561
|
+
* @return {Array} - Left and right channel sample data
|
|
7562
|
+
* @memberof ZzFXM */
|
|
7275
7563
|
function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
7276
7564
|
{
|
|
7277
7565
|
let i, j, k;
|
|
@@ -7374,17 +7662,20 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
7374
7662
|
* - Buttons
|
|
7375
7663
|
* - Checkboxes
|
|
7376
7664
|
* - Images
|
|
7665
|
+
* @namespace UISystem
|
|
7377
7666
|
*/
|
|
7378
7667
|
|
|
7379
7668
|
///////////////////////////////////////////////////////////////////////////////
|
|
7380
7669
|
|
|
7381
7670
|
/** Global UI system plugin object
|
|
7382
|
-
* @type {UISystemPlugin}
|
|
7671
|
+
* @type {UISystemPlugin}
|
|
7672
|
+
* @memberof UISystem */
|
|
7383
7673
|
let uiSystem;
|
|
7384
7674
|
|
|
7385
7675
|
///////////////////////////////////////////////////////////////////////////////
|
|
7386
7676
|
/**
|
|
7387
7677
|
* UI System Global Object
|
|
7678
|
+
* @memberof UISystem
|
|
7388
7679
|
*/
|
|
7389
7680
|
class UISystemPlugin
|
|
7390
7681
|
{
|
|
@@ -7411,6 +7702,8 @@ class UISystemPlugin
|
|
|
7411
7702
|
this.defaultHoverColor = hsl(0,0,.9);
|
|
7412
7703
|
/** @property {Color} - Default color for disabled UI elements */
|
|
7413
7704
|
this.defaultDisabledColor = hsl(0,0,.3);
|
|
7705
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
7706
|
+
this.defaultGradientColor = undefined;
|
|
7414
7707
|
/** @property {number} - Default line width for UI elements */
|
|
7415
7708
|
this.defaultLineWidth = 4;
|
|
7416
7709
|
/** @property {number} - Default rounded rect corner radius for UI elements */
|
|
@@ -7418,7 +7711,7 @@ class UISystemPlugin
|
|
|
7418
7711
|
/** @property {number} - Default scale to use for fitting text to object */
|
|
7419
7712
|
this.defaultTextScale = .8;
|
|
7420
7713
|
/** @property {string} - Default font for UI elements */
|
|
7421
|
-
this.defaultFont =
|
|
7714
|
+
this.defaultFont = fontDefault;
|
|
7422
7715
|
/** @property {Sound} - Default sound when interactive UI element is pressed */
|
|
7423
7716
|
this.defaultSoundPress = undefined;
|
|
7424
7717
|
/** @property {Sound} - Default sound when interactive UI element is released */
|
|
@@ -7429,11 +7722,15 @@ class UISystemPlugin
|
|
|
7429
7722
|
this.uiObjects = [];
|
|
7430
7723
|
/** @property {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} - Context to render UI elements to */
|
|
7431
7724
|
this.uiContext = context;
|
|
7432
|
-
/** @property {UIObject} - Top most object user is over */
|
|
7433
|
-
this.hoverObject = undefined;
|
|
7434
7725
|
/** @property {UIObject} - Object user is currently interacting with */
|
|
7435
7726
|
this.activeObject = undefined;
|
|
7436
|
-
|
|
7727
|
+
/** @property {UIObject} - Top most object user is over */
|
|
7728
|
+
this.hoverObject = undefined;
|
|
7729
|
+
/** @property {UIObject} - Hover object at start of update */
|
|
7730
|
+
this.lastHoverObject = undefined;
|
|
7731
|
+
/** @property {number} - If set ui coords will be renormalized to this canvas height */
|
|
7732
|
+
this.nativeHeight = 0;
|
|
7733
|
+
|
|
7437
7734
|
engineAddPlugin(uiUpdate, uiRender);
|
|
7438
7735
|
|
|
7439
7736
|
// setup recursive update and render
|
|
@@ -7463,6 +7760,7 @@ class UISystemPlugin
|
|
|
7463
7760
|
updateInvisibleObject(o);
|
|
7464
7761
|
}
|
|
7465
7762
|
// reset hover object at start of update
|
|
7763
|
+
uiSystem.lastHoverObject = uiSystem.hoverObject;
|
|
7466
7764
|
uiSystem.hoverObject = undefined;
|
|
7467
7765
|
for (let i = uiSystem.uiObjects.length; i--;)
|
|
7468
7766
|
{
|
|
@@ -7472,6 +7770,17 @@ class UISystemPlugin
|
|
|
7472
7770
|
}
|
|
7473
7771
|
function uiRender()
|
|
7474
7772
|
{
|
|
7773
|
+
const context = uiSystem.uiContext;
|
|
7774
|
+
context.save();
|
|
7775
|
+
if (uiSystem.nativeHeight)
|
|
7776
|
+
{
|
|
7777
|
+
// convert to native height
|
|
7778
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
7779
|
+
context.translate(-s*mainCanvasSize.x/2,0);
|
|
7780
|
+
context.scale(s,s);
|
|
7781
|
+
context.translate(mainCanvasSize.x/2/s,0);
|
|
7782
|
+
}
|
|
7783
|
+
|
|
7475
7784
|
function renderObject(o)
|
|
7476
7785
|
{
|
|
7477
7786
|
if (!o.visible)
|
|
@@ -7483,6 +7792,7 @@ class UISystemPlugin
|
|
|
7483
7792
|
renderObject(c);
|
|
7484
7793
|
}
|
|
7485
7794
|
uiSystem.uiObjects.forEach(o=> o.parent || renderObject(o));
|
|
7795
|
+
context.restore();
|
|
7486
7796
|
}
|
|
7487
7797
|
}
|
|
7488
7798
|
|
|
@@ -7492,11 +7802,30 @@ class UISystemPlugin
|
|
|
7492
7802
|
* @param {Color} [color=uiSystem.defaultColor]
|
|
7493
7803
|
* @param {number} [lineWidth=uiSystem.defaultLineWidth]
|
|
7494
7804
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
7495
|
-
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
7496
|
-
|
|
7497
|
-
|
|
7805
|
+
* @param {number} [cornerRadius=uiSystem.defaultCornerRadius]
|
|
7806
|
+
* @param {Color} [gradientColor=uiSystem.defaultGradientColor] */
|
|
7807
|
+
drawRect(pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, cornerRadius=uiSystem.defaultCornerRadius, gradientColor=uiSystem.defaultGradientColor)
|
|
7808
|
+
{
|
|
7809
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
7810
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
7811
|
+
ASSERT(isColor(color), 'color must be a color');
|
|
7812
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
7813
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
7814
|
+
ASSERT(isNumber(cornerRadius), 'cornerRadius must be a number');
|
|
7815
|
+
|
|
7498
7816
|
const context = uiSystem.uiContext;
|
|
7499
|
-
|
|
7817
|
+
if (gradientColor)
|
|
7818
|
+
{
|
|
7819
|
+
const g = context.createLinearGradient(
|
|
7820
|
+
pos.x, pos.y-size.y/2, pos.x, pos.y+size.y/2);
|
|
7821
|
+
const c = color.toString();
|
|
7822
|
+
g.addColorStop(0, c);
|
|
7823
|
+
g.addColorStop(.5, gradientColor.toString());
|
|
7824
|
+
g.addColorStop(1, c);
|
|
7825
|
+
context.fillStyle = g;
|
|
7826
|
+
}
|
|
7827
|
+
else
|
|
7828
|
+
context.fillStyle = color.toString();
|
|
7500
7829
|
context.beginPath();
|
|
7501
7830
|
if (cornerRadius && context['roundRect'])
|
|
7502
7831
|
context['roundRect'](pos.x-size.x/2, pos.y-size.y/2, size.x, size.y, cornerRadius);
|
|
@@ -7518,6 +7847,11 @@ class UISystemPlugin
|
|
|
7518
7847
|
* @param {Color} [lineColor=uiSystem.defaultLineColor] */
|
|
7519
7848
|
drawLine(posA, posB, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor)
|
|
7520
7849
|
{
|
|
7850
|
+
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
7851
|
+
ASSERT(isVector2(posB), 'posB must be a vec2');
|
|
7852
|
+
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
7853
|
+
ASSERT(isColor(lineColor), 'lineColor must be a color');
|
|
7854
|
+
|
|
7521
7855
|
const context = uiSystem.uiContext;
|
|
7522
7856
|
context.strokeStyle = lineColor.toString();
|
|
7523
7857
|
context.lineWidth = lineWidth;
|
|
@@ -7548,17 +7882,43 @@ class UISystemPlugin
|
|
|
7548
7882
|
* @param {Color} [lineColor=uiSystem.defaultLineColor]
|
|
7549
7883
|
* @param {string} [align]
|
|
7550
7884
|
* @param {string} [font=uiSystem.defaultFont]
|
|
7885
|
+
* @param {string} [fontStyle]
|
|
7551
7886
|
* @param {boolean} [applyMaxWidth=true] */
|
|
7552
|
-
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, applyMaxWidth=true)
|
|
7887
|
+
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, fontStyle='', applyMaxWidth=true)
|
|
7888
|
+
{
|
|
7889
|
+
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, fontStyle, applyMaxWidth ? size.x : undefined, uiSystem.uiContext);
|
|
7890
|
+
}
|
|
7891
|
+
|
|
7892
|
+
/**
|
|
7893
|
+
* @callback DragAndDropCallback - Callback for drag and drop events
|
|
7894
|
+
* @param {DragEvent} event - The drag event
|
|
7895
|
+
* @memberof UISystem
|
|
7896
|
+
*/
|
|
7897
|
+
|
|
7898
|
+
/** Setup drag and drop event handlers
|
|
7899
|
+
* Automatically prevents defaults and calls the given functions
|
|
7900
|
+
* @param {DragAndDropCallback} [onDrop] - when a file is dropped
|
|
7901
|
+
* @param {DragAndDropCallback} [onDragEnter] - when a file is dragged onto the window
|
|
7902
|
+
* @param {DragAndDropCallback} [onDragLeave] - when a file is dragged off the window
|
|
7903
|
+
* @param {DragAndDropCallback} [onDragOver] - continously when dragging over */
|
|
7904
|
+
setupDragAndDrop(onDrop, onDragEnter, onDragLeave, onDragOver)
|
|
7553
7905
|
{
|
|
7554
|
-
|
|
7906
|
+
function setCallback(callback, listenerType)
|
|
7907
|
+
{
|
|
7908
|
+
function listener(e) { e.preventDefault(); callback && callback(e); }
|
|
7909
|
+
document.addEventListener(listenerType, listener);
|
|
7910
|
+
}
|
|
7911
|
+
setCallback(onDrop, 'drop');
|
|
7912
|
+
setCallback(onDragEnter, 'dragenter');
|
|
7913
|
+
setCallback(onDragLeave, 'dragleave');
|
|
7914
|
+
setCallback(onDragOver, 'dragover');
|
|
7555
7915
|
}
|
|
7556
7916
|
}
|
|
7557
7917
|
|
|
7558
7918
|
///////////////////////////////////////////////////////////////////////////////
|
|
7559
7919
|
/**
|
|
7560
7920
|
* UI Object - Base level object for all UI elements
|
|
7561
|
-
*/
|
|
7921
|
+
* @memberof UISystem */
|
|
7562
7922
|
class UIObject
|
|
7563
7923
|
{
|
|
7564
7924
|
/** Create a UIObject
|
|
@@ -7567,6 +7927,9 @@ class UIObject
|
|
|
7567
7927
|
*/
|
|
7568
7928
|
constructor(pos=vec2(), size=vec2())
|
|
7569
7929
|
{
|
|
7930
|
+
ASSERT(isVector2(pos), 'ui object pos must be a vec2');
|
|
7931
|
+
ASSERT(isVector2(size), 'ui object size must be a vec2');
|
|
7932
|
+
|
|
7570
7933
|
/** @property {Vector2} - Local position of the object */
|
|
7571
7934
|
this.localPos = pos.copy();
|
|
7572
7935
|
/** @property {Vector2} - Screen space position of the object */
|
|
@@ -7574,27 +7937,31 @@ class UIObject
|
|
|
7574
7937
|
/** @property {Vector2} - Screen space size of the object */
|
|
7575
7938
|
this.size = size.copy();
|
|
7576
7939
|
/** @property {Color} - Color of the object */
|
|
7577
|
-
this.color = uiSystem.defaultColor;
|
|
7940
|
+
this.color = uiSystem.defaultColor.copy();
|
|
7578
7941
|
/** @property {Color} - Color of the object when active, uses color if undefined */
|
|
7579
7942
|
this.activeColor = undefined;
|
|
7580
7943
|
/** @property {string} - Text for this ui object */
|
|
7581
7944
|
this.text = undefined;
|
|
7582
7945
|
/** @property {Color} - Color when disabled */
|
|
7583
|
-
this.disabledColor = uiSystem.defaultDisabledColor;
|
|
7946
|
+
this.disabledColor = uiSystem.defaultDisabledColor.copy();
|
|
7584
7947
|
/** @property {boolean} - Is this object disabled? */
|
|
7585
7948
|
this.disabled = false;
|
|
7586
7949
|
/** @property {Color} - Color for text */
|
|
7587
|
-
this.textColor = uiSystem.defaultTextColor;
|
|
7950
|
+
this.textColor = uiSystem.defaultTextColor.copy();
|
|
7588
7951
|
/** @property {Color} - Color used when hovering over the object */
|
|
7589
|
-
this.hoverColor = uiSystem.defaultHoverColor;
|
|
7952
|
+
this.hoverColor = uiSystem.defaultHoverColor.copy();
|
|
7590
7953
|
/** @property {Color} - Color for line drawing */
|
|
7591
|
-
this.lineColor = uiSystem.defaultLineColor;
|
|
7954
|
+
this.lineColor = uiSystem.defaultLineColor.copy();
|
|
7955
|
+
/** @property {Color} - Uses a gradient fill combined with color */
|
|
7956
|
+
this.gradientColor = uiSystem.defaultGradientColor ? uiSystem.defaultGradientColor.copy() : undefined;
|
|
7592
7957
|
/** @property {number} - Width for line drawing */
|
|
7593
7958
|
this.lineWidth = uiSystem.defaultLineWidth;
|
|
7594
7959
|
/** @property {number} - Corner radius for rounded rects */
|
|
7595
7960
|
this.cornerRadius = uiSystem.defaultCornerRadius;
|
|
7596
7961
|
/** @property {string} - Font for this objecct */
|
|
7597
7962
|
this.font = uiSystem.defaultFont;
|
|
7963
|
+
/** @property {string} - Font style for this object or undefined */
|
|
7964
|
+
this.fontStyle = undefined;
|
|
7598
7965
|
/** @property {number} - Override for text width */
|
|
7599
7966
|
this.textWidth = undefined;
|
|
7600
7967
|
/** @property {number} - Override for text height */
|
|
@@ -7619,6 +7986,8 @@ class UIObject
|
|
|
7619
7986
|
this.interactive = false;
|
|
7620
7987
|
/** @property {boolean} - Activate when dragged over with mouse held down */
|
|
7621
7988
|
this.dragActivate = false;
|
|
7989
|
+
/** @property {boolean} - True if this can be a hover object */
|
|
7990
|
+
this.canBeHover = true;
|
|
7622
7991
|
uiSystem.uiObjects.push(this);
|
|
7623
7992
|
}
|
|
7624
7993
|
|
|
@@ -7642,31 +8011,47 @@ class UIObject
|
|
|
7642
8011
|
child.parent = undefined;
|
|
7643
8012
|
}
|
|
7644
8013
|
|
|
8014
|
+
/** Check if the mouse is overlapping a box in screen space
|
|
8015
|
+
* @return {boolean} - True if overlapping
|
|
8016
|
+
*/
|
|
8017
|
+
isMouseOverlapping()
|
|
8018
|
+
{
|
|
8019
|
+
const size = !isTouchDevice ? this.size :
|
|
8020
|
+
this.size.add(vec2(this.extraTouchSize || 0));
|
|
8021
|
+
if (!uiSystem.nativeHeight)
|
|
8022
|
+
return isOverlapping(this.pos, size, mousePosScreen);
|
|
8023
|
+
|
|
8024
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8025
|
+
const sInv = 1/s;
|
|
8026
|
+
let pos = mousePosScreen.copy();
|
|
8027
|
+
pos.x += s*mainCanvasSize.x/2;
|
|
8028
|
+
pos.x *= sInv;
|
|
8029
|
+
pos.y *= sInv;
|
|
8030
|
+
pos.x -= sInv*mainCanvasSize.x/2;
|
|
8031
|
+
return isOverlapping(this.pos, size, pos);
|
|
8032
|
+
}
|
|
8033
|
+
|
|
7645
8034
|
/** Update the object, called automatically by plugin once each frame */
|
|
7646
8035
|
update()
|
|
7647
8036
|
{
|
|
7648
|
-
const wasHover = this
|
|
8037
|
+
const wasHover = uiSystem.lastHoverObject === this;
|
|
7649
8038
|
const isActive = this.isActiveObject();
|
|
7650
8039
|
const mouseDown = mouseIsDown(0);
|
|
7651
8040
|
const mousePress = this.dragActivate ? mouseDown : mouseWasPressed(0);
|
|
7652
|
-
if (
|
|
8041
|
+
if (this.canBeHover)
|
|
7653
8042
|
if (mousePress || isActive || (!mouseDown && !isTouchDevice))
|
|
7654
|
-
|
|
7655
|
-
|
|
7656
|
-
if (isOverlapping(this.pos, size, mousePosScreen))
|
|
7657
|
-
uiSystem.hoverObject = this;
|
|
7658
|
-
}
|
|
8043
|
+
if (!uiSystem.hoverObject && this.isMouseOverlapping())
|
|
8044
|
+
uiSystem.hoverObject = this;
|
|
7659
8045
|
if (this.isHoverObject())
|
|
7660
8046
|
{
|
|
7661
|
-
if (mousePress)
|
|
7662
|
-
inputClearKey(0,0,0,1,0); // clear mouse was pressed state
|
|
7663
8047
|
if (!this.disabled)
|
|
7664
8048
|
{
|
|
7665
8049
|
if (mousePress)
|
|
7666
8050
|
{
|
|
7667
8051
|
if (this.interactive)
|
|
7668
8052
|
{
|
|
7669
|
-
this.
|
|
8053
|
+
if (!this.dragActivate || (!wasHover || mouseWasPressed(0)))
|
|
8054
|
+
this.onPress();
|
|
7670
8055
|
if (this.soundPress)
|
|
7671
8056
|
this.soundPress.play();
|
|
7672
8057
|
if (uiSystem.activeObject && !isActive)
|
|
@@ -7674,13 +8059,15 @@ class UIObject
|
|
|
7674
8059
|
uiSystem.activeObject = this;
|
|
7675
8060
|
}
|
|
7676
8061
|
}
|
|
7677
|
-
if (!mouseDown &&
|
|
8062
|
+
if (!mouseDown && this.isActiveObject() && this.interactive)
|
|
7678
8063
|
{
|
|
7679
8064
|
this.onClick();
|
|
7680
8065
|
if (this.soundClick)
|
|
7681
8066
|
this.soundClick.play();
|
|
7682
8067
|
}
|
|
7683
8068
|
}
|
|
8069
|
+
// clear mouse was pressed state even when disabled
|
|
8070
|
+
mousePress && inputClearKey(0,0,0,1,0);
|
|
7684
8071
|
}
|
|
7685
8072
|
if (isActive)
|
|
7686
8073
|
if (!mouseDown || (this.dragActivate && !this.isHoverObject()))
|
|
@@ -7691,6 +8078,7 @@ class UIObject
|
|
|
7691
8078
|
uiSystem.activeObject = undefined;
|
|
7692
8079
|
}
|
|
7693
8080
|
|
|
8081
|
+
// call enter/leave events
|
|
7694
8082
|
if (this.isHoverObject() !== wasHover)
|
|
7695
8083
|
this.isHoverObject() ? this.onEnter() : this.onLeave();
|
|
7696
8084
|
}
|
|
@@ -7701,7 +8089,7 @@ class UIObject
|
|
|
7701
8089
|
if (!this.size.x || !this.size.y) return;
|
|
7702
8090
|
|
|
7703
8091
|
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
7704
|
-
const color = this.
|
|
8092
|
+
const color = this.disabled ? this.disabledColor : this.interactive ? this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
7705
8093
|
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
7706
8094
|
}
|
|
7707
8095
|
|
|
@@ -7752,6 +8140,7 @@ class UIObject
|
|
|
7752
8140
|
/**
|
|
7753
8141
|
* UIText - A UI object that displays text
|
|
7754
8142
|
* @extends UIObject
|
|
8143
|
+
* @memberof UISystem
|
|
7755
8144
|
*/
|
|
7756
8145
|
class UIText extends UIObject
|
|
7757
8146
|
{
|
|
@@ -7766,6 +8155,10 @@ class UIText extends UIObject
|
|
|
7766
8155
|
{
|
|
7767
8156
|
super(pos, size);
|
|
7768
8157
|
|
|
8158
|
+
ASSERT(isString(text), 'ui text must be a string');
|
|
8159
|
+
ASSERT(['left','center','right'].includes(align), 'ui text align must be left, center, or right');
|
|
8160
|
+
ASSERT(isString(font), 'ui text font must be a string');
|
|
8161
|
+
|
|
7769
8162
|
// set properties
|
|
7770
8163
|
this.text = text;
|
|
7771
8164
|
this.align = align;
|
|
@@ -7773,11 +8166,13 @@ class UIText extends UIObject
|
|
|
7773
8166
|
|
|
7774
8167
|
// make text not outlined by default
|
|
7775
8168
|
this.lineWidth = 0;
|
|
8169
|
+
// text can not be a hover object by default
|
|
8170
|
+
this.canBeHover = false;
|
|
7776
8171
|
}
|
|
7777
8172
|
render()
|
|
7778
8173
|
{
|
|
7779
8174
|
const textSize = this.getTextSize();
|
|
7780
|
-
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font);
|
|
8175
|
+
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font, this.fontStyle);
|
|
7781
8176
|
}
|
|
7782
8177
|
}
|
|
7783
8178
|
|
|
@@ -7785,6 +8180,7 @@ class UIText extends UIObject
|
|
|
7785
8180
|
/**
|
|
7786
8181
|
* UITile - A UI object that displays a tile image
|
|
7787
8182
|
* @extends UIObject
|
|
8183
|
+
* @memberof UISystem
|
|
7788
8184
|
*/
|
|
7789
8185
|
class UITile extends UIObject
|
|
7790
8186
|
{
|
|
@@ -7799,15 +8195,19 @@ class UITile extends UIObject
|
|
|
7799
8195
|
constructor(pos, size, tileInfo, color=WHITE, angle=0, mirror=false)
|
|
7800
8196
|
{
|
|
7801
8197
|
super(pos, size);
|
|
8198
|
+
|
|
8199
|
+
ASSERT(tileInfo instanceof TileInfo, 'ui tile tileInfo must be a TileInfo');
|
|
8200
|
+
ASSERT(isColor(color), 'ui tile color must be a color');
|
|
8201
|
+
ASSERT(isNumber(angle), 'ui tile angle must be a number');
|
|
8202
|
+
|
|
7802
8203
|
/** @property {TileInfo} - Tile image to use */
|
|
7803
8204
|
this.tileInfo = tileInfo;
|
|
7804
8205
|
/** @property {number} - Angle to rotate in radians */
|
|
7805
8206
|
this.angle = angle;
|
|
7806
8207
|
/** @property {boolean} - Should it be mirrored? */
|
|
7807
8208
|
this.mirror = mirror;
|
|
7808
|
-
|
|
7809
8209
|
// set properties
|
|
7810
|
-
this.color = color;
|
|
8210
|
+
this.color = color.copy();
|
|
7811
8211
|
}
|
|
7812
8212
|
render()
|
|
7813
8213
|
{
|
|
@@ -7819,6 +8219,7 @@ class UITile extends UIObject
|
|
|
7819
8219
|
/**
|
|
7820
8220
|
* UIButton - A UI object that acts as a button
|
|
7821
8221
|
* @extends UIObject
|
|
8222
|
+
* @memberof UISystem
|
|
7822
8223
|
*/
|
|
7823
8224
|
class UIButton extends UIObject
|
|
7824
8225
|
{
|
|
@@ -7832,9 +8233,12 @@ class UIButton extends UIObject
|
|
|
7832
8233
|
{
|
|
7833
8234
|
super(pos, size);
|
|
7834
8235
|
|
|
8236
|
+
ASSERT(isString(text), 'ui button must be a string');
|
|
8237
|
+
ASSERT(isColor(color), 'ui button color must be a color');
|
|
8238
|
+
|
|
7835
8239
|
// set properties
|
|
7836
8240
|
this.text = text;
|
|
7837
|
-
this.color = color;
|
|
8241
|
+
this.color = color.copy();
|
|
7838
8242
|
this.interactive = true;
|
|
7839
8243
|
}
|
|
7840
8244
|
render()
|
|
@@ -7844,7 +8248,7 @@ class UIButton extends UIObject
|
|
|
7844
8248
|
// draw the text scaled to fit
|
|
7845
8249
|
const textSize = this.getTextSize();
|
|
7846
8250
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
7847
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8251
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
7848
8252
|
}
|
|
7849
8253
|
}
|
|
7850
8254
|
|
|
@@ -7852,6 +8256,7 @@ class UIButton extends UIObject
|
|
|
7852
8256
|
/**
|
|
7853
8257
|
* UICheckbox - A UI object that acts as a checkbox
|
|
7854
8258
|
* @extends UIObject
|
|
8259
|
+
* @memberof UISystem
|
|
7855
8260
|
*/
|
|
7856
8261
|
class UICheckbox extends UIObject
|
|
7857
8262
|
{
|
|
@@ -7865,12 +8270,15 @@ class UICheckbox extends UIObject
|
|
|
7865
8270
|
constructor(pos, size, checked=false, text='', color=uiSystem.defaultButtonColor)
|
|
7866
8271
|
{
|
|
7867
8272
|
super(pos, size);
|
|
8273
|
+
|
|
8274
|
+
ASSERT(isString(text), 'ui checkbox must be a string');
|
|
8275
|
+
ASSERT(isColor(color), 'ui checkbox color must be a color');
|
|
8276
|
+
|
|
7868
8277
|
/** @property {boolean} - Current percentage value of this scrollbar 0-1 */
|
|
7869
8278
|
this.checked = checked;
|
|
7870
|
-
|
|
7871
8279
|
// set properties
|
|
7872
8280
|
this.text = text;
|
|
7873
|
-
this.color = color;
|
|
8281
|
+
this.color = color.copy();
|
|
7874
8282
|
this.interactive = true;
|
|
7875
8283
|
}
|
|
7876
8284
|
onClick()
|
|
@@ -7894,7 +8302,7 @@ class UICheckbox extends UIObject
|
|
|
7894
8302
|
const textSize = this.getTextSize();
|
|
7895
8303
|
const pos = this.pos.add(vec2(this.size.x,0));
|
|
7896
8304
|
uiSystem.drawText(this.text, pos, textSize,
|
|
7897
|
-
this.textColor, 0, undefined, 'left', this.font, false);
|
|
8305
|
+
this.textColor, 0, undefined, 'left', this.font, this.fontStyle, false);
|
|
7898
8306
|
}
|
|
7899
8307
|
}
|
|
7900
8308
|
|
|
@@ -7902,6 +8310,7 @@ class UICheckbox extends UIObject
|
|
|
7902
8310
|
/**
|
|
7903
8311
|
* UIScrollbar - A UI object that acts as a scrollbar
|
|
7904
8312
|
* @extends UIObject
|
|
8313
|
+
* @memberof UISystem
|
|
7905
8314
|
*/
|
|
7906
8315
|
class UIScrollbar extends UIObject
|
|
7907
8316
|
{
|
|
@@ -7917,14 +8326,19 @@ class UIScrollbar extends UIObject
|
|
|
7917
8326
|
{
|
|
7918
8327
|
super(pos, size);
|
|
7919
8328
|
|
|
8329
|
+
ASSERT(isNumber(value), 'ui scrollbar value must be a number');
|
|
8330
|
+
ASSERT(isString(text), 'ui scrollbar must be a string');
|
|
8331
|
+
ASSERT(isColor(color), 'ui scrollbar color must be a color');
|
|
8332
|
+
ASSERT(isColor(handleColor), 'ui scrollbar handleColor must be a color');
|
|
8333
|
+
|
|
7920
8334
|
/** @property {number} - Current percentage value of this scrollbar 0-1 */
|
|
7921
8335
|
this.value = value;
|
|
7922
8336
|
/** @property {Color} - Color for the handle part of the scrollbar */
|
|
7923
|
-
this.handleColor = handleColor;
|
|
8337
|
+
this.handleColor = handleColor.copy();
|
|
7924
8338
|
|
|
7925
8339
|
// set properties
|
|
7926
8340
|
this.text = text;
|
|
7927
|
-
this.color = color;
|
|
8341
|
+
this.color = color.copy();
|
|
7928
8342
|
this.interactive = true;
|
|
7929
8343
|
}
|
|
7930
8344
|
update()
|
|
@@ -7932,34 +8346,47 @@ class UIScrollbar extends UIObject
|
|
|
7932
8346
|
super.update();
|
|
7933
8347
|
if (this.isActiveObject() && this.interactive)
|
|
7934
8348
|
{
|
|
8349
|
+
// handle horizontal or vertical scrollbar
|
|
8350
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8351
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8352
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8353
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8354
|
+
|
|
7935
8355
|
// check if value changed
|
|
7936
|
-
const
|
|
7937
|
-
const
|
|
7938
|
-
const
|
|
7939
|
-
const p2 = this.pos.x + handleWidth/2;
|
|
8356
|
+
const handleWidth = barSize - handleSize;
|
|
8357
|
+
const p1 = centerPos - handleWidth/2;
|
|
8358
|
+
const p2 = centerPos + handleWidth/2;
|
|
7940
8359
|
const oldValue = this.value;
|
|
7941
|
-
this.value =
|
|
8360
|
+
this.value = isHorizontal ?
|
|
8361
|
+
percent(mousePosScreen.x, p1, p2) :
|
|
8362
|
+
percent(mousePosScreen.y, p2, p1);
|
|
7942
8363
|
this.value === oldValue || this.onChange();
|
|
7943
8364
|
}
|
|
7944
8365
|
}
|
|
7945
8366
|
render()
|
|
7946
8367
|
{
|
|
7947
8368
|
super.render();
|
|
7948
|
-
|
|
8369
|
+
|
|
8370
|
+
// handle horizontal or vertical scrollbar
|
|
8371
|
+
const isHorizontal = this.size.x > this.size.y;
|
|
8372
|
+
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
8373
|
+
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
8374
|
+
const centerPos = isHorizontal ? this.pos.x : this.pos.y;
|
|
8375
|
+
|
|
7949
8376
|
// draw the scrollbar handle
|
|
7950
|
-
const
|
|
7951
|
-
const
|
|
7952
|
-
const
|
|
7953
|
-
const
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
uiSystem.drawRect(handlePos, handleSize, handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
8377
|
+
const handleWidth = barSize - handleSize;
|
|
8378
|
+
const p1 = centerPos - handleWidth/2;
|
|
8379
|
+
const p2 = centerPos + handleWidth/2;
|
|
8380
|
+
const handlePos = isHorizontal ?
|
|
8381
|
+
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
8382
|
+
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
8383
|
+
const handleColor = this.disabled ? this.disabledColor : this.handleColor;
|
|
8384
|
+
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
7958
8385
|
|
|
7959
8386
|
// draw the text scaled to fit on the scrollbar
|
|
7960
8387
|
const textSize = this.getTextSize();
|
|
7961
8388
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
7962
|
-
this.textColor, 0, undefined, this.align, this.font);
|
|
8389
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
7963
8390
|
}
|
|
7964
8391
|
}
|
|
7965
8392
|
/**
|
|
@@ -8001,6 +8428,7 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
|
|
|
8001
8428
|
* - Each object has a Box2D body which can have multiple fixtures and joints
|
|
8002
8429
|
* - Provides interface for Box2D body and fixture functions
|
|
8003
8430
|
* @extends EngineObject
|
|
8431
|
+
* @memberof Box2D
|
|
8004
8432
|
*/
|
|
8005
8433
|
class Box2dObject extends EngineObject
|
|
8006
8434
|
{
|
|
@@ -8012,7 +8440,7 @@ class Box2dObject extends EngineObject
|
|
|
8012
8440
|
* @param {Color} [color]
|
|
8013
8441
|
* @param {number} [bodyType]
|
|
8014
8442
|
* @param {number} [renderOrder] */
|
|
8015
|
-
constructor(pos
|
|
8443
|
+
constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
8016
8444
|
{
|
|
8017
8445
|
super(pos, size, tileInfo, angle, color, renderOrder);
|
|
8018
8446
|
|
|
@@ -8026,7 +8454,7 @@ class Box2dObject extends EngineObject
|
|
|
8026
8454
|
this.lineColor = BLACK;
|
|
8027
8455
|
}
|
|
8028
8456
|
|
|
8029
|
-
/** Destroy this object and
|
|
8457
|
+
/** Destroy this object and its physics body */
|
|
8030
8458
|
destroy()
|
|
8031
8459
|
{
|
|
8032
8460
|
// destroy physics body, fixtures, and joints
|
|
@@ -8510,6 +8938,7 @@ class Box2dRaycastResult
|
|
|
8510
8938
|
* Box2D Joint
|
|
8511
8939
|
* - Base class for Box2D joints
|
|
8512
8940
|
* - A joint is used to connect objects together
|
|
8941
|
+
* @memberof Box2D
|
|
8513
8942
|
*/
|
|
8514
8943
|
class Box2dJoint
|
|
8515
8944
|
{
|
|
@@ -8565,6 +8994,7 @@ class Box2dJoint
|
|
|
8565
8994
|
* - This a soft constraint with a max force
|
|
8566
8995
|
* - This allows the constraint to stretch and without applying huge forces
|
|
8567
8996
|
* @extends Box2dJoint
|
|
8997
|
+
* @memberof Box2D
|
|
8568
8998
|
*/
|
|
8569
8999
|
class Box2dTargetJoint extends Box2dJoint
|
|
8570
9000
|
{
|
|
@@ -8614,6 +9044,7 @@ class Box2dTargetJoint extends Box2dJoint
|
|
|
8614
9044
|
* - Constrains two points on two objects to remain at a fixed distance
|
|
8615
9045
|
* - You can view this as a massless, rigid rod
|
|
8616
9046
|
* @extends Box2dJoint
|
|
9047
|
+
* @memberof Box2D
|
|
8617
9048
|
*/
|
|
8618
9049
|
class Box2dDistanceJoint extends Box2dJoint
|
|
8619
9050
|
{
|
|
@@ -8677,6 +9108,7 @@ class Box2dDistanceJoint extends Box2dJoint
|
|
|
8677
9108
|
* Box2D Pin Joint
|
|
8678
9109
|
* - Pins two objects together at a point
|
|
8679
9110
|
* @extends Box2dDistanceJoint
|
|
9111
|
+
* @memberof Box2D
|
|
8680
9112
|
*/
|
|
8681
9113
|
class Box2dPinJoint extends Box2dDistanceJoint
|
|
8682
9114
|
{
|
|
@@ -8696,6 +9128,7 @@ class Box2dPinJoint extends Box2dDistanceJoint
|
|
|
8696
9128
|
* Box2D Rope Joint
|
|
8697
9129
|
* - Enforces a maximum distance between two points on two objects
|
|
8698
9130
|
* @extends Box2dJoint
|
|
9131
|
+
* @memberof Box2D
|
|
8699
9132
|
*/
|
|
8700
9133
|
class Box2dRopeJoint extends Box2dJoint
|
|
8701
9134
|
{
|
|
@@ -8748,6 +9181,7 @@ class Box2dRopeJoint extends Box2dJoint
|
|
|
8748
9181
|
* - You can use a motor to drive the relative rotation about the shared point
|
|
8749
9182
|
* - A maximum motor torque is provided so that infinite forces are not generated
|
|
8750
9183
|
* @extends Box2dJoint
|
|
9184
|
+
* @memberof Box2D
|
|
8751
9185
|
*/
|
|
8752
9186
|
class Box2dRevoluteJoint extends Box2dJoint
|
|
8753
9187
|
{
|
|
@@ -8849,6 +9283,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
8849
9283
|
* - Either joint can be a revolute or prismatic joint
|
|
8850
9284
|
* - You specify a gear ratio to bind the motions together
|
|
8851
9285
|
* @extends Box2dJoint
|
|
9286
|
+
* @memberof Box2D
|
|
8852
9287
|
*/
|
|
8853
9288
|
class Box2dGearJoint extends Box2dJoint
|
|
8854
9289
|
{
|
|
@@ -8897,6 +9332,7 @@ class Box2dGearJoint extends Box2dJoint
|
|
|
8897
9332
|
* - You can use a joint limit to restrict the range of motion
|
|
8898
9333
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
8899
9334
|
* @extends Box2dJoint
|
|
9335
|
+
* @memberof Box2D
|
|
8900
9336
|
*/
|
|
8901
9337
|
class Box2dPrismaticJoint extends Box2dJoint
|
|
8902
9338
|
{
|
|
@@ -9006,6 +9442,7 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
9006
9442
|
* - You can use a joint motor to drive the motion or to model joint friction
|
|
9007
9443
|
* - This joint is designed for vehicle suspensions
|
|
9008
9444
|
* @extends Box2dJoint
|
|
9445
|
+
* @memberof Box2D
|
|
9009
9446
|
*/
|
|
9010
9447
|
class Box2dWheelJoint extends Box2dJoint
|
|
9011
9448
|
{
|
|
@@ -9101,6 +9538,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
9101
9538
|
* Box2D Weld Joint
|
|
9102
9539
|
* - Glues two objects together
|
|
9103
9540
|
* @extends Box2dJoint
|
|
9541
|
+
* @memberof Box2D
|
|
9104
9542
|
*/
|
|
9105
9543
|
class Box2dWeldJoint extends Box2dJoint
|
|
9106
9544
|
{
|
|
@@ -9159,6 +9597,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
9159
9597
|
* - Used to apply top-down friction
|
|
9160
9598
|
* - Provides 2D translational friction and angular friction
|
|
9161
9599
|
* @extends Box2dJoint
|
|
9600
|
+
* @memberof Box2D
|
|
9162
9601
|
*/
|
|
9163
9602
|
class Box2dFrictionJoint extends Box2dJoint
|
|
9164
9603
|
{
|
|
@@ -9213,6 +9652,7 @@ class Box2dFrictionJoint extends Box2dJoint
|
|
|
9213
9652
|
* - The pulley supports a ratio such that: length1 + ratio * length2 <= constant
|
|
9214
9653
|
* - The force transmitted is scaled by the ratio
|
|
9215
9654
|
* @extends Box2dJoint
|
|
9655
|
+
* @memberof Box2D
|
|
9216
9656
|
*/
|
|
9217
9657
|
class Box2dPulleyJoint extends Box2dJoint
|
|
9218
9658
|
{
|
|
@@ -9280,6 +9720,7 @@ class Box2dPulleyJoint extends Box2dJoint
|
|
|
9280
9720
|
* - Controls the relative motion between two objects
|
|
9281
9721
|
* - Typical usage is to control the movement of a object with respect to the ground
|
|
9282
9722
|
* @extends Box2dJoint
|
|
9723
|
+
* @memberof Box2D
|
|
9283
9724
|
*/
|
|
9284
9725
|
class Box2dMotorJoint extends Box2dJoint
|
|
9285
9726
|
{
|
|
@@ -9343,6 +9784,7 @@ class Box2dMotorJoint extends Box2dJoint
|
|
|
9343
9784
|
/**
|
|
9344
9785
|
* Box2D Global Object
|
|
9345
9786
|
* - Wraps Box2d world and provides global functions
|
|
9787
|
+
* @memberof Box2D
|
|
9346
9788
|
*/
|
|
9347
9789
|
class Box2dPlugin
|
|
9348
9790
|
{
|