littlejsengine 1.14.19 → 1.14.28
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 +217 -38
- package/dist/littlejs.esm.js +879 -324
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +861 -324
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +799 -325
- package/examples/box2d/gameObjects.js +6 -10
- package/examples/box2d/scenes.js +2 -2
- package/examples/breakout/gameObjects.js +1 -1
- package/examples/electron/game.js +1 -41
- package/examples/electron/index.html +2 -2
- package/examples/htmlMenu/game.js +1 -1
- package/examples/index.html +19 -10
- package/examples/module/game.js +5 -7
- package/examples/particles/index.html +6 -9
- package/examples/platformer/gameCharacter.js +4 -3
- package/examples/platformer/gameEffects.js +7 -2
- package/examples/platformer/gameObjects.js +6 -11
- package/examples/shorts/animation.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/box2d.js +2 -2
- package/examples/shorts/box2dCar.js +18 -7
- package/examples/shorts/box2dPool.js +108 -0
- package/examples/shorts/cameraDrag.js +22 -0
- package/examples/shorts/debugDraw.js +37 -0
- package/examples/shorts/flappyGame.js +1 -0
- package/examples/shorts/fps.js +90 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +1 -3
- package/examples/shorts/input.js +59 -0
- package/examples/shorts/landerGame.js +1 -3
- package/examples/shorts/medals.js +15 -9
- package/examples/shorts/music.js +15 -17
- package/examples/shorts/musicPlayer.js +24 -24
- package/examples/shorts/platformer.js +0 -2
- package/examples/shorts/postProcess.js +1 -1
- package/examples/shorts/sequencer.js +3 -3
- package/examples/shorts/slidingPuzzle.js +1 -1
- package/examples/shorts/spaceGame.js +1 -3
- package/examples/shorts/spriteAtlas.js +6 -8
- package/examples/shorts/starfield.js +1 -1
- package/examples/shorts/tileRaycast.js +39 -0
- package/examples/shorts/tiles.png +0 -0
- package/examples/shorts/tiltedView.js +1 -4
- package/examples/shorts/timers.js +1 -2
- package/examples/shorts/topDown.js +0 -2
- package/examples/starter/game.js +5 -7
- package/examples/starter/index.html +2 -2
- package/examples/typescript/game.js +3 -2
- package/examples/typescript/game.ts +5 -7
- package/examples/uiSystem/game.js +2 -2
- package/package.json +1 -1
- package/plugins/box2d.js +170 -50
- package/plugins/pluginExport.js +2 -0
- package/plugins/uiSystem.js +55 -27
- package/src/engine.js +19 -16
- package/src/engineAudio.js +27 -15
- package/src/engineDebug.js +64 -0
- package/src/engineDraw.js +88 -32
- package/src/engineExport.js +16 -0
- package/src/engineInput.js +35 -25
- package/src/engineMedals.js +2 -2
- package/src/engineObject.js +55 -10
- package/src/engineParticles.js +36 -20
- package/src/engineRelease.js +2 -1
- package/src/engineSettings.js +20 -1
- package/src/engineTileLayer.js +64 -59
- package/src/engineUtilities.js +213 -59
- package/src/engineWebGL.js +13 -8
- package/examples/shorts/raycasting.js +0 -79
package/src/engineUtilities.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
'use strict';
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** The value of PI
|
|
14
14
|
* @type {number}
|
|
15
15
|
* @default Math.PI
|
|
16
16
|
* @memberof Utilities */
|
|
@@ -20,25 +20,80 @@ const PI = Math.PI;
|
|
|
20
20
|
* @param {number} value
|
|
21
21
|
* @return {number}
|
|
22
22
|
* @memberof Utilities */
|
|
23
|
-
|
|
23
|
+
const abs = Math.abs;
|
|
24
|
+
|
|
25
|
+
/** Returns floored value of value passed in
|
|
26
|
+
* @param {number} value
|
|
27
|
+
* @return {number}
|
|
28
|
+
* @memberof Utilities */
|
|
29
|
+
const floor = Math.floor;
|
|
30
|
+
|
|
31
|
+
/** Returns ceiled value of value passed in
|
|
32
|
+
* @param {number} value
|
|
33
|
+
* @return {number}
|
|
34
|
+
* @memberof Utilities */
|
|
35
|
+
const ceil = Math.ceil;
|
|
36
|
+
|
|
37
|
+
/** Returns rounded value passed in
|
|
38
|
+
* @param {number} value
|
|
39
|
+
* @return {number}
|
|
40
|
+
* @memberof Utilities */
|
|
41
|
+
const round = Math.round;
|
|
24
42
|
|
|
25
43
|
/** Returns lowest value passed in
|
|
26
44
|
* @param {...number} values
|
|
27
45
|
* @return {number}
|
|
28
46
|
* @memberof Utilities */
|
|
29
|
-
|
|
47
|
+
const min = Math.min;
|
|
30
48
|
|
|
31
49
|
/** Returns highest value passed in
|
|
32
50
|
* @param {...number} values
|
|
33
51
|
* @return {number}
|
|
34
52
|
* @memberof Utilities */
|
|
35
|
-
|
|
53
|
+
const max = Math.max;
|
|
36
54
|
|
|
37
55
|
/** Returns the sign of value passed in
|
|
38
56
|
* @param {number} value
|
|
39
57
|
* @return {number}
|
|
40
58
|
* @memberof Utilities */
|
|
41
|
-
|
|
59
|
+
const sign = Math.sign;
|
|
60
|
+
|
|
61
|
+
/** Returns hypotenuse of values passed in
|
|
62
|
+
* @param {...number} values
|
|
63
|
+
* @return {number}
|
|
64
|
+
* @memberof Utilities */
|
|
65
|
+
const hypot = Math.hypot;
|
|
66
|
+
|
|
67
|
+
/** Returns log2 of value passed in
|
|
68
|
+
* @param {number} value
|
|
69
|
+
* @return {number}
|
|
70
|
+
* @memberof Utilities */
|
|
71
|
+
const log2 = Math.log2;
|
|
72
|
+
|
|
73
|
+
/** Returns sin of value passed in
|
|
74
|
+
* @param {number} value
|
|
75
|
+
* @return {number}
|
|
76
|
+
* @memberof Utilities */
|
|
77
|
+
const sin = Math.sin;
|
|
78
|
+
|
|
79
|
+
/** Returns cos of value passed in
|
|
80
|
+
* @param {number} value
|
|
81
|
+
* @return {number}
|
|
82
|
+
* @memberof Utilities */
|
|
83
|
+
const cos = Math.cos;
|
|
84
|
+
|
|
85
|
+
/** Returns tan of value passed in
|
|
86
|
+
* @param {number} value
|
|
87
|
+
* @return {number}
|
|
88
|
+
* @memberof Utilities */
|
|
89
|
+
const tan = Math.tan;
|
|
90
|
+
|
|
91
|
+
/** Returns atan2 of values passed in
|
|
92
|
+
* @param {number} y
|
|
93
|
+
* @param {number} x
|
|
94
|
+
* @return {number}
|
|
95
|
+
* @memberof Utilities */
|
|
96
|
+
const atan2 = Math.atan2;
|
|
42
97
|
|
|
43
98
|
/** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
|
|
44
99
|
* @param {number} dividend
|
|
@@ -143,7 +198,7 @@ function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
|
143
198
|
* @param {number} value
|
|
144
199
|
* @return {number}
|
|
145
200
|
* @memberof Utilities */
|
|
146
|
-
function nearestPowerOfTwo(value) { return 2**
|
|
201
|
+
function nearestPowerOfTwo(value) { return 2**ceil(log2(value)); }
|
|
147
202
|
|
|
148
203
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
149
204
|
* this can be used for simple collision detection between objects
|
|
@@ -211,7 +266,7 @@ function isIntersecting(start, end, pos, size)
|
|
|
211
266
|
* @return {number} - Value waving between 0 and amplitude
|
|
212
267
|
* @memberof Utilities */
|
|
213
268
|
function wave(frequency=1, amplitude=1, t=time, offset=0)
|
|
214
|
-
{ return amplitude/2 * (1 -
|
|
269
|
+
{ return amplitude/2 * (1 - cos(offset + t*frequency*2*PI)); }
|
|
215
270
|
|
|
216
271
|
/** Formats seconds to mm:ss style for display purposes
|
|
217
272
|
* @param {number} t - time in seconds
|
|
@@ -250,6 +305,103 @@ function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
|
250
305
|
* @memberof Utilities */
|
|
251
306
|
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
252
307
|
|
|
308
|
+
/**
|
|
309
|
+
* Check if object is an array
|
|
310
|
+
* @param {any} a
|
|
311
|
+
* @return {boolean}
|
|
312
|
+
* @memberof Utilities */
|
|
313
|
+
function isArray(a) { return Array.isArray(a); }
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @callback LineTestFunction - Checks if a position is colliding
|
|
317
|
+
* @param {Vector2} pos
|
|
318
|
+
* @memberof Draw
|
|
319
|
+
*/
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Casts a ray and returns position of the first collision found, or undefined if none are found
|
|
323
|
+
* @param {Vector2} posStart
|
|
324
|
+
* @param {Vector2} posEnd
|
|
325
|
+
* @param {LineTestFunction} testFunction - Check if colliding
|
|
326
|
+
* @param {Vector2} [normal] - Optional vector to store the normal
|
|
327
|
+
* @return {Vector2|undefined} - Position of the collision or undefined if none found
|
|
328
|
+
* @memberof Utilities */
|
|
329
|
+
function lineTest(posStart, posEnd, testFunction, normal)
|
|
330
|
+
{
|
|
331
|
+
ASSERT(isVector2(posStart), 'posStart must be a vec2');
|
|
332
|
+
ASSERT(isVector2(posEnd), 'posEnd must be a vec2');
|
|
333
|
+
ASSERT(typeof testFunction === 'function', 'testFunction must be a function');
|
|
334
|
+
ASSERT(!normal || isVector2(normal), 'normal must be a vec2');
|
|
335
|
+
|
|
336
|
+
// get ray direction and length
|
|
337
|
+
const dx = posEnd.x - posStart.x;
|
|
338
|
+
const dy = posEnd.y - posStart.y;
|
|
339
|
+
const totalLength = hypot(dx, dy);
|
|
340
|
+
if (!totalLength)
|
|
341
|
+
return;
|
|
342
|
+
|
|
343
|
+
// current integer cell we are in
|
|
344
|
+
const pos = posStart.floor();
|
|
345
|
+
|
|
346
|
+
// normalize ray direction
|
|
347
|
+
const dirX = dx / totalLength;
|
|
348
|
+
const dirY = dy / totalLength;
|
|
349
|
+
|
|
350
|
+
// step direction in grid
|
|
351
|
+
const stepX = sign(dirX);
|
|
352
|
+
const stepY = sign(dirY);
|
|
353
|
+
|
|
354
|
+
// distance along the ray to cross one full cell in X or Y
|
|
355
|
+
const tDeltaX = dirX ? abs(1 / dirX) : Infinity;
|
|
356
|
+
const tDeltaY = dirY ? abs(1 / dirY) : Infinity;
|
|
357
|
+
|
|
358
|
+
// distance along the ray from start to the first grid boundary
|
|
359
|
+
const nextGridX = stepX > 0 ? pos.x + 1 : pos.x;
|
|
360
|
+
const nextGridY = stepY > 0 ? pos.y + 1 : pos.y;
|
|
361
|
+
const tMaxX = dirX ? (nextGridX - posStart.x) / dirX : Infinity;
|
|
362
|
+
const tMaxY = dirY ? (nextGridY - posStart.y) / dirY : Infinity;
|
|
363
|
+
|
|
364
|
+
// use line drawing algorithm to test for collisions
|
|
365
|
+
let t = 0, tX = tMaxX, tY = tMaxY, wasX = tDeltaX < tDeltaY;
|
|
366
|
+
while (t < totalLength)
|
|
367
|
+
{
|
|
368
|
+
if (testFunction(pos))
|
|
369
|
+
{
|
|
370
|
+
// set hit point
|
|
371
|
+
const hitPos = vec2(posStart.x + dirX*t, posStart.y + dirY*t);
|
|
372
|
+
|
|
373
|
+
// move inside of tile if on positive edge
|
|
374
|
+
const e = 1e-9;
|
|
375
|
+
if (wasX)
|
|
376
|
+
{
|
|
377
|
+
if (stepX < 0)
|
|
378
|
+
hitPos.x -= e;
|
|
379
|
+
}
|
|
380
|
+
if (stepY < 0)
|
|
381
|
+
hitPos.y -= e;
|
|
382
|
+
|
|
383
|
+
// set normal
|
|
384
|
+
if (normal)
|
|
385
|
+
wasX ? normal.set(-stepX,0) : normal.set(0,-stepY);
|
|
386
|
+
return hitPos;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// advance to the next grid boundary
|
|
390
|
+
if (wasX = tX < tY)
|
|
391
|
+
{
|
|
392
|
+
pos.x += stepX;
|
|
393
|
+
t = tX;
|
|
394
|
+
tX += tDeltaX;
|
|
395
|
+
}
|
|
396
|
+
else
|
|
397
|
+
{
|
|
398
|
+
pos.y += stepY;
|
|
399
|
+
t = tY;
|
|
400
|
+
tY += tDeltaY;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
253
405
|
///////////////////////////////////////////////////////////////////////////////
|
|
254
406
|
|
|
255
407
|
/** Random global functions
|
|
@@ -268,7 +420,7 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
268
420
|
* @param {number} [valueB]
|
|
269
421
|
* @return {number}
|
|
270
422
|
* @memberof Random */
|
|
271
|
-
function randInt(valueA, valueB=0) { return
|
|
423
|
+
function randInt(valueA, valueB=0) { return floor(rand(valueA,valueB)); }
|
|
272
424
|
|
|
273
425
|
/** Randomly returns true or false given the chance of true passed in
|
|
274
426
|
* @param {number} [chance]
|
|
@@ -347,7 +499,7 @@ class RandomGenerator
|
|
|
347
499
|
* @param {number} valueA
|
|
348
500
|
* @param {number} [valueB]
|
|
349
501
|
* @return {number} */
|
|
350
|
-
int(valueA, valueB=0) { return
|
|
502
|
+
int(valueA, valueB=0) { return floor(this.float(valueA, valueB)); }
|
|
351
503
|
|
|
352
504
|
/** Randomly returns true or false given the chance of true passed in
|
|
353
505
|
* @param {number} [chance]
|
|
@@ -374,6 +526,39 @@ class RandomGenerator
|
|
|
374
526
|
* @return {Vector2} */
|
|
375
527
|
vec2(valueA=1, valueB=0)
|
|
376
528
|
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
529
|
+
|
|
530
|
+
/** Returns a random color between the two passed in colors, combine components if linear
|
|
531
|
+
* @param {Color} [colorA=(1,1,1,1)]
|
|
532
|
+
* @param {Color} [colorB=(0,0,0,1)]
|
|
533
|
+
* @param {boolean} [linear]
|
|
534
|
+
* @return {Color} */
|
|
535
|
+
randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
536
|
+
{
|
|
537
|
+
return linear ? colorA.lerp(colorB, this.float()) :
|
|
538
|
+
new Color(
|
|
539
|
+
this.float(colorA.r,colorB.r),
|
|
540
|
+
this.float(colorA.g,colorB.g),
|
|
541
|
+
this.float(colorA.b,colorB.b),
|
|
542
|
+
this.float(colorA.a,colorB.a));
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/** Returns a new color that has each component randomly adjusted
|
|
546
|
+
* @param {Color} color
|
|
547
|
+
* @param {number} [amount]
|
|
548
|
+
* @param {number} [alphaAmount]
|
|
549
|
+
* @return {Color} */
|
|
550
|
+
mutateColor(color, amount=.05, alphaAmount=0)
|
|
551
|
+
{
|
|
552
|
+
ASSERT_NUMBER_VALID(amount);
|
|
553
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
554
|
+
return new Color
|
|
555
|
+
(
|
|
556
|
+
color.r + this.float(amount, -amount),
|
|
557
|
+
color.g + this.float(amount, -amount),
|
|
558
|
+
color.b + this.float(amount, -amount),
|
|
559
|
+
color.a + this.float(alphaAmount, -alphaAmount)
|
|
560
|
+
).clamp();
|
|
561
|
+
}
|
|
377
562
|
}
|
|
378
563
|
|
|
379
564
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -525,7 +710,7 @@ class Vector2
|
|
|
525
710
|
|
|
526
711
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
527
712
|
* @return {number} */
|
|
528
|
-
angle() { return
|
|
713
|
+
angle() { return atan2(this.x, this.y); }
|
|
529
714
|
|
|
530
715
|
/** Sets this vector with clockwise angle and length passed in
|
|
531
716
|
* @param {number} [angle]
|
|
@@ -535,8 +720,8 @@ class Vector2
|
|
|
535
720
|
{
|
|
536
721
|
ASSERT_NUMBER_VALID(angle);
|
|
537
722
|
ASSERT_NUMBER_VALID(length);
|
|
538
|
-
this.x = length*
|
|
539
|
-
this.y = length*
|
|
723
|
+
this.x = length*sin(angle);
|
|
724
|
+
this.y = length*cos(angle);
|
|
540
725
|
return this;
|
|
541
726
|
}
|
|
542
727
|
|
|
@@ -546,7 +731,7 @@ class Vector2
|
|
|
546
731
|
rotate(angle)
|
|
547
732
|
{
|
|
548
733
|
ASSERT_NUMBER_VALID(angle);
|
|
549
|
-
const c =
|
|
734
|
+
const c = cos(-angle), s = sin(-angle);
|
|
550
735
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
551
736
|
}
|
|
552
737
|
|
|
@@ -578,7 +763,7 @@ class Vector2
|
|
|
578
763
|
|
|
579
764
|
/** Returns a copy of this vector with each axis floored
|
|
580
765
|
* @return {Vector2} */
|
|
581
|
-
floor() { return new Vector2(
|
|
766
|
+
floor() { return new Vector2(floor(this.x), floor(this.y)); }
|
|
582
767
|
|
|
583
768
|
/** Returns new vec2 with modded values
|
|
584
769
|
* @param {number} [divisor]
|
|
@@ -826,7 +1011,6 @@ class Color
|
|
|
826
1011
|
* @return {string} */
|
|
827
1012
|
toString(useAlpha = true)
|
|
828
1013
|
{
|
|
829
|
-
ASSERT(typeof useAlpha === 'boolean', 'Use alpha boolean is invalid.', useAlpha);
|
|
830
1014
|
if (debug && !this.isValid())
|
|
831
1015
|
return `#000`;
|
|
832
1016
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
@@ -886,67 +1070,67 @@ class Color
|
|
|
886
1070
|
/** Color - White #ffffff
|
|
887
1071
|
* @type {Color}
|
|
888
1072
|
* @memberof Utilities */
|
|
889
|
-
const WHITE =
|
|
1073
|
+
const WHITE = debugProtectConstant(rgb());
|
|
890
1074
|
|
|
891
1075
|
/** Color - Clear White #757474ff with 0 alpha
|
|
892
1076
|
* @type {Color}
|
|
893
1077
|
* @memberof Utilities */
|
|
894
|
-
const CLEAR_WHITE =
|
|
1078
|
+
const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
895
1079
|
|
|
896
1080
|
/** Color - Black #000000
|
|
897
1081
|
* @type {Color}
|
|
898
1082
|
* @memberof Utilities */
|
|
899
|
-
const BLACK =
|
|
1083
|
+
const BLACK = debugProtectConstant(rgb(0,0,0));
|
|
900
1084
|
|
|
901
1085
|
/** Color - Clear Black #000000 with 0 alpha
|
|
902
1086
|
* @type {Color}
|
|
903
1087
|
* @memberof Utilities */
|
|
904
|
-
const CLEAR_BLACK =
|
|
1088
|
+
const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
|
|
905
1089
|
|
|
906
1090
|
/** Color - Gray #808080
|
|
907
1091
|
* @type {Color}
|
|
908
1092
|
* @memberof Utilities */
|
|
909
|
-
const GRAY =
|
|
1093
|
+
const GRAY = debugProtectConstant(rgb(.5,.5,.5));
|
|
910
1094
|
|
|
911
1095
|
/** Color - Red #ff0000
|
|
912
1096
|
* @type {Color}
|
|
913
1097
|
* @memberof Utilities */
|
|
914
|
-
const RED =
|
|
1098
|
+
const RED = debugProtectConstant(rgb(1,0,0));
|
|
915
1099
|
|
|
916
1100
|
/** Color - Orange #ff8000
|
|
917
1101
|
* @type {Color}
|
|
918
1102
|
* @memberof Utilities */
|
|
919
|
-
const ORANGE =
|
|
1103
|
+
const ORANGE = debugProtectConstant(rgb(1,.5,0));
|
|
920
1104
|
|
|
921
1105
|
/** Color - Yellow #ffff00
|
|
922
1106
|
* @type {Color}
|
|
923
1107
|
* @memberof Utilities */
|
|
924
|
-
const YELLOW =
|
|
1108
|
+
const YELLOW = debugProtectConstant(rgb(1,1,0));
|
|
925
1109
|
|
|
926
1110
|
/** Color - Green #00ff00
|
|
927
1111
|
* @type {Color}
|
|
928
1112
|
* @memberof Utilities */
|
|
929
|
-
const GREEN =
|
|
1113
|
+
const GREEN = debugProtectConstant(rgb(0,1,0));
|
|
930
1114
|
|
|
931
1115
|
/** Color - Cyan #00ffff
|
|
932
1116
|
* @type {Color}
|
|
933
1117
|
* @memberof Utilities */
|
|
934
|
-
const CYAN =
|
|
1118
|
+
const CYAN = debugProtectConstant(rgb(0,1,1));
|
|
935
1119
|
|
|
936
1120
|
/** Color - Blue #0000ff
|
|
937
1121
|
* @type {Color}
|
|
938
1122
|
* @memberof Utilities */
|
|
939
|
-
const BLUE =
|
|
1123
|
+
const BLUE = debugProtectConstant(rgb(0,0,1));
|
|
940
1124
|
|
|
941
1125
|
/** Color - Purple #8000ff
|
|
942
1126
|
* @type {Color}
|
|
943
1127
|
* @memberof Utilities */
|
|
944
|
-
const PURPLE =
|
|
1128
|
+
const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
945
1129
|
|
|
946
1130
|
/** Color - Magenta #ff00ff
|
|
947
1131
|
* @type {Color}
|
|
948
1132
|
* @memberof Utilities */
|
|
949
|
-
const MAGENTA =
|
|
1133
|
+
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
950
1134
|
|
|
951
1135
|
///////////////////////////////////////////////////////////////////////////////
|
|
952
1136
|
|
|
@@ -1009,39 +1193,9 @@ class Timer
|
|
|
1009
1193
|
|
|
1010
1194
|
/** Returns this timer expressed as a string
|
|
1011
1195
|
* @return {string} */
|
|
1012
|
-
toString() { return this.isSet() ?
|
|
1196
|
+
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
1013
1197
|
|
|
1014
1198
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1015
1199
|
* @return {number} */
|
|
1016
1200
|
valueOf() { return this.get(); }
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
1020
|
-
// Helper functions used by the engine
|
|
1021
|
-
|
|
1022
|
-
// make color constants immutable with debug assertions
|
|
1023
|
-
function protectEngineConstant(obj)
|
|
1024
|
-
{
|
|
1025
|
-
if (debug)
|
|
1026
|
-
{
|
|
1027
|
-
// get properties and store original values
|
|
1028
|
-
const props = Object.keys(obj), values = {};
|
|
1029
|
-
props.forEach(prop => values[prop] = obj[prop]);
|
|
1030
|
-
|
|
1031
|
-
// replace with getters/setters that assert
|
|
1032
|
-
props.forEach(prop =>
|
|
1033
|
-
{
|
|
1034
|
-
Object.defineProperty(obj, prop, {
|
|
1035
|
-
get: () => values[prop],
|
|
1036
|
-
set: (value) =>
|
|
1037
|
-
{
|
|
1038
|
-
ASSERT(false, `Cannot modify engine constant. Attempted to set constant (${obj}) property '${prop}' to '${value}'.`);
|
|
1039
|
-
},
|
|
1040
|
-
enumerable: true
|
|
1041
|
-
});
|
|
1042
|
-
});
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
// freeze the object to prevent adding new properties
|
|
1046
|
-
return Object.freeze(obj);
|
|
1047
1201
|
}
|
package/src/engineWebGL.js
CHANGED
|
@@ -29,7 +29,7 @@ let glContext;
|
|
|
29
29
|
let glAntialias = true;
|
|
30
30
|
|
|
31
31
|
// WebGL internal variables not exposed to documentation
|
|
32
|
-
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos;
|
|
32
|
+
let glShader, glPolyShader, glPolyMode, glAdditive, glBatchAdditive, glActiveTexture, glArrayBuffer, glGeometryBuffer, glPositionData, glColorData, glBatchCount, glTextureInfos, glCanBeEnabled = true;
|
|
33
33
|
|
|
34
34
|
// WebGL internal constants
|
|
35
35
|
const gl_ARRAY_BUFFER_SIZE = 5e5;
|
|
@@ -48,7 +48,11 @@ function glInit()
|
|
|
48
48
|
// keep set of texture infos so they can be restored if context is lost
|
|
49
49
|
glTextureInfos = new Set;
|
|
50
50
|
|
|
51
|
-
if (!glEnable || headlessMode)
|
|
51
|
+
if (!glEnable || headlessMode)
|
|
52
|
+
{
|
|
53
|
+
glCanBeEnabled = false;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
52
56
|
|
|
53
57
|
// create the canvas and textures
|
|
54
58
|
glCanvas = document.createElement('canvas');
|
|
@@ -59,10 +63,11 @@ function glInit()
|
|
|
59
63
|
console.warn('WebGL2 not supported, falling back to 2D canvas rendering!');
|
|
60
64
|
glCanvas = glContext = undefined;
|
|
61
65
|
glEnable = false;
|
|
66
|
+
glCanBeEnabled = false;
|
|
62
67
|
return;
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
//
|
|
70
|
+
// attach the WebGL canvas
|
|
66
71
|
const rootElement = mainCanvas.parentElement;
|
|
67
72
|
rootElement.appendChild(glCanvas);
|
|
68
73
|
|
|
@@ -85,7 +90,7 @@ function glInit()
|
|
|
85
90
|
});
|
|
86
91
|
glCanvas.addEventListener('webglcontextrestored', ()=>
|
|
87
92
|
{
|
|
88
|
-
glEnable = true; //
|
|
93
|
+
glEnable = true; // re-enable WebGL rendering
|
|
89
94
|
glCanvas.style.display = ''; // show the gl canvas
|
|
90
95
|
LOG('WebGL context restored, reinitializing...');
|
|
91
96
|
|
|
@@ -237,8 +242,8 @@ function glPreRender()
|
|
|
237
242
|
const s = vec2(2*cameraScale).divide(mainCanvasSize);
|
|
238
243
|
const rotatedCam = cameraPos.rotate(-cameraAngle);
|
|
239
244
|
const p = vec2(-1).subtract(rotatedCam.multiply(s));
|
|
240
|
-
const ca =
|
|
241
|
-
const sa =
|
|
245
|
+
const ca = cos(cameraAngle);
|
|
246
|
+
const sa = sin(cameraAngle);
|
|
242
247
|
const transform = [
|
|
243
248
|
s.x * ca, s.y * sa, 0, 0,
|
|
244
249
|
-s.x * sa, s.y * ca, 0, 0,
|
|
@@ -543,8 +548,8 @@ function glDrawPointsTransform(points, rgba, x, y, sx, sy, angle, tristrip=true)
|
|
|
543
548
|
// transform the point
|
|
544
549
|
const px = p.x*sx;
|
|
545
550
|
const py = p.y*sy;
|
|
546
|
-
const sa =
|
|
547
|
-
const ca =
|
|
551
|
+
const sa = sin(-angle);
|
|
552
|
+
const ca = cos(-angle);
|
|
548
553
|
pointsOut.push(vec2(x + ca*px - sa*py, y + sa*px + ca*py));
|
|
549
554
|
}
|
|
550
555
|
const drawPoints = tristrip ? glPolyStrip(pointsOut) : pointsOut;
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
let playerPos = vec2(), playerAngle = 0;
|
|
2
|
-
|
|
3
|
-
// a simple function to generate a level
|
|
4
|
-
const levelTest = (X,Y)=> ((X|0)**3&(Y|0)**2)%30>5;
|
|
5
|
-
|
|
6
|
-
// cast a ray and return the distance to the first wall hit (0-1)
|
|
7
|
-
function raycast(pos, angle)
|
|
8
|
-
{
|
|
9
|
-
const maxDistance = 40, delta = .05;
|
|
10
|
-
const rayX = delta*Math.sin(angle);
|
|
11
|
-
const rayY = delta*Math.cos(angle);
|
|
12
|
-
let posX = pos.x, posY = pos.y;
|
|
13
|
-
for (let distance = 0; distance < maxDistance; distance += delta)
|
|
14
|
-
{
|
|
15
|
-
if (levelTest(posX, posY))
|
|
16
|
-
return distance/maxDistance;
|
|
17
|
-
posX += rayX;
|
|
18
|
-
posY += rayY;
|
|
19
|
-
}
|
|
20
|
-
return 1;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function gameUpdate()
|
|
24
|
-
{
|
|
25
|
-
// update camera angle with mouse pointer lock
|
|
26
|
-
if (mouseWasPressed(0))
|
|
27
|
-
pointerLockRequest();
|
|
28
|
-
if (keyWasPressed('Escape'))
|
|
29
|
-
pointerLockExit()
|
|
30
|
-
if (pointerLockIsActive() || isTouchDevice)
|
|
31
|
-
playerAngle += mouseDelta.x * .003;
|
|
32
|
-
|
|
33
|
-
// update player movement, prevent walking through walls
|
|
34
|
-
let velocity = keyDirection();
|
|
35
|
-
if (velocity.y && mouseIsDown(0))
|
|
36
|
-
velocity.y = 1; // forward movement with mouse
|
|
37
|
-
velocity = velocity.rotate(playerAngle).scale(.1);
|
|
38
|
-
const newPos = playerPos.add(velocity);
|
|
39
|
-
if (!levelTest(newPos.x, newPos.y))
|
|
40
|
-
playerPos = newPos;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function gameRender()
|
|
44
|
-
{
|
|
45
|
-
{
|
|
46
|
-
// draw horizontal slizes to create floor and ceiling
|
|
47
|
-
const h = 9;
|
|
48
|
-
let pos = vec2(), size = vec2(39, .15), color = rgb();
|
|
49
|
-
for (let y=-h; y<h; y+=.1)
|
|
50
|
-
{
|
|
51
|
-
const p = 1.01-abs(y/h)
|
|
52
|
-
pos.y = y;
|
|
53
|
-
color.setHSLA(.1, y>0?0:.5, .7-p*.7);
|
|
54
|
-
drawRect(pos, size, color);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
{
|
|
58
|
-
// draw vertical slices to create the walls
|
|
59
|
-
const w = 20;
|
|
60
|
-
const pos = vec2(), size = vec2(.15), color = rgb();
|
|
61
|
-
for (let x=-w; x<w; x+=.1)
|
|
62
|
-
{
|
|
63
|
-
const p = raycast(playerPos, playerAngle + x/w);
|
|
64
|
-
pos.x = x;
|
|
65
|
-
size.y = .4/p;
|
|
66
|
-
color.setHSLA(.6, .5, .7-p*.7);
|
|
67
|
-
drawRect(pos, size, color);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// draw instructions and pointer lock status
|
|
71
|
-
const instructions = pointerLockIsActive() ?
|
|
72
|
-
'Mouse Control Active - ESC To Exit' :
|
|
73
|
-
'Click To Enable Mouse Control';
|
|
74
|
-
const textPos = vec2(mainCanvasSize.x/2, 50);
|
|
75
|
-
const textSize = 24;
|
|
76
|
-
const textColor = pointerLockIsActive() ? GREEN : WHITE;
|
|
77
|
-
if (!isTouchDevice)
|
|
78
|
-
drawTextScreen(instructions, textPos, textSize, textColor);
|
|
79
|
-
}
|