littlejsengine 1.1.26 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -9
- package/build.bat +1 -1
- package/engine/engine.all.js +369 -377
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +338 -147
- package/engine/engine.js +7 -7
- package/engine/engineAudio.js +2 -2
- package/engine/engineBuild.bat +9 -1
- package/engine/engineDebug.js +32 -231
- package/engine/engineDraw.js +130 -20
- package/engine/engineFont.png +0 -0
- package/engine/engineInput.js +32 -9
- package/engine/engineMedals.js +7 -4
- package/engine/engineObject.js +30 -11
- package/engine/engineParticles.js +5 -3
- package/engine/engineSettings.js +5 -0
- package/engine/engineTileLayer.js +17 -14
- package/engine/engineUtilities.js +50 -18
- package/engine/engineWebGL.js +53 -59
- package/engine/index.d.ts +1644 -0
- package/examples/breakout/game.js +9 -7
- package/examples/breakout/gameObjects.js +3 -3
- package/examples/breakout/index.html +3 -3
- package/examples/particles/index.html +366 -0
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +5 -5
- package/examples/platformer/gameEffects.js +10 -10
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/platformer/gamePlayer.js +11 -6
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/game.js +3 -3
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +4 -4
- package/game.js +4 -7
- package/index.html +13 -13
- package/package.json +8 -1
- package/engine/engineBuildSetup.bat +0 -6
|
@@ -82,27 +82,27 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
82
82
|
|
|
83
83
|
/** Clamps the value beween max and min
|
|
84
84
|
* @param {Number} value
|
|
85
|
-
* @param {Number} [max=1]
|
|
86
85
|
* @param {Number} [min=0]
|
|
86
|
+
* @param {Number} [max=1]
|
|
87
87
|
* @return {Number}
|
|
88
88
|
* @memberof Utilities */
|
|
89
|
-
const clamp = (v,
|
|
89
|
+
const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
90
90
|
|
|
91
91
|
/** Returns what percentage the value is between max and min
|
|
92
92
|
* @param {Number} value
|
|
93
|
-
* @param {Number} [max=1]
|
|
94
93
|
* @param {Number} [min=0]
|
|
94
|
+
* @param {Number} [max=1]
|
|
95
95
|
* @return {Number}
|
|
96
96
|
* @memberof Utilities */
|
|
97
|
-
const percent = (v,
|
|
97
|
+
const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
98
98
|
|
|
99
99
|
/** Linearly interpolates the percent value between max and min
|
|
100
100
|
* @param {Number} percent
|
|
101
|
-
* @param {Number} [max=1]
|
|
102
101
|
* @param {Number} [min=0]
|
|
102
|
+
* @param {Number} [max=1]
|
|
103
103
|
* @return {Number}
|
|
104
104
|
* @memberof Utilities */
|
|
105
|
-
const lerp = (p,
|
|
105
|
+
const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
|
|
106
106
|
|
|
107
107
|
/** Applies smoothstep function to the percentage value
|
|
108
108
|
* @param {Number} value
|
|
@@ -117,11 +117,11 @@ const smoothStep = (p)=> p * p * (3 - 2 * p);
|
|
|
117
117
|
const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
118
118
|
|
|
119
119
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
120
|
-
* @param {Vector2} pointA
|
|
121
|
-
* @param {Vector2} sizeA
|
|
122
|
-
* @param {Vector2} pointB
|
|
123
|
-
* @param {Vector2} sizeB
|
|
124
|
-
* @return {Boolean}
|
|
120
|
+
* @param {Vector2} pointA - Center of box A
|
|
121
|
+
* @param {Vector2} sizeA - Size of box A
|
|
122
|
+
* @param {Vector2} pointB - Center of box B
|
|
123
|
+
* @param {Vector2} [sizeB] - Size of box B
|
|
124
|
+
* @return {Boolean} - True if overlapping
|
|
125
125
|
* @memberof Utilities */
|
|
126
126
|
const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x & abs(pA.y - pB.y)*2 < sA.y + sB.y;
|
|
127
127
|
|
|
@@ -344,7 +344,13 @@ class Vector2
|
|
|
344
344
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
345
345
|
* @param {Vector2} arraySize
|
|
346
346
|
* @return {Boolean} */
|
|
347
|
-
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
347
|
+
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
348
|
+
|
|
349
|
+
/** Returns this vector expressed as a string
|
|
350
|
+
* @param {float} digits - precision to display
|
|
351
|
+
* @return {String} */
|
|
352
|
+
toString(digits=3)
|
|
353
|
+
{ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
|
|
348
354
|
}
|
|
349
355
|
|
|
350
356
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -415,7 +421,7 @@ class Color
|
|
|
415
421
|
* @return {Color} */
|
|
416
422
|
lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
|
|
417
423
|
|
|
418
|
-
/** Sets this color given a hue, saturation, lightness
|
|
424
|
+
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
419
425
|
* @param {Number} [hue=0]
|
|
420
426
|
* @param {Number} [saturation=0]
|
|
421
427
|
* @param {Number} [lightness=1]
|
|
@@ -451,21 +457,43 @@ class Color
|
|
|
451
457
|
).clamp();
|
|
452
458
|
}
|
|
453
459
|
|
|
454
|
-
/** Returns this color expressed as an
|
|
460
|
+
/** Returns this color expressed as an CSS color value
|
|
455
461
|
* @return {String} */
|
|
456
|
-
|
|
462
|
+
toString()
|
|
457
463
|
{
|
|
458
464
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
459
465
|
return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
|
|
460
466
|
}
|
|
461
467
|
|
|
462
|
-
/** Returns this color expressed as 32 bit integer value
|
|
468
|
+
/** Returns this color expressed as 32 bit integer RGBA value
|
|
463
469
|
* @return {Number} */
|
|
464
470
|
rgbaInt()
|
|
465
471
|
{
|
|
466
472
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
467
473
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
468
474
|
}
|
|
475
|
+
|
|
476
|
+
/** Returns this color expressed as a hex code
|
|
477
|
+
* @return {String} */
|
|
478
|
+
hex()
|
|
479
|
+
{
|
|
480
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
481
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/** Set this color from a hex code
|
|
485
|
+
* @param {String} hex - html hex code
|
|
486
|
+
* @return {Color} */
|
|
487
|
+
setHex(hex)
|
|
488
|
+
{
|
|
489
|
+
const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
|
|
490
|
+
this.r = fromHex(1);
|
|
491
|
+
this.g = fromHex(3),
|
|
492
|
+
this.b = fromHex(5);
|
|
493
|
+
this.a = 1;
|
|
494
|
+
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
495
|
+
return this;
|
|
496
|
+
}
|
|
469
497
|
}
|
|
470
498
|
|
|
471
499
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -502,7 +530,7 @@ class Timer
|
|
|
502
530
|
|
|
503
531
|
/** Returns true if set and elapsed
|
|
504
532
|
* @return {Boolean} */
|
|
505
|
-
elapsed() { return time >
|
|
533
|
+
elapsed() { return time > this.time; }
|
|
506
534
|
|
|
507
535
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
508
536
|
* @return {Number} */
|
|
@@ -510,7 +538,11 @@ class Timer
|
|
|
510
538
|
|
|
511
539
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
512
540
|
* @return {Number} */
|
|
513
|
-
getPercent() { return this.isSet()? percent(this.time - time,
|
|
541
|
+
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
542
|
+
|
|
543
|
+
/** Returns this timer expressed as a string
|
|
544
|
+
* @return {String} */
|
|
545
|
+
toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
|
|
514
546
|
}
|
|
515
547
|
/**
|
|
516
548
|
* LittleJS Engine Settings
|
|
@@ -659,6 +691,11 @@ let inputWASDEmulateDirection = 1;
|
|
|
659
691
|
* @memberof Settings */
|
|
660
692
|
let touchGamepadEnable = 0;
|
|
661
693
|
|
|
694
|
+
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
695
|
+
* @default
|
|
696
|
+
* @memberof Settings */
|
|
697
|
+
let touchGamepadAnalog = 1;
|
|
698
|
+
|
|
662
699
|
/** Size of virutal gamepad for touch devices in pixels
|
|
663
700
|
* @default
|
|
664
701
|
* @memberof Settings */
|
|
@@ -748,7 +785,7 @@ let medalDisplayIconSize = 50;
|
|
|
748
785
|
const engineName = 'LittleJS';
|
|
749
786
|
|
|
750
787
|
/** Version of engine */
|
|
751
|
-
const engineVersion = '1.
|
|
788
|
+
const engineVersion = '1.2.8';
|
|
752
789
|
|
|
753
790
|
/** Frames per second to update objects
|
|
754
791
|
* @default */
|
|
@@ -785,7 +822,7 @@ let averageFPS, drawCount;
|
|
|
785
822
|
// css text used for elements created by engine
|
|
786
823
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
787
824
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
788
|
-
const
|
|
825
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
789
826
|
|
|
790
827
|
///////////////////////////////////////////////////////////////////////////////
|
|
791
828
|
|
|
@@ -810,7 +847,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
810
847
|
document.body.style = styleBody;
|
|
811
848
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
812
849
|
mainContext = mainCanvas.getContext('2d');
|
|
813
|
-
mainCanvas.style =
|
|
850
|
+
mainCanvas.style = styleCanvas;
|
|
814
851
|
|
|
815
852
|
// init stuff and start engine
|
|
816
853
|
debugInit();
|
|
@@ -819,7 +856,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
819
856
|
// create overlay canvas for hud to appear above gl canvas
|
|
820
857
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
821
858
|
overlayContext = overlayCanvas.getContext('2d');
|
|
822
|
-
overlayCanvas.style =
|
|
859
|
+
overlayCanvas.style = styleCanvas;
|
|
823
860
|
|
|
824
861
|
gameInit();
|
|
825
862
|
touchGamepadCreate();
|
|
@@ -829,13 +866,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
829
866
|
// main update loop
|
|
830
867
|
const engineUpdate = (frameTimeMS=0)=>
|
|
831
868
|
{
|
|
832
|
-
requestAnimationFrame(engineUpdate);
|
|
833
|
-
|
|
834
869
|
// update time keeping
|
|
835
870
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
836
871
|
frameTimeLastMS = frameTimeMS;
|
|
837
872
|
if (debug || showWatermark)
|
|
838
|
-
averageFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1)
|
|
873
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
839
874
|
if (debug)
|
|
840
875
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
841
876
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -929,6 +964,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
929
964
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
930
965
|
drawCount = 0;
|
|
931
966
|
}
|
|
967
|
+
|
|
968
|
+
requestAnimationFrame(engineUpdate);
|
|
932
969
|
}
|
|
933
970
|
|
|
934
971
|
// set tile image source to load the image and start the engine
|
|
@@ -1042,11 +1079,11 @@ function engineObjectsCallback(pos, size, callbackFunction, objects=engineObject
|
|
|
1042
1079
|
class EngineObject
|
|
1043
1080
|
{
|
|
1044
1081
|
/** Create an engine object and adds it to the list of objects
|
|
1045
|
-
* @param {Vector2} [position=new Vector2(
|
|
1082
|
+
* @param {Vector2} [position=new Vector2()] - World space position of the object
|
|
1046
1083
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1047
1084
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1048
1085
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
1049
|
-
* @param {Number} [angle=0] - Angle
|
|
1086
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
1050
1087
|
* @param {Color} [color] - Color to apply to tile when rendered
|
|
1051
1088
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
1052
1089
|
*/
|
|
@@ -1087,12 +1124,15 @@ class EngineObject
|
|
|
1087
1124
|
this.gravityScale = 1;
|
|
1088
1125
|
/** @property {Number} [renderOrder=0] - Objects are sorted by render order */
|
|
1089
1126
|
this.renderOrder = renderOrder;
|
|
1127
|
+
/** @property {Vector2} [velocity=new Vector2()] - Velocity of the object */
|
|
1128
|
+
this.velocity = new Vector2();
|
|
1129
|
+
/** @property {Number} [angleVelocity=0] - Angular velocity of the object */
|
|
1130
|
+
this.angleVelocity = 0;
|
|
1090
1131
|
|
|
1091
1132
|
// init other internal object stuff
|
|
1092
1133
|
this.spawnTime = time;
|
|
1093
|
-
this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
|
|
1094
|
-
this.collideTiles = 1;
|
|
1095
1134
|
this.children = [];
|
|
1135
|
+
this.collideTiles = 1;
|
|
1096
1136
|
|
|
1097
1137
|
// add to list of objects
|
|
1098
1138
|
engineObjects.push(this);
|
|
@@ -1111,8 +1151,8 @@ class EngineObject
|
|
|
1111
1151
|
}
|
|
1112
1152
|
|
|
1113
1153
|
// limit max speed to prevent missing collisions
|
|
1114
|
-
this.velocity.x = clamp(this.velocity.x, objectMaxSpeed,
|
|
1115
|
-
this.velocity.y = clamp(this.velocity.y, objectMaxSpeed,
|
|
1154
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
1155
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
1116
1156
|
|
|
1117
1157
|
// apply physics
|
|
1118
1158
|
const oldPos = this.pos.copy();
|
|
@@ -1202,8 +1242,8 @@ class EngineObject
|
|
|
1202
1242
|
|
|
1203
1243
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1204
1244
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1205
|
-
this.velocity.y = lerp(elasticity,
|
|
1206
|
-
o.velocity.y = lerp(elasticity,
|
|
1245
|
+
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
1246
|
+
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
1207
1247
|
}
|
|
1208
1248
|
}
|
|
1209
1249
|
if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
|
|
@@ -1223,8 +1263,8 @@ class EngineObject
|
|
|
1223
1263
|
|
|
1224
1264
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1225
1265
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1226
|
-
this.velocity.x = lerp(elasticity,
|
|
1227
|
-
o.velocity.x = lerp(elasticity,
|
|
1266
|
+
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
1267
|
+
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
1228
1268
|
}
|
|
1229
1269
|
else // bounce if other object is fixed
|
|
1230
1270
|
this.velocity.x *= -this.elasticity;
|
|
@@ -1254,7 +1294,7 @@ class EngineObject
|
|
|
1254
1294
|
|
|
1255
1295
|
// adjust next velocity to settle on ground
|
|
1256
1296
|
const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
|
|
1257
|
-
if (o < 0 && o >
|
|
1297
|
+
if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
|
|
1258
1298
|
this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
|
|
1259
1299
|
|
|
1260
1300
|
// move to previous position
|
|
@@ -1359,6 +1399,22 @@ class EngineObject
|
|
|
1359
1399
|
this.isSolid = isSolid;
|
|
1360
1400
|
this.collideTiles = collideTiles;
|
|
1361
1401
|
}
|
|
1402
|
+
|
|
1403
|
+
toString()
|
|
1404
|
+
{
|
|
1405
|
+
let text = 'type = ' + this.constructor.name;
|
|
1406
|
+
if (this.pos.x || this.pos.y)
|
|
1407
|
+
text += '\npos = ' + this.pos;
|
|
1408
|
+
if (this.velocity.x || this.velocity.y)
|
|
1409
|
+
text += '\nvelocity = ' + this.velocity;
|
|
1410
|
+
if (this.size.x || this.size.y)
|
|
1411
|
+
text += '\nsize = ' + this.size;
|
|
1412
|
+
if (this.angle)
|
|
1413
|
+
text += '\nangle = ' + this.angle.toFixed(3);
|
|
1414
|
+
if (this.color)
|
|
1415
|
+
text += '\ncolor = ' + this.color;
|
|
1416
|
+
return text;
|
|
1417
|
+
}
|
|
1362
1418
|
}
|
|
1363
1419
|
/**
|
|
1364
1420
|
* LittleJS Drawing System
|
|
@@ -1472,7 +1528,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1472
1528
|
if (tileIndex < 0)
|
|
1473
1529
|
{
|
|
1474
1530
|
// if negative tile index, force untextured
|
|
1475
|
-
context.fillStyle = color
|
|
1531
|
+
context.fillStyle = color;
|
|
1476
1532
|
context.fillRect(-.5, -.5, 1, 1);
|
|
1477
1533
|
}
|
|
1478
1534
|
else
|
|
@@ -1565,7 +1621,20 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1565
1621
|
context.restore();
|
|
1566
1622
|
}
|
|
1567
1623
|
|
|
1568
|
-
/**
|
|
1624
|
+
/** Enable normal or additive blend mode
|
|
1625
|
+
* @param {Boolean} [additive=0]
|
|
1626
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
1627
|
+
* @memberof Draw */
|
|
1628
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
1629
|
+
{
|
|
1630
|
+
if (glEnable && useWebGL)
|
|
1631
|
+
glSetBlendMode(additive);
|
|
1632
|
+
else
|
|
1633
|
+
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/** Draw text on overlay canvas in screen space
|
|
1637
|
+
* Automatically splits new lines into rows
|
|
1569
1638
|
* @param {String} text
|
|
1570
1639
|
* @param {Vector2} pos
|
|
1571
1640
|
* @param {Number} [size=1]
|
|
@@ -1574,32 +1643,129 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1574
1643
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1575
1644
|
* @param {String} [textAlign='center']
|
|
1576
1645
|
* @memberof Draw */
|
|
1577
|
-
function
|
|
1646
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1578
1647
|
{
|
|
1579
|
-
|
|
1580
|
-
overlayContext.
|
|
1648
|
+
overlayContext.fillStyle = color;
|
|
1649
|
+
overlayContext.lineWidth = lineWidth *= cameraScale;
|
|
1650
|
+
overlayContext.strokeStyle = lineColor;
|
|
1581
1651
|
overlayContext.textAlign = textAlign;
|
|
1652
|
+
overlayContext.font = size + 'px '+ font;
|
|
1582
1653
|
overlayContext.textBaseline = 'middle';
|
|
1583
|
-
|
|
1654
|
+
|
|
1655
|
+
pos = pos.copy();
|
|
1656
|
+
text.split('\n').forEach(line=>
|
|
1584
1657
|
{
|
|
1585
|
-
overlayContext.
|
|
1586
|
-
overlayContext.
|
|
1587
|
-
|
|
1588
|
-
}
|
|
1589
|
-
overlayContext.fillStyle = color.rgba();
|
|
1590
|
-
overlayContext.fillText(text, pos.x, pos.y);
|
|
1658
|
+
lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
|
|
1659
|
+
overlayContext.fillText(line, pos.x, pos.y);
|
|
1660
|
+
pos.y += size;
|
|
1661
|
+
});
|
|
1591
1662
|
}
|
|
1592
1663
|
|
|
1593
|
-
/**
|
|
1594
|
-
*
|
|
1595
|
-
* @param {
|
|
1664
|
+
/** Draw text on overlay canvas in world space
|
|
1665
|
+
* Automatically splits new lines into rows
|
|
1666
|
+
* @param {String} text
|
|
1667
|
+
* @param {Vector2} pos
|
|
1668
|
+
* @param {Number} [size=1]
|
|
1669
|
+
* @param {Color} [color=new Color(1,1,1)]
|
|
1670
|
+
* @param {Number} [lineWidth=0]
|
|
1671
|
+
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1672
|
+
* @param {String} [textAlign='center']
|
|
1596
1673
|
* @memberof Draw */
|
|
1597
|
-
function
|
|
1674
|
+
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1598
1675
|
{
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1676
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
1683
|
+
* <br> - 96 characters (from space to tilde) are stored in an image
|
|
1684
|
+
* <br> - Uses a default 8x8 font if none is supplied
|
|
1685
|
+
* <br> - You can also use fonts from the main tile sheet
|
|
1686
|
+
* @example
|
|
1687
|
+
* // use built in font
|
|
1688
|
+
* const font = new ImageFont;
|
|
1689
|
+
*
|
|
1690
|
+
* // draw text
|
|
1691
|
+
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1692
|
+
*/
|
|
1693
|
+
|
|
1694
|
+
let engineFontImage;
|
|
1695
|
+
|
|
1696
|
+
class FontImage
|
|
1697
|
+
{
|
|
1698
|
+
/** Create an image font
|
|
1699
|
+
* @param {Image} [image] - The image the font is stored in, if undefined the default font is used
|
|
1700
|
+
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1701
|
+
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1702
|
+
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
1703
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1704
|
+
*/
|
|
1705
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1706
|
+
{
|
|
1707
|
+
if (!image && !engineFontImage)
|
|
1708
|
+
{
|
|
1709
|
+
// load default font image
|
|
1710
|
+
engineFontImage = new Image();
|
|
1711
|
+
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
this.image = image || engineFontImage;
|
|
1715
|
+
this.tileSize = tileSize;
|
|
1716
|
+
this.paddingSize = paddingSize;
|
|
1717
|
+
this.startTileIndex = startTileIndex;
|
|
1718
|
+
this.context = context;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
/** Draw text in screen space using the image font
|
|
1722
|
+
* @param {String} text
|
|
1723
|
+
* @param {Vector2} pos
|
|
1724
|
+
* @param {Number} [scale=4]
|
|
1725
|
+
* @param {Boolean} [center]
|
|
1726
|
+
*/
|
|
1727
|
+
drawTextScreen(text, pos, scale=4, center)
|
|
1728
|
+
{
|
|
1729
|
+
const context = this.context;
|
|
1730
|
+
context.save();
|
|
1731
|
+
context.imageSmoothingEnabled = !cavasPixelated;
|
|
1732
|
+
|
|
1733
|
+
const size = this.tileSize;
|
|
1734
|
+
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1735
|
+
const cols = this.image.width / this.tileSize.x |0;
|
|
1736
|
+
text.split('\n').forEach((line, i)=>
|
|
1737
|
+
{
|
|
1738
|
+
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1739
|
+
for(let j=line.length; j--;)
|
|
1740
|
+
{
|
|
1741
|
+
// draw each character
|
|
1742
|
+
let charCode = line[j].charCodeAt();
|
|
1743
|
+
if (charCode < 32 || charCode > 127)
|
|
1744
|
+
charCode = 127; // unknown character
|
|
1745
|
+
|
|
1746
|
+
// get the character source location and draw it
|
|
1747
|
+
const tile = this.startTileIndex + charCode - 32;
|
|
1748
|
+
const x = tile % cols;
|
|
1749
|
+
const y = tile / cols |0;
|
|
1750
|
+
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
1751
|
+
context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
|
|
1752
|
+
drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
|
|
1753
|
+
}
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
context.restore();
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
/** Draw text in world space using the image font
|
|
1760
|
+
* @param {String} text
|
|
1761
|
+
* @param {Vector2} pos
|
|
1762
|
+
* @param {Number} [scale=.25]
|
|
1763
|
+
* @param {Boolean} [center]
|
|
1764
|
+
*/
|
|
1765
|
+
drawText(text, pos, scale=1, center)
|
|
1766
|
+
{
|
|
1767
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1768
|
+
}
|
|
1603
1769
|
}
|
|
1604
1770
|
|
|
1605
1771
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1790,7 +1956,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
1790
1956
|
|
|
1791
1957
|
const rect = mainCanvas.getBoundingClientRect();
|
|
1792
1958
|
return mainCanvasSize.multiply(
|
|
1793
|
-
vec2(percent(mousePos.x, rect.
|
|
1959
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
1794
1960
|
}
|
|
1795
1961
|
|
|
1796
1962
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1831,8 +1997,8 @@ function gamepadsUpdate()
|
|
|
1831
1997
|
// read clamp dead zone of analog sticks
|
|
1832
1998
|
const deadZone = .3, deadZoneMax = .8;
|
|
1833
1999
|
const applyDeadZone = (v)=>
|
|
1834
|
-
v > deadZone ? percent( v,
|
|
1835
|
-
v < -deadZone ? -percent(-v,
|
|
2000
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2001
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
1836
2002
|
|
|
1837
2003
|
// read analog sticks
|
|
1838
2004
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -1958,7 +2124,14 @@ function touchGamepadCreate()
|
|
|
1958
2124
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
1959
2125
|
{
|
|
1960
2126
|
// virtual analog stick
|
|
1961
|
-
|
|
2127
|
+
if (touchGamepadAnalog)
|
|
2128
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2129
|
+
else
|
|
2130
|
+
{
|
|
2131
|
+
// 8 way dpad
|
|
2132
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
2133
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2134
|
+
}
|
|
1962
2135
|
}
|
|
1963
2136
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
1964
2137
|
{
|
|
@@ -1982,7 +2155,7 @@ function touchGamepadRender()
|
|
|
1982
2155
|
return;
|
|
1983
2156
|
|
|
1984
2157
|
// fade off when not touching or paused
|
|
1985
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
2158
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
1986
2159
|
if (!alpha || paused)
|
|
1987
2160
|
return;
|
|
1988
2161
|
|
|
@@ -1993,12 +2166,28 @@ function touchGamepadRender()
|
|
|
1993
2166
|
overlayContext.lineWidth = 3;
|
|
1994
2167
|
|
|
1995
2168
|
// draw left analog stick
|
|
1996
|
-
overlayContext.fillStyle =
|
|
2169
|
+
overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
1997
2170
|
overlayContext.beginPath();
|
|
1998
|
-
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
1999
|
-
overlayContext.fill();
|
|
2000
|
-
overlayContext.stroke();
|
|
2001
2171
|
|
|
2172
|
+
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2173
|
+
if (touchGamepadAnalog)
|
|
2174
|
+
{
|
|
2175
|
+
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
2176
|
+
overlayContext.fill();
|
|
2177
|
+
overlayContext.stroke();
|
|
2178
|
+
}
|
|
2179
|
+
else // draw cross shaped gamepad
|
|
2180
|
+
{
|
|
2181
|
+
for(let i=10; i--;)
|
|
2182
|
+
{
|
|
2183
|
+
const angle = i*PI/4;
|
|
2184
|
+
overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
2185
|
+
i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
2186
|
+
i==1 && overlayContext.fill();
|
|
2187
|
+
}
|
|
2188
|
+
overlayContext.stroke();
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2002
2191
|
// draw right face buttons
|
|
2003
2192
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2004
2193
|
for (let i=4; i--;)
|
|
@@ -2085,7 +2274,7 @@ class Sound
|
|
|
2085
2274
|
return; // out of range
|
|
2086
2275
|
|
|
2087
2276
|
// attenuate volume by distance
|
|
2088
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
2277
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
2089
2278
|
}
|
|
2090
2279
|
|
|
2091
2280
|
// get pan from screen space coords
|
|
@@ -2267,7 +2456,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2267
2456
|
// connect source to gain
|
|
2268
2457
|
(
|
|
2269
2458
|
window.StereoPannerNode ? // create pan node if possible
|
|
2270
|
-
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1,
|
|
2459
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
2271
2460
|
: source
|
|
2272
2461
|
)
|
|
2273
2462
|
.connect(gainNode);
|
|
@@ -2639,14 +2828,14 @@ class TileLayerData
|
|
|
2639
2828
|
*/
|
|
2640
2829
|
class TileLayer extends EngineObject
|
|
2641
2830
|
{
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2831
|
+
/** Create a tile layer object
|
|
2832
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2833
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
2834
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2835
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2836
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2837
|
+
*/
|
|
2838
|
+
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2650
2839
|
{
|
|
2651
2840
|
super(pos, size, -1, tileSize, 0, undefined, renderOrder);
|
|
2652
2841
|
|
|
@@ -2704,17 +2893,19 @@ class TileLayer extends EngineObject
|
|
|
2704
2893
|
);
|
|
2705
2894
|
}
|
|
2706
2895
|
|
|
2707
|
-
/** Draw all the tile data to an offscreen canvas
|
|
2896
|
+
/** Draw all the tile data to an offscreen canvas
|
|
2897
|
+
* - This may be slow in some browsers
|
|
2898
|
+
*/
|
|
2708
2899
|
redraw()
|
|
2709
2900
|
{
|
|
2710
|
-
this.redrawStart();
|
|
2901
|
+
this.redrawStart(1);
|
|
2711
2902
|
this.drawAllTileData();
|
|
2712
2903
|
this.redrawEnd();
|
|
2713
2904
|
}
|
|
2714
2905
|
|
|
2715
2906
|
/** Call to start the redraw process
|
|
2716
|
-
* @param {Boolean} [clear=
|
|
2717
|
-
redrawStart(clear =
|
|
2907
|
+
* @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
|
|
2908
|
+
redrawStart(clear = 0)
|
|
2718
2909
|
{
|
|
2719
2910
|
if (clear)
|
|
2720
2911
|
{
|
|
@@ -2804,7 +2995,7 @@ class TileLayer extends EngineObject
|
|
|
2804
2995
|
if (tileIndex < 0)
|
|
2805
2996
|
{
|
|
2806
2997
|
// untextured
|
|
2807
|
-
context.fillStyle = color
|
|
2998
|
+
context.fillStyle = color;
|
|
2808
2999
|
context.fillRect(-.5, -.5, 1, 1);
|
|
2809
3000
|
}
|
|
2810
3001
|
else
|
|
@@ -2823,7 +3014,8 @@ class TileLayer extends EngineObject
|
|
|
2823
3014
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
2824
3015
|
* @param {Color} [color=new Color(1,1,1)]
|
|
2825
3016
|
* @param {Number} [angle=0] */
|
|
2826
|
-
drawRect(pos, size, color, angle)
|
|
3017
|
+
drawRect(pos, size, color, angle)
|
|
3018
|
+
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
2827
3019
|
}
|
|
2828
3020
|
/*
|
|
2829
3021
|
LittleJS Particle System
|
|
@@ -2842,9 +3034,9 @@ class TileLayer extends EngineObject
|
|
|
2842
3034
|
* let pos = vec2(2,3);
|
|
2843
3035
|
* let particleEmiter = new ParticleEmitter
|
|
2844
3036
|
* (
|
|
2845
|
-
* pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
|
|
3037
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
2846
3038
|
* 0, vec2(16), // tileIndex, tileSize
|
|
2847
|
-
* new Color,
|
|
3039
|
+
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
2848
3040
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
2849
3041
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
2850
3042
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
@@ -2855,6 +3047,7 @@ class ParticleEmitter extends EngineObject
|
|
|
2855
3047
|
{
|
|
2856
3048
|
/** Create a particle system with the given settings
|
|
2857
3049
|
* @param {Vector2} position - World space position of the emitter
|
|
3050
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
2858
3051
|
* @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
2859
3052
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
2860
3053
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
@@ -2884,6 +3077,7 @@ class ParticleEmitter extends EngineObject
|
|
|
2884
3077
|
constructor
|
|
2885
3078
|
(
|
|
2886
3079
|
pos,
|
|
3080
|
+
angle,
|
|
2887
3081
|
emitSize = 0,
|
|
2888
3082
|
emitTime = 0,
|
|
2889
3083
|
emitRate = 100,
|
|
@@ -2911,7 +3105,7 @@ class ParticleEmitter extends EngineObject
|
|
|
2911
3105
|
renderOrder = additive ? 1e9 : 0
|
|
2912
3106
|
)
|
|
2913
3107
|
{
|
|
2914
|
-
super(pos, new Vector2, tileIndex, tileSize,
|
|
3108
|
+
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
2915
3109
|
|
|
2916
3110
|
// emitter settings
|
|
2917
3111
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3144,7 +3338,7 @@ function medalsInit(saveName)
|
|
|
3144
3338
|
{
|
|
3145
3339
|
// check if medals are unlocked
|
|
3146
3340
|
medalsSaveName = saveName;
|
|
3147
|
-
debugMedals || medals.forEach(medal=>
|
|
3341
|
+
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3148
3342
|
}
|
|
3149
3343
|
|
|
3150
3344
|
/**
|
|
@@ -3333,6 +3527,9 @@ class Newgrounds
|
|
|
3333
3527
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
3334
3528
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
3335
3529
|
debugMedals && console.log(this.scoreboards);
|
|
3530
|
+
|
|
3531
|
+
const keepAliveMS = 5 * 60 * 1e3;
|
|
3532
|
+
setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
|
|
3336
3533
|
}
|
|
3337
3534
|
|
|
3338
3535
|
/** Send message to unlock a medal by id
|
|
@@ -3344,9 +3541,6 @@ class Newgrounds
|
|
|
3344
3541
|
* @param {Number} value - The score value */
|
|
3345
3542
|
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
|
|
3346
3543
|
|
|
3347
|
-
/** Send message to log a view */
|
|
3348
|
-
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3349
|
-
|
|
3350
3544
|
/** Get scores from a scoreboard
|
|
3351
3545
|
* @param {Number} id - The scoreboard id
|
|
3352
3546
|
* @param {String} [user=0] - A user's id or name
|
|
@@ -3358,6 +3552,9 @@ class Newgrounds
|
|
|
3358
3552
|
getScores(id, user=0, social=0, skip=0, limit=10)
|
|
3359
3553
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3360
3554
|
|
|
3555
|
+
/** Send message to log a view */
|
|
3556
|
+
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3557
|
+
|
|
3361
3558
|
/** Send a message to call a component of the Newgrounds API
|
|
3362
3559
|
* @param {String} component - Name of the component
|
|
3363
3560
|
* @param {Object} [parameters=0] - Parameters to use for call
|
|
@@ -3445,8 +3642,8 @@ function glInit()
|
|
|
3445
3642
|
|
|
3446
3643
|
// create the canvas and tile texture
|
|
3447
3644
|
glCanvas = document.createElement('canvas');
|
|
3448
|
-
glContext = glCanvas.getContext('webgl');
|
|
3449
|
-
glCanvas.style =
|
|
3645
|
+
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3646
|
+
glCanvas.style = styleCanvas;
|
|
3450
3647
|
glTileTexture = glCreateTexture(tileImage);
|
|
3451
3648
|
|
|
3452
3649
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -3454,25 +3651,24 @@ function glInit()
|
|
|
3454
3651
|
|
|
3455
3652
|
// setup vertex and fragment shaders
|
|
3456
3653
|
glShader = glCreateProgram(
|
|
3457
|
-
'precision highp float;'+
|
|
3458
|
-
'uniform mat4 m;'+
|
|
3459
|
-
'attribute
|
|
3460
|
-
'attribute
|
|
3461
|
-
'
|
|
3462
|
-
'varying
|
|
3463
|
-
'
|
|
3464
|
-
'
|
|
3465
|
-
'
|
|
3466
|
-
'
|
|
3467
|
-
'}' // end of shader
|
|
3654
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3655
|
+
'uniform mat4 m;'+ // transform matrix
|
|
3656
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3657
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3658
|
+
'varying vec2 v;'+ // return uv
|
|
3659
|
+
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3660
|
+
'void main(){'+ // shader entry point
|
|
3661
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
3662
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3663
|
+
'}' // end of shader
|
|
3468
3664
|
,
|
|
3469
|
-
'precision highp float;'+
|
|
3470
|
-
'varying vec2 v;'+
|
|
3471
|
-
'varying vec4 d,e;'+
|
|
3472
|
-
'uniform sampler2D
|
|
3473
|
-
'void main(){'+
|
|
3474
|
-
'gl_FragColor=texture2D(
|
|
3475
|
-
'}'
|
|
3665
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3666
|
+
'varying vec2 v;'+ // uv
|
|
3667
|
+
'varying vec4 d,e;'+ // color, additiveColor
|
|
3668
|
+
'uniform sampler2D s;'+ // texture
|
|
3669
|
+
'void main(){'+ // shader entry point
|
|
3670
|
+
'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
3671
|
+
'}' // end of shader
|
|
3476
3672
|
);
|
|
3477
3673
|
|
|
3478
3674
|
// init buffers
|
|
@@ -3490,12 +3686,10 @@ function glInit()
|
|
|
3490
3686
|
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3491
3687
|
offset += size*typeSize;
|
|
3492
3688
|
}
|
|
3493
|
-
initVertexAttribArray('a', gl_FLOAT, 4, 1); // angle
|
|
3494
3689
|
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3495
|
-
initVertexAttribArray('s', gl_FLOAT, 4, 2); // size
|
|
3496
3690
|
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3497
3691
|
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3498
|
-
initVertexAttribArray('
|
|
3692
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3499
3693
|
}
|
|
3500
3694
|
|
|
3501
3695
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3610,6 +3804,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3610
3804
|
|
|
3611
3805
|
// clear and set to same size as main canvas
|
|
3612
3806
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3807
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3613
3808
|
|
|
3614
3809
|
// set up the shader
|
|
3615
3810
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
@@ -3656,12 +3851,10 @@ function glCopyToContext(context, forceDraw)
|
|
|
3656
3851
|
if (!glEnable || !glBatchCount) return;
|
|
3657
3852
|
|
|
3658
3853
|
glFlush();
|
|
3854
|
+
|
|
3855
|
+
// do not draw in overlay mode because the canvas is visible
|
|
3659
3856
|
if (!glOverlay || forceDraw)
|
|
3660
|
-
{
|
|
3661
|
-
// do not draw/clear in overlay mode because the canvas is visible
|
|
3662
3857
|
context.drawImage(glCanvas, 0, 0);
|
|
3663
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3664
|
-
}
|
|
3665
3858
|
}
|
|
3666
3859
|
|
|
3667
3860
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
@@ -3684,51 +3877,49 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3684
3877
|
// flush if there is no room for more verts or if different blend mode
|
|
3685
3878
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
3686
3879
|
glFlush();
|
|
3880
|
+
|
|
3881
|
+
// prepare to create the verts from size and angle
|
|
3882
|
+
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3883
|
+
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
3687
3884
|
|
|
3688
3885
|
// setup 2 triangles to form a quad
|
|
3689
3886
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3690
3887
|
|
|
3691
3888
|
// vertex 0
|
|
3692
|
-
glPositionData[offset++] =
|
|
3693
|
-
glPositionData[offset++] =
|
|
3694
|
-
glPositionData[offset++] =
|
|
3695
|
-
|
|
3696
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3889
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3890
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3891
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3892
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3697
3893
|
|
|
3698
3894
|
// vertex 1
|
|
3699
|
-
glPositionData[offset++] =
|
|
3700
|
-
glPositionData[offset++] =
|
|
3701
|
-
glPositionData[offset++] =
|
|
3702
|
-
|
|
3703
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3895
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3896
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3897
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3898
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3704
3899
|
|
|
3705
3900
|
// vertex 2
|
|
3706
|
-
glPositionData[offset++] =
|
|
3707
|
-
glPositionData[offset++] =
|
|
3708
|
-
glPositionData[offset++] =
|
|
3709
|
-
|
|
3710
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3901
|
+
glPositionData[offset++] = x - cx + sy;
|
|
3902
|
+
glPositionData[offset++] = y + cy + sx;
|
|
3903
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3904
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3711
3905
|
|
|
3712
3906
|
// vertex 0
|
|
3713
|
-
glPositionData[offset++] =
|
|
3714
|
-
glPositionData[offset++] =
|
|
3715
|
-
glPositionData[offset++] =
|
|
3716
|
-
|
|
3717
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3907
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3908
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3909
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3910
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3718
3911
|
|
|
3719
3912
|
// vertex 3
|
|
3720
|
-
glPositionData[offset++] =
|
|
3721
|
-
glPositionData[offset++] =
|
|
3722
|
-
glPositionData[offset++] =
|
|
3723
|
-
|
|
3724
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3913
|
+
glPositionData[offset++] = x + cx - sy;
|
|
3914
|
+
glPositionData[offset++] = y - cy - sx;
|
|
3915
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3916
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3725
3917
|
|
|
3726
3918
|
// vertex 1
|
|
3727
|
-
glPositionData[offset++] =
|
|
3728
|
-
glPositionData[offset++] =
|
|
3729
|
-
glPositionData[offset++] =
|
|
3730
|
-
|
|
3731
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3919
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3920
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3921
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3922
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3732
3923
|
}
|
|
3733
3924
|
|
|
3734
3925
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -3760,6 +3951,6 @@ gl_LINK_STATUS = 35714,
|
|
|
3760
3951
|
|
|
3761
3952
|
// constants for batch rendering
|
|
3762
3953
|
gl_VERTICES_PER_QUAD = 6,
|
|
3763
|
-
gl_INDICIES_PER_VERT =
|
|
3954
|
+
gl_INDICIES_PER_VERT = 6,
|
|
3764
3955
|
gl_MAX_BATCH = 1<<16,
|
|
3765
|
-
gl_VERTEX_BYTE_STRIDE =
|
|
3956
|
+
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|