littlejsengine 1.14.19 → 1.14.23
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 +118 -18
- package/dist/littlejs.esm.js +506 -199
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +493 -199
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +468 -200
- 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 +12 -6
- package/examples/module/game.js +5 -7
- package/examples/particles/index.html +6 -9
- package/examples/platformer/gameCharacter.js +1 -1
- package/examples/platformer/gameEffects.js +7 -2
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/shorts/animation.js +2 -2
- package/examples/shorts/base.html +4 -1
- package/examples/shorts/cameraDrag.js +22 -0
- package/examples/shorts/debugDraw.js +38 -0
- package/examples/shorts/fps.js +90 -0
- package/examples/shorts/helloWorld.js +1 -1
- package/examples/shorts/hillGlideGame.js +1 -1
- package/examples/shorts/landerGame.js +1 -1
- package/examples/shorts/postProcess.js +1 -1
- package/examples/shorts/sequencer.js +3 -3
- package/examples/shorts/spaceGame.js +1 -1
- package/examples/shorts/spriteAtlas.js +6 -6
- 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 -1
- 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/src/engine.js +3 -3
- package/src/engineAudio.js +13 -13
- package/src/engineDebug.js +27 -0
- package/src/engineDraw.js +68 -20
- package/src/engineExport.js +13 -0
- package/src/engineInput.js +14 -16
- package/src/engineObject.js +31 -6
- package/src/engineParticles.js +36 -15
- package/src/engineRelease.js +2 -1
- package/src/engineSettings.js +20 -1
- package/src/engineTileLayer.js +62 -59
- package/src/engineUtilities.js +206 -58
- 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,96 @@ 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
|
+
* @callback LineTestFunction - Checks if a position is colliding
|
|
310
|
+
* @param {Vector2} pos
|
|
311
|
+
* @memberof Draw
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Casts a ray and returns position of the first collision found, or undefined if none are found
|
|
316
|
+
* @param {Vector2} posStart
|
|
317
|
+
* @param {Vector2} posEnd
|
|
318
|
+
* @param {LineTestFunction} testFunction - Check if colliding
|
|
319
|
+
* @param {Vector2} [normal] - Optional vector to store the normal
|
|
320
|
+
* @return {Vector2|undefined} - Position of the collision or undefined if none found
|
|
321
|
+
* @memberof Utilities */
|
|
322
|
+
function lineTest(posStart, posEnd, testFunction, normal)
|
|
323
|
+
{
|
|
324
|
+
ASSERT(isVector2(posStart), 'posStart must be a vec2');
|
|
325
|
+
ASSERT(isVector2(posEnd), 'posEnd must be a vec2');
|
|
326
|
+
ASSERT(typeof testFunction === 'function', 'testFunction must be a function');
|
|
327
|
+
ASSERT(!normal || isVector2(normal), 'normal must be a vec2');
|
|
328
|
+
|
|
329
|
+
// get ray direction and length
|
|
330
|
+
const dx = posEnd.x - posStart.x;
|
|
331
|
+
const dy = posEnd.y - posStart.y;
|
|
332
|
+
const totalLength = hypot(dx, dy);
|
|
333
|
+
if (!totalLength)
|
|
334
|
+
return;
|
|
335
|
+
|
|
336
|
+
// current integer cell we are in
|
|
337
|
+
const pos = posStart.floor();
|
|
338
|
+
|
|
339
|
+
// normalize ray direction
|
|
340
|
+
const dirX = dx / totalLength;
|
|
341
|
+
const dirY = dy / totalLength;
|
|
342
|
+
|
|
343
|
+
// step direction in grid
|
|
344
|
+
const stepX = sign(dirX);
|
|
345
|
+
const stepY = sign(dirY);
|
|
346
|
+
|
|
347
|
+
// distance along the ray to cross one full cell in X or Y
|
|
348
|
+
const tDeltaX = dirX ? abs(1 / dirX) : Infinity;
|
|
349
|
+
const tDeltaY = dirY ? abs(1 / dirY) : Infinity;
|
|
350
|
+
|
|
351
|
+
// distance along the ray from start to the first grid boundary
|
|
352
|
+
const nextGridX = stepX > 0 ? pos.x + 1 : pos.x;
|
|
353
|
+
const nextGridY = stepY > 0 ? pos.y + 1 : pos.y;
|
|
354
|
+
const tMaxX = dirX ? (nextGridX - posStart.x) / dirX : Infinity;
|
|
355
|
+
const tMaxY = dirY ? (nextGridY - posStart.y) / dirY : Infinity;
|
|
356
|
+
|
|
357
|
+
// use line drawing algorithm to test for collisions
|
|
358
|
+
let t = 0, tX = tMaxX, tY = tMaxY, wasX = tDeltaX < tDeltaY;
|
|
359
|
+
while (t < totalLength)
|
|
360
|
+
{
|
|
361
|
+
if (testFunction(pos))
|
|
362
|
+
{
|
|
363
|
+
// set hit point
|
|
364
|
+
const hitPos = vec2(posStart.x + dirX*t, posStart.y + dirY*t);
|
|
365
|
+
|
|
366
|
+
// move inside of tile if on positive edge
|
|
367
|
+
const e = 1e-9;
|
|
368
|
+
if (wasX)
|
|
369
|
+
{
|
|
370
|
+
if (stepX < 0)
|
|
371
|
+
hitPos.x -= e;
|
|
372
|
+
}
|
|
373
|
+
if (stepY < 0)
|
|
374
|
+
hitPos.y -= e;
|
|
375
|
+
|
|
376
|
+
// set normal
|
|
377
|
+
if (normal)
|
|
378
|
+
wasX ? normal.set(-stepX,0) : normal.set(0,-stepY);
|
|
379
|
+
return hitPos;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// advance to the next grid boundary
|
|
383
|
+
if (wasX = tX < tY)
|
|
384
|
+
{
|
|
385
|
+
pos.x += stepX;
|
|
386
|
+
t = tX;
|
|
387
|
+
tX += tDeltaX;
|
|
388
|
+
}
|
|
389
|
+
else
|
|
390
|
+
{
|
|
391
|
+
pos.y += stepY;
|
|
392
|
+
t = tY;
|
|
393
|
+
tY += tDeltaY;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
253
398
|
///////////////////////////////////////////////////////////////////////////////
|
|
254
399
|
|
|
255
400
|
/** Random global functions
|
|
@@ -268,7 +413,7 @@ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valu
|
|
|
268
413
|
* @param {number} [valueB]
|
|
269
414
|
* @return {number}
|
|
270
415
|
* @memberof Random */
|
|
271
|
-
function randInt(valueA, valueB=0) { return
|
|
416
|
+
function randInt(valueA, valueB=0) { return floor(rand(valueA,valueB)); }
|
|
272
417
|
|
|
273
418
|
/** Randomly returns true or false given the chance of true passed in
|
|
274
419
|
* @param {number} [chance]
|
|
@@ -347,7 +492,7 @@ class RandomGenerator
|
|
|
347
492
|
* @param {number} valueA
|
|
348
493
|
* @param {number} [valueB]
|
|
349
494
|
* @return {number} */
|
|
350
|
-
int(valueA, valueB=0) { return
|
|
495
|
+
int(valueA, valueB=0) { return floor(this.float(valueA, valueB)); }
|
|
351
496
|
|
|
352
497
|
/** Randomly returns true or false given the chance of true passed in
|
|
353
498
|
* @param {number} [chance]
|
|
@@ -374,6 +519,39 @@ class RandomGenerator
|
|
|
374
519
|
* @return {Vector2} */
|
|
375
520
|
vec2(valueA=1, valueB=0)
|
|
376
521
|
{ return vec2(this.float(valueA, valueB), this.float(valueA, valueB)); }
|
|
522
|
+
|
|
523
|
+
/** Returns a random color between the two passed in colors, combine components if linear
|
|
524
|
+
* @param {Color} [colorA=(1,1,1,1)]
|
|
525
|
+
* @param {Color} [colorB=(0,0,0,1)]
|
|
526
|
+
* @param {boolean} [linear]
|
|
527
|
+
* @return {Color} */
|
|
528
|
+
randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear=false)
|
|
529
|
+
{
|
|
530
|
+
return linear ? colorA.lerp(colorB, this.float()) :
|
|
531
|
+
new Color(
|
|
532
|
+
this.float(colorA.r,colorB.r),
|
|
533
|
+
this.float(colorA.g,colorB.g),
|
|
534
|
+
this.float(colorA.b,colorB.b),
|
|
535
|
+
this.float(colorA.a,colorB.a));
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** Returns a new color that has each component randomly adjusted
|
|
539
|
+
* @param {Color} color
|
|
540
|
+
* @param {number} [amount]
|
|
541
|
+
* @param {number} [alphaAmount]
|
|
542
|
+
* @return {Color} */
|
|
543
|
+
mutateColor(color, amount=.05, alphaAmount=0)
|
|
544
|
+
{
|
|
545
|
+
ASSERT_NUMBER_VALID(amount);
|
|
546
|
+
ASSERT_NUMBER_VALID(alphaAmount);
|
|
547
|
+
return new Color
|
|
548
|
+
(
|
|
549
|
+
color.r + this.float(amount, -amount),
|
|
550
|
+
color.g + this.float(amount, -amount),
|
|
551
|
+
color.b + this.float(amount, -amount),
|
|
552
|
+
color.a + this.float(alphaAmount, -alphaAmount)
|
|
553
|
+
).clamp();
|
|
554
|
+
}
|
|
377
555
|
}
|
|
378
556
|
|
|
379
557
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -525,7 +703,7 @@ class Vector2
|
|
|
525
703
|
|
|
526
704
|
/** Returns the clockwise angle of this vector, up is angle 0
|
|
527
705
|
* @return {number} */
|
|
528
|
-
angle() { return
|
|
706
|
+
angle() { return atan2(this.x, this.y); }
|
|
529
707
|
|
|
530
708
|
/** Sets this vector with clockwise angle and length passed in
|
|
531
709
|
* @param {number} [angle]
|
|
@@ -535,8 +713,8 @@ class Vector2
|
|
|
535
713
|
{
|
|
536
714
|
ASSERT_NUMBER_VALID(angle);
|
|
537
715
|
ASSERT_NUMBER_VALID(length);
|
|
538
|
-
this.x = length*
|
|
539
|
-
this.y = length*
|
|
716
|
+
this.x = length*sin(angle);
|
|
717
|
+
this.y = length*cos(angle);
|
|
540
718
|
return this;
|
|
541
719
|
}
|
|
542
720
|
|
|
@@ -546,7 +724,7 @@ class Vector2
|
|
|
546
724
|
rotate(angle)
|
|
547
725
|
{
|
|
548
726
|
ASSERT_NUMBER_VALID(angle);
|
|
549
|
-
const c =
|
|
727
|
+
const c = cos(-angle), s = sin(-angle);
|
|
550
728
|
return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
|
|
551
729
|
}
|
|
552
730
|
|
|
@@ -578,7 +756,7 @@ class Vector2
|
|
|
578
756
|
|
|
579
757
|
/** Returns a copy of this vector with each axis floored
|
|
580
758
|
* @return {Vector2} */
|
|
581
|
-
floor() { return new Vector2(
|
|
759
|
+
floor() { return new Vector2(floor(this.x), floor(this.y)); }
|
|
582
760
|
|
|
583
761
|
/** Returns new vec2 with modded values
|
|
584
762
|
* @param {number} [divisor]
|
|
@@ -886,67 +1064,67 @@ class Color
|
|
|
886
1064
|
/** Color - White #ffffff
|
|
887
1065
|
* @type {Color}
|
|
888
1066
|
* @memberof Utilities */
|
|
889
|
-
const WHITE =
|
|
1067
|
+
const WHITE = debugProtectConstant(rgb());
|
|
890
1068
|
|
|
891
1069
|
/** Color - Clear White #757474ff with 0 alpha
|
|
892
1070
|
* @type {Color}
|
|
893
1071
|
* @memberof Utilities */
|
|
894
|
-
const CLEAR_WHITE =
|
|
1072
|
+
const CLEAR_WHITE = debugProtectConstant(rgb(1,1,1,0));
|
|
895
1073
|
|
|
896
1074
|
/** Color - Black #000000
|
|
897
1075
|
* @type {Color}
|
|
898
1076
|
* @memberof Utilities */
|
|
899
|
-
const BLACK =
|
|
1077
|
+
const BLACK = debugProtectConstant(rgb(0,0,0));
|
|
900
1078
|
|
|
901
1079
|
/** Color - Clear Black #000000 with 0 alpha
|
|
902
1080
|
* @type {Color}
|
|
903
1081
|
* @memberof Utilities */
|
|
904
|
-
const CLEAR_BLACK =
|
|
1082
|
+
const CLEAR_BLACK = debugProtectConstant(rgb(0,0,0,0));
|
|
905
1083
|
|
|
906
1084
|
/** Color - Gray #808080
|
|
907
1085
|
* @type {Color}
|
|
908
1086
|
* @memberof Utilities */
|
|
909
|
-
const GRAY =
|
|
1087
|
+
const GRAY = debugProtectConstant(rgb(.5,.5,.5));
|
|
910
1088
|
|
|
911
1089
|
/** Color - Red #ff0000
|
|
912
1090
|
* @type {Color}
|
|
913
1091
|
* @memberof Utilities */
|
|
914
|
-
const RED =
|
|
1092
|
+
const RED = debugProtectConstant(rgb(1,0,0));
|
|
915
1093
|
|
|
916
1094
|
/** Color - Orange #ff8000
|
|
917
1095
|
* @type {Color}
|
|
918
1096
|
* @memberof Utilities */
|
|
919
|
-
const ORANGE =
|
|
1097
|
+
const ORANGE = debugProtectConstant(rgb(1,.5,0));
|
|
920
1098
|
|
|
921
1099
|
/** Color - Yellow #ffff00
|
|
922
1100
|
* @type {Color}
|
|
923
1101
|
* @memberof Utilities */
|
|
924
|
-
const YELLOW =
|
|
1102
|
+
const YELLOW = debugProtectConstant(rgb(1,1,0));
|
|
925
1103
|
|
|
926
1104
|
/** Color - Green #00ff00
|
|
927
1105
|
* @type {Color}
|
|
928
1106
|
* @memberof Utilities */
|
|
929
|
-
const GREEN =
|
|
1107
|
+
const GREEN = debugProtectConstant(rgb(0,1,0));
|
|
930
1108
|
|
|
931
1109
|
/** Color - Cyan #00ffff
|
|
932
1110
|
* @type {Color}
|
|
933
1111
|
* @memberof Utilities */
|
|
934
|
-
const CYAN =
|
|
1112
|
+
const CYAN = debugProtectConstant(rgb(0,1,1));
|
|
935
1113
|
|
|
936
1114
|
/** Color - Blue #0000ff
|
|
937
1115
|
* @type {Color}
|
|
938
1116
|
* @memberof Utilities */
|
|
939
|
-
const BLUE =
|
|
1117
|
+
const BLUE = debugProtectConstant(rgb(0,0,1));
|
|
940
1118
|
|
|
941
1119
|
/** Color - Purple #8000ff
|
|
942
1120
|
* @type {Color}
|
|
943
1121
|
* @memberof Utilities */
|
|
944
|
-
const PURPLE =
|
|
1122
|
+
const PURPLE = debugProtectConstant(rgb(.5,0,1));
|
|
945
1123
|
|
|
946
1124
|
/** Color - Magenta #ff00ff
|
|
947
1125
|
* @type {Color}
|
|
948
1126
|
* @memberof Utilities */
|
|
949
|
-
const MAGENTA =
|
|
1127
|
+
const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
950
1128
|
|
|
951
1129
|
///////////////////////////////////////////////////////////////////////////////
|
|
952
1130
|
|
|
@@ -1009,39 +1187,9 @@ class Timer
|
|
|
1009
1187
|
|
|
1010
1188
|
/** Returns this timer expressed as a string
|
|
1011
1189
|
* @return {string} */
|
|
1012
|
-
toString() { return this.isSet() ?
|
|
1190
|
+
toString() { return this.isSet() ? abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }
|
|
1013
1191
|
|
|
1014
1192
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1015
1193
|
* @return {number} */
|
|
1016
1194
|
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
1195
|
}
|
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
|
-
}
|