littlejsengine 1.6.6 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +76 -53
  2. package/build/littlejs.d.ts +154 -136
  3. package/build/littlejs.esm.js +461 -305
  4. package/build/littlejs.esm.min.js +1 -1
  5. package/build/littlejs.js +452 -291
  6. package/build/littlejs.min.js +1 -1
  7. package/build/littlejs.release.js +2317 -2168
  8. package/examples/breakout/game.js +5 -1
  9. package/examples/breakout/index.html +5 -5
  10. package/examples/breakoutTutorial/README.md +6 -6
  11. package/examples/breakoutTutorial/game.js +3 -0
  12. package/examples/breakoutTutorial/index.html +3 -3
  13. package/examples/electron/build.js +105 -0
  14. package/examples/electron/game.js +4 -2
  15. package/examples/electron/index.html +13 -2
  16. package/examples/electron/package.json +5 -3
  17. package/examples/empty/game.js +3 -1
  18. package/examples/empty/index.html +1 -1
  19. package/examples/js13k/build.bat +2 -0
  20. package/examples/js13k/build.js +110 -0
  21. package/examples/js13k/game.js +109 -0
  22. package/examples/js13k/index.html +18 -0
  23. package/examples/js13k/tiles.png +0 -0
  24. package/examples/module/game.js +9 -3
  25. package/examples/module/index.html +2 -2
  26. package/examples/particles/index.html +7 -13
  27. package/examples/platformer/game.js +5 -2
  28. package/examples/platformer/gameEffects.js +12 -12
  29. package/examples/platformer/gamePlayer.js +2 -2
  30. package/examples/platformer/index.html +8 -8
  31. package/examples/puzzle/game.js +5 -3
  32. package/examples/puzzle/index.html +4 -4
  33. package/examples/starter/build.bat +2 -78
  34. package/examples/starter/build.js +109 -0
  35. package/examples/starter/game.js +4 -1
  36. package/examples/starter/index.html +15 -18
  37. package/examples/stress/index.html +5 -7
  38. package/examples/typescript/build.bat +2 -14
  39. package/examples/typescript/build.js +31 -0
  40. package/examples/typescript/game.js +94 -89
  41. package/examples/typescript/game.ts +9 -3
  42. package/examples/typescript/index.html +2 -2
  43. package/package.json +2 -2
  44. package/src/engine.js +3 -3
  45. package/src/engineAudio.js +44 -16
  46. package/src/engineBuild.bat +2 -132
  47. package/src/engineBuild.js +156 -0
  48. package/src/engineDebug.js +25 -15
  49. package/src/engineDraw.js +62 -53
  50. package/src/engineExport.js +7 -12
  51. package/src/engineInput.js +27 -35
  52. package/src/engineObject.js +15 -13
  53. package/src/engineParticles.js +6 -6
  54. package/src/engineRelease.js +1 -3
  55. package/src/engineSettings.js +1 -0
  56. package/src/engineTileLayer.js +31 -18
  57. package/src/engineUtilities.js +161 -93
  58. package/src/engineWebGL.js +63 -27
  59. package/examples/electron/build.bat +0 -52
@@ -4,6 +4,7 @@
4
4
  * - Vector2 - fast, simple, easy 2D vector class
5
5
  * - Color - holds a rgba color with some math functions
6
6
  * - Timer - tracks time automatically
7
+ * - RandomGenerator - seeded random number generator
7
8
  * @namespace Utilities
8
9
  */
9
10
 
@@ -19,34 +20,34 @@ const PI = Math.PI;
19
20
  * @param {Number} value
20
21
  * @return {Number}
21
22
  * @memberof Utilities */
22
- function abs(a) { return a < 0 ? -a : a; }
23
+ function abs(value) { return Math.abs(value); }
23
24
 
24
25
  /** Returns lowest of two values passed in
25
26
  * @param {Number} valueA
26
27
  * @param {Number} valueB
27
28
  * @return {Number}
28
29
  * @memberof Utilities */
29
- function min(a, b) { return a < b ? a : b; }
30
+ function min(valueA, valueB) { return Math.min(valueA, valueB); }
30
31
 
31
32
  /** Returns highest of two values passed in
32
33
  * @param {Number} valueA
33
34
  * @param {Number} valueB
34
35
  * @return {Number}
35
36
  * @memberof Utilities */
36
- function max(a, b) { return a > b ? a : b; }
37
+ function max(valueA, valueB) { return Math.max(valueA, valueB); }
37
38
 
38
39
  /** Returns the sign of value passed in (also returns 1 if 0)
39
40
  * @param {Number} value
40
41
  * @return {Number}
41
42
  * @memberof Utilities */
42
- function sign(a) { return a < 0 ? -1 : 1; }
43
+ function sign(value) { return Math.sign(value); }
43
44
 
44
45
  /** Returns first parm modulo the second param, but adjusted so negative numbers work as expected
45
46
  * @param {Number} dividend
46
47
  * @param {Number} [divisor=1]
47
48
  * @return {Number}
48
49
  * @memberof Utilities */
49
- function mod(a, b=1) { return ((a % b) + b) % b; }
50
+ function mod(dividend, divisor=1) { return ((dividend % divisor) + divisor) % divisor; }
50
51
 
51
52
  /** Clamps the value beween max and min
52
53
  * @param {Number} value
@@ -54,47 +55,83 @@ function mod(a, b=1) { return ((a % b) + b) % b; }
54
55
  * @param {Number} [max=1]
55
56
  * @return {Number}
56
57
  * @memberof Utilities */
57
- function clamp(v, min=0, max=1)
58
- { return v < min ? min : v > max ? max : v; }
58
+ function clamp(value, min=0, max=1) { return value < min ? min : value > max ? max : value; }
59
59
 
60
- /** Returns what percentage the value is between max and min
60
+ /** Returns what percentage the value is between valueA and valueB
61
61
  * @param {Number} value
62
- * @param {Number} [min=0]
63
- * @param {Number} [max=1]
62
+ * @param {Number} valueA
63
+ * @param {Number} valueB
64
64
  * @return {Number}
65
65
  * @memberof Utilities */
66
- function percent(v, min=0, max=1)
67
- { return max-min ? clamp((v-min) / (max-min)) : 0; }
66
+ function percent(value, valueA, valueB)
67
+ { return valueB-valueA ? clamp((value-valueA) / (valueB-valueA)) : 0; }
68
68
 
69
- /** Linearly interpolates the percent value between max and min
69
+ /** Linearly interpolates between values passed in using percent
70
70
  * @param {Number} percent
71
- * @param {Number} [min=0]
72
- * @param {Number} [max=1]
71
+ * @param {Number} valueA
72
+ * @param {Number} valueB
73
73
  * @return {Number}
74
74
  * @memberof Utilities */
75
- function lerp(p, min=0, max=1){ return min + clamp(p) * (max-min); }
75
+ function lerp(percent, valueA, valueB) { return valueA + clamp(percent) * (valueB-valueA); }
76
+
77
+ /** Returns signed wrapped distance between the two values passed in
78
+ * @param {Number} valueA
79
+ * @param {Number} valueB
80
+ * @param {Number} [wrapSize=1]
81
+ * @returns {Number}
82
+ * @memberof Utilities */
83
+ function distanceWrap(valueA, valueB, wrapSize=1)
84
+ { const d = (valueA - valueB) % wrapSize; return d*2 % wrapSize - d; }
85
+
86
+ /** Linearly interpolates between values passed in with wrappping
87
+ * @param {Number} percent
88
+ * @param {Number} valueA
89
+ * @param {Number} valueB
90
+ * @param {Number} [wrapSize=1]
91
+ * @returns {Number}
92
+ * @memberof Utilities */
93
+ function lerpWrap(percent, valueA, valueB, wrapSize=1)
94
+ { return valueB + clamp(percent) * distanceWrap(valueA, valueB, wrapSize); }
95
+
96
+ /** Returns signed wrapped distance between the two angles passed in
97
+ * @param {Number} angleA
98
+ * @param {Number} angleB
99
+ * @returns {Number}
100
+ * @memberof Utilities */
101
+ function distanceAngle(angleA, angleB) { distanceWrap(angleA, angleB, 2*PI); }
102
+
103
+ /** Linearly interpolates between the angles passed in with wrappping
104
+ * @param {Number} percent
105
+ * @param {Number} angleA
106
+ * @param {Number} angleB
107
+ * @returns {Number}
108
+ * @memberof Utilities */
109
+ function lerpAngle(percent, angleA, angleB) { return lerpWrap(percent, angleA, angleB, 2*PI); }
76
110
 
77
111
  /** Applies smoothstep function to the percentage value
78
- * @param {Number} value
112
+ * @param {Number} percent
79
113
  * @return {Number}
80
114
  * @memberof Utilities */
81
- function smoothStep(p) { return p * p * (3 - 2 * p); }
115
+ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
82
116
 
83
117
  /** Returns the nearest power of two not less then the value
84
118
  * @param {Number} value
85
119
  * @return {Number}
86
120
  * @memberof Utilities */
87
- function nearestPowerOfTwo(v) { return 2**Math.ceil(Math.log2(v)); }
121
+ function nearestPowerOfTwo(value) { return 2**Math.ceil(Math.log2(value)); }
88
122
 
89
123
  /** Returns true if two axis aligned bounding boxes are overlapping
90
124
  * @param {Vector2} pointA - Center of box A
91
125
  * @param {Vector2} sizeA - Size of box A
92
126
  * @param {Vector2} pointB - Center of box B
93
- * @param {Vector2} [sizeB] - Size of box B
127
+ * @param {Vector2} sizeB - Size of box B
94
128
  * @return {Boolean} - True if overlapping
95
129
  * @memberof Utilities */
96
- function isOverlapping(pA, sA, pB, sB)
97
- { return abs(pA.x - pB.x)*2 < sA.x + sB.x && abs(pA.y - pB.y)*2 < sA.y + sB.y; }
130
+ function isOverlapping(pointA, sizeA, pointB, sizeB)
131
+ {
132
+ return abs(pointA.x - pointB.x)*2 < sizeA.x + sizeB.x
133
+ && abs(pointA.y - pointB.y)*2 < sizeA.y + sizeB.y;
134
+ }
98
135
 
99
136
  /** Returns an oscillating wave between 0 and amplitude with frequency of 1 Hz by default
100
137
  * @param {Number} [frequency=1] - Frequency of the wave in Hz
@@ -121,20 +158,26 @@ function formatTime(t) { return (t/60|0) + ':' + (t%60<10?'0':'') + (t%60|0); }
121
158
  * @param {Number} [valueB=0]
122
159
  * @return {Number}
123
160
  * @memberof Random */
124
- function rand(a=1, b=0) { return b + (a-b)*Math.random(); }
161
+ function rand(valueA=1, valueB=0) { return valueB + Math.random() * (valueA-valueB); }
125
162
 
126
163
  /** Returns a floored random value the two values passed in
127
- * @param {Number} [valueA=1]
164
+ * @param {Number} valueA
128
165
  * @param {Number} [valueB=0]
129
166
  * @return {Number}
130
167
  * @memberof Random */
131
- function randInt(a=1, b=0) { return rand(a,b)|0; }
168
+ function randInt(valueA, valueB=0) { return Math.floor(rand(valueA,valueB)); }
132
169
 
133
170
  /** Randomly returns either -1 or 1
134
171
  * @return {Number}
135
172
  * @memberof Random */
136
173
  function randSign() { return randInt(2) * 2 - 1; }
137
174
 
175
+ /** Returns a random Vector2 with the passed in length
176
+ * @param {Number} [length=1]
177
+ * @return {Vector2}
178
+ * @memberof Random */
179
+ function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
180
+
138
181
  /** Returns a random Vector2 within a circular shape
139
182
  * @param {Number} [radius=1]
140
183
  * @param {Number} [minRadius=0]
@@ -143,44 +186,62 @@ function randSign() { return randInt(2) * 2 - 1; }
143
186
  function randInCircle(radius=1, minRadius=0)
144
187
  { return radius > 0 ? randVector(radius * rand(minRadius / radius, 1)**.5) : new Vector2; }
145
188
 
146
- /** Returns a random Vector2 with the passed in length
147
- * @param {Number} [length=1]
148
- * @return {Vector2}
149
- * @memberof Random */
150
- function randVector(length=1) { return new Vector2().setAngle(rand(2*PI), length); }
151
-
152
189
  /** Returns a random color between the two passed in colors, combine components if linear
153
190
  * @param {Color} [colorA=Color()]
154
191
  * @param {Color} [colorB=Color(0,0,0,1)]
155
192
  * @param {Boolean} [linear]
156
193
  * @return {Color}
157
194
  * @memberof Random */
158
- function randColor(cA = new Color, cB = new Color(0,0,0,1), linear)
159
- { return linear ? cA.lerp(cB, rand()) : new Color(rand(cA.r,cB.r),rand(cA.g,cB.g),rand(cA.b,cB.b),rand(cA.a,cB.a)); }
160
-
161
- /** Seed used by the randSeeded function
162
- * @type {Number}
163
- * @default
164
- * @memberof Random */
165
- let randSeed = 1;
195
+ function randColor(colorA=new Color, colorB=new Color(0,0,0,1), linear)
196
+ {
197
+ return linear ? colorA.lerp(colorB, rand()) :
198
+ new Color(rand(colorA.r,colorB.r), rand(colorA.g,colorB.g), rand(colorA.b,colorB.b), rand(colorA.a,colorB.a));
199
+ }
166
200
 
167
- /** Set seed used by the randSeeded function, should not be 0
168
- * @param {Number} seed
169
- * @memberof Random */
170
- function setRandSeed(seed) { randSeed = seed; }
201
+ ///////////////////////////////////////////////////////////////////////////////
171
202
 
172
- /** Returns a seeded random value between the two values passed in using randSeed
173
- * @param {Number} [valueA=1]
174
- * @param {Number} [valueB=0]
175
- * @return {Number}
176
- * @memberof Random */
177
- function randSeeded(a=1, b=0)
203
+ /**
204
+ * Seeded random number generator
205
+ * - Can be used to create a deterministic random number sequence
206
+ * @example
207
+ * let r = new RandomGenerator(123); // random number generator with seed 123
208
+ * let a = r.rand(); // random value between 0 and 1
209
+ * let b = r.randInt(10); // random integer between 0 and 9
210
+ * r.seed = 123; // reset the seed
211
+ * let c = r.rand(); // the same value as a
212
+ */
213
+ class RandomGenerator
178
214
  {
179
- // xorshift algorithm
180
- randSeed ^= randSeed << 13;
181
- randSeed ^= randSeed >>> 17;
182
- randSeed ^= randSeed << 5;
183
- return b + (a-b) * abs(randSeed % 1e9) / 1e9;
215
+ /** Create a random number generator with the seed passed in
216
+ * @param {Number} seed - Starting seed */
217
+ constructor(seed)
218
+ {
219
+ /** @property {Number} - random seed */
220
+ this.seed = seed;
221
+ }
222
+
223
+ /** Returns a seeded random value between the two values passed in
224
+ * @param {Number} [valueA=1]
225
+ * @param {Number} [valueB=0]
226
+ * @return {Number} */
227
+ float(valueA=1, valueB=0)
228
+ {
229
+ // xorshift algorithm
230
+ this.seed ^= this.seed << 13;
231
+ this.seed ^= this.seed >>> 17;
232
+ this.seed ^= this.seed << 5;
233
+ return valueB + (valueA - valueB) * abs(this.seed % 1e9) / 1e9;
234
+ }
235
+
236
+ /** Returns a floored seeded random value the two values passed in
237
+ * @param {Number} valueA
238
+ * @param {Number} [valueB=0]
239
+ * @return {Number} */
240
+ int(valueA, valueB=0) { return Math.floor(this.rand(valueA, valueB)); }
241
+
242
+ /** Randomly returns either -1 or 1 deterministically
243
+ * @return {Number} */
244
+ sign() { return this.randInt(2) * 2 - 1; }
184
245
  }
185
246
 
186
247
  ///////////////////////////////////////////////////////////////////////////////
@@ -202,7 +263,7 @@ function vec2(x=0, y)
202
263
 
203
264
  /**
204
265
  * Check if object is a valid Vector2
205
- * @param {Vector2} vector
266
+ * @param {Vector2} v
206
267
  * @return {Boolean}
207
268
  * @memberof Utilities
208
269
  */
@@ -235,27 +296,27 @@ class Vector2
235
296
  copy() { return new Vector2(this.x, this.y); }
236
297
 
237
298
  /** Returns a copy of this vector plus the vector passed in
238
- * @param {Vector2} vector
299
+ * @param {Vector2} v - other vector
239
300
  * @return {Vector2} */
240
301
  add(v) { ASSERT(isVector2(v)); return new Vector2(this.x + v.x, this.y + v.y); }
241
302
 
242
303
  /** Returns a copy of this vector minus the vector passed in
243
- * @param {Vector2} vector
304
+ * @param {Vector2} v - other vector
244
305
  * @return {Vector2} */
245
306
  subtract(v) { ASSERT(isVector2(v)); return new Vector2(this.x - v.x, this.y - v.y); }
246
307
 
247
308
  /** Returns a copy of this vector times the vector passed in
248
- * @param {Vector2} vector
309
+ * @param {Vector2} v - other vector
249
310
  * @return {Vector2} */
250
311
  multiply(v) { ASSERT(isVector2(v)); return new Vector2(this.x * v.x, this.y * v.y); }
251
312
 
252
313
  /** Returns a copy of this vector divided by the vector passed in
253
- * @param {Vector2} vector
314
+ * @param {Vector2} v - other vector
254
315
  * @return {Vector2} */
255
316
  divide(v) { ASSERT(isVector2(v)); return new Vector2(this.x / v.x, this.y / v.y); }
256
317
 
257
318
  /** Returns a copy of this vector scaled by the vector passed in
258
- * @param {Number} scale
319
+ * @param {Number} s - scale
259
320
  * @return {Vector2} */
260
321
  scale(s) { ASSERT(!isVector2(s)); return new Vector2(this.x * s, this.y * s); }
261
322
 
@@ -268,12 +329,12 @@ class Vector2
268
329
  lengthSquared() { return this.x**2 + this.y**2; }
269
330
 
270
331
  /** Returns the distance from this vector to vector passed in
271
- * @param {Vector2} vector
332
+ * @param {Vector2} v - other vector
272
333
  * @return {Number} */
273
334
  distance(v) { return this.distanceSquared(v)**.5; }
274
335
 
275
336
  /** Returns the distance squared from this vector to vector passed in
276
- * @param {Vector2} vector
337
+ * @param {Vector2} v - other vector
277
338
  * @return {Number} */
278
339
  distanceSquared(v) { return (this.x - v.x)**2 + (this.y - v.y)**2; }
279
340
 
@@ -288,12 +349,12 @@ class Vector2
288
349
  clampLength(length=1) { const l = this.length(); return l > length ? this.scale(length/l) : this; }
289
350
 
290
351
  /** Returns the dot product of this and the vector passed in
291
- * @param {Vector2} vector
352
+ * @param {Vector2} v - other vector
292
353
  * @return {Number} */
293
354
  dot(v) { ASSERT(isVector2(v)); return this.x*v.x + this.y*v.y; }
294
355
 
295
356
  /** Returns the cross product of this and the vector passed in
296
- * @param {Vector2} vector
357
+ * @param {Vector2} v - other vector
297
358
  * @return {Number} */
298
359
  cross(v) { ASSERT(isVector2(v)); return this.x*v.y - this.y*v.x; }
299
360
 
@@ -305,12 +366,17 @@ class Vector2
305
366
  * @param {Number} [angle=0]
306
367
  * @param {Number} [length=1]
307
368
  * @return {Vector2} */
308
- setAngle(a=0, length=1) { this.x = length*Math.sin(a); this.y = length*Math.cos(a); return this; }
369
+ setAngle(angle=0, length=1)
370
+ { this.x = length*Math.sin(angle); this.y = length*Math.cos(angle); return this; }
309
371
 
310
372
  /** Returns copy of this vector rotated by the angle passed in
311
373
  * @param {Number} angle
312
374
  * @return {Vector2} */
313
- rotate(a) { const c = Math.cos(a), s = Math.sin(a); return new Vector2(this.x*c-this.y*s, this.x*s+this.y*c); }
375
+ rotate(angle)
376
+ {
377
+ const c = Math.cos(angle), s = Math.sin(angle);
378
+ return new Vector2(this.x*c - this.y*s, this.x*s + this.y*c);
379
+ }
314
380
 
315
381
  /** Returns the integer direction of this vector, corrosponding to multiples of 90 degree rotation (0-3)
316
382
  * @return {Number} */
@@ -329,10 +395,11 @@ class Vector2
329
395
  area() { return abs(this.x * this.y); }
330
396
 
331
397
  /** Returns a new vector that is p percent between this and the vector passed in
332
- * @param {Vector2} vector
398
+ * @param {Vector2} v - other vector
333
399
  * @param {Number} percent
334
400
  * @return {Vector2} */
335
- lerp(v, p) { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(p))); }
401
+ lerp(v, percent)
402
+ { ASSERT(isVector2(v)); return this.add(v.subtract(this).scale(clamp(percent))); }
336
403
 
337
404
  /** Returns true if this vector is within the bounds of an array size passed in
338
405
  * @param {Vector2} arraySize
@@ -350,10 +417,10 @@ class Vector2
350
417
 
351
418
  /**
352
419
  * Create a color object with RGBA values
353
- * @param {Number} [r=1]
354
- * @param {Number} [g=1]
355
- * @param {Number} [b=1]
356
- * @param {Number} [a=1]
420
+ * @param {Number} [r=1] - red
421
+ * @param {Number} [g=1] - green
422
+ * @param {Number} [b=1] - blue
423
+ * @param {Number} [a=1] - alpha
357
424
  * @return {Color}
358
425
  * @memberof Utilities
359
426
  */
@@ -361,10 +428,10 @@ function rgb(r, g, b, a) { return new Color(r, g, b, a); }
361
428
 
362
429
  /**
363
430
  * Create a color object with HSLA values
364
- * @param {Number} [h=0]
365
- * @param {Number} [s=0]
366
- * @param {Number} [l=1]
367
- * @param {Number} [a=1]
431
+ * @param {Number} [h=0] - hue
432
+ * @param {Number} [s=0] - saturation
433
+ * @param {Number} [l=1] - lightness
434
+ * @param {Number} [a=1] - alpha
368
435
  * @return {Color}
369
436
  * @memberof Utilities
370
437
  */
@@ -381,11 +448,11 @@ function hsl(h, s, l, a) { return new Color().setHSLA(h, s, l, a); }
381
448
  */
382
449
  class Color
383
450
  {
384
- /** Create a color with the components passed in, white by default
385
- * @param {Number} [red=1]
386
- * @param {Number} [green=1]
387
- * @param {Number} [blue=1]
388
- * @param {Number} [alpha=1] */
451
+ /** Create a color with the rgba components passed in, white by default
452
+ * @param {Number} [r=1] - red
453
+ * @param {Number} [g=1] - green
454
+ * @param {Number} [b=1] - blue
455
+ * @param {Number} [a=1] - alpha*/
389
456
  constructor(r=1, g=1, b=1, a=1)
390
457
  {
391
458
  /** @property {Number} - Red */
@@ -403,22 +470,22 @@ class Color
403
470
  copy() { return new Color(this.r, this.g, this.b, this.a); }
404
471
 
405
472
  /** Returns a copy of this color plus the color passed in
406
- * @param {Color} color
473
+ * @param {Color} c - other color
407
474
  * @return {Color} */
408
475
  add(c) { return new Color(this.r+c.r, this.g+c.g, this.b+c.b, this.a+c.a); }
409
476
 
410
477
  /** Returns a copy of this color minus the color passed in
411
- * @param {Color} color
478
+ * @param {Color} c - other color
412
479
  * @return {Color} */
413
480
  subtract(c) { return new Color(this.r-c.r, this.g-c.g, this.b-c.b, this.a-c.a); }
414
481
 
415
482
  /** Returns a copy of this color times the color passed in
416
- * @param {Color} color
483
+ * @param {Color} c - other color
417
484
  * @return {Color} */
418
485
  multiply(c) { return new Color(this.r*c.r, this.g*c.g, this.b*c.b, this.a*c.a); }
419
486
 
420
487
  /** Returns a copy of this color divided by the color passed in
421
- * @param {Color} color
488
+ * @param {Color} c - other color
422
489
  * @return {Color} */
423
490
  divide(c) { return new Color(this.r/c.r, this.g/c.g, this.b/c.b, this.a/c.a); }
424
491
 
@@ -426,23 +493,24 @@ class Color
426
493
  * @param {Number} scale
427
494
  * @param {Number} [alphaScale=scale]
428
495
  * @return {Color} */
429
- scale(s, a=s) { return new Color(this.r*s, this.g*s, this.b*s, this.a*a); }
496
+ scale(scale, alphaScale=scale)
497
+ { return new Color(this.r*scale, this.g*scale, this.b*scale, this.a*alphaScale); }
430
498
 
431
499
  /** Returns a copy of this color clamped to the valid range between 0 and 1
432
500
  * @return {Color} */
433
501
  clamp() { return new Color(clamp(this.r), clamp(this.g), clamp(this.b), clamp(this.a)); }
434
502
 
435
503
  /** Returns a new color that is p percent between this and the color passed in
436
- * @param {Color} color
504
+ * @param {Color} c - other color
437
505
  * @param {Number} percent
438
506
  * @return {Color} */
439
- lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
507
+ lerp(c, percent) { return this.add(c.subtract(this).scale(clamp(percent))); }
440
508
 
441
509
  /** Sets this color given a hue, saturation, lightness, and alpha
442
- * @param {Number} [hue=0]
443
- * @param {Number} [saturation=0]
444
- * @param {Number} [lightness=1]
445
- * @param {Number} [alpha=1]
510
+ * @param {Number} [h=0] - hue
511
+ * @param {Number} [s=0] - saturation
512
+ * @param {Number} [l=1] - lightness
513
+ * @param {Number} [a=1] - alpha
446
514
  * @return {Color} */
447
515
  setHSLA(h=0, s=0, l=1, a=1)
448
516
  {
@@ -583,7 +651,7 @@ class Timer
583
651
 
584
652
  /** Returns this timer expressed as a string
585
653
  * @return {String} */
586
- toString() { if (debug) { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }}
654
+ toString() { if (debug) { return this.isSet() ? Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ) : 'unset'; }}
587
655
 
588
656
  /** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
589
657
  * @return {Number} */
@@ -203,8 +203,8 @@ function glFlush()
203
203
 
204
204
  // draw all the sprites in the batch and reset the buffer
205
205
  glContext.bufferSubData(gl_ARRAY_BUFFER, 0,
206
- glPositionData.subarray(0, glBatchCount * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT));
207
- glContext.drawArrays(gl_TRIANGLES, 0, glBatchCount * gl_VERTICES_PER_QUAD);
206
+ glPositionData.subarray(0, glBatchCount * gl_INDICIES_PER_VERT));
207
+ glContext.drawArrays(gl_TRIANGLE_STRIP, 0, glBatchCount);
208
208
  glBatchCount = 0;
209
209
  glBatchAdditive = glAdditive;
210
210
  }
@@ -225,39 +225,75 @@ function glCopyToContext(context, forceDraw)
225
225
  }
226
226
 
227
227
  /** Add a sprite to the gl draw list, used by all gl draw functions
228
- * @param x
229
- * @param y
230
- * @param sizeX
231
- * @param sizeY
232
- * @param angle
233
- * @param uv0X
234
- * @param uv0Y
235
- * @param uv1X
236
- * @param uv1Y
237
- * @param rgba
238
- * @param [rgbaAdditive=0]
228
+ * @param {Number} x
229
+ * @param {Number} y
230
+ * @param {Number} sizeX
231
+ * @param {Number} sizeY
232
+ * @param {Number} angle
233
+ * @param {Number} uv0X
234
+ * @param {Number} uv0Y
235
+ * @param {Number} uv1X
236
+ * @param {Number} uv1Y
237
+ * @param {Number} rgba
238
+ * @param {Number} [rgbaAdditive=0]
239
239
  * @memberof WebGL */
240
240
  function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba, rgbaAdditive=0)
241
241
  {
242
- // flush if there is no room for more verts or if different blend mode
243
- if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
242
+ // flush if there is not enough room or if different blend mode
243
+ const vertCount = 6;
244
+ if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
244
245
  glFlush();
245
246
 
246
247
  // prepare to create the verts from size and angle
247
248
  const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
248
249
  const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
249
-
250
- // setup 2 triangles to form a quad
251
- for(let i=6, offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT; i--;)
250
+ const positionData =
251
+ [
252
+ x-cx+sy, y+cy+sx, uv0X, uv0Y,
253
+ x-cx-sy, y-cy+sx, uv0X, uv1Y,
254
+ x+cx+sy, y+cy-sx, uv1X, uv0Y,
255
+ x+cx-sy, y-cy-sx, uv1X, uv1Y,
256
+ ];
257
+
258
+ // setup 2 triangle strip quad
259
+ for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
252
260
  {
253
- const a = i-4&&i>1, b = i-5&&i-2&&i-1;
254
- glPositionData[offset++] = x + (a?-cx:cx) + (b?sy:-sy);
255
- glPositionData[offset++] = y + (b?cy:-cy) + (a?sx:-sx);
256
- glPositionData[offset++] = a ? uv0X : uv1X;
257
- glPositionData[offset++] = b ? uv0Y : uv1Y;
261
+ let j = clamp(i-1, 0, 3)*4; // degenerate tri at ends
262
+ glPositionData[offset++] = positionData[j++];
263
+ glPositionData[offset++] = positionData[j++];
264
+ glPositionData[offset++] = positionData[j++];
265
+ glPositionData[offset++] = positionData[j++];
258
266
  glColorData[offset++] = rgba;
259
267
  glColorData[offset++] = rgbaAdditive;
260
268
  }
269
+ glBatchCount += vertCount;
270
+ }
271
+
272
+ /** Add a convex polygon to the gl draw list
273
+ * @param {Array} points - Array of Vector2 points
274
+ * @param {Number} rgba - Color of the polygon
275
+ * @memberof WebGL */
276
+ function glDrawPoints(points, rgba)
277
+ {
278
+ // flush if there is not enough room or if different blend mode
279
+ const vertCount = points.length + 2;
280
+ if (glBatchCount >= gl_MAX_BATCH-vertCount || glBatchAdditive != glAdditive)
281
+ glFlush();
282
+
283
+ // setup triangle strip from list of points
284
+ for(let i = vertCount, offset = glBatchCount * gl_INDICIES_PER_VERT; i--;)
285
+ {
286
+ const j = clamp(i-1, 0, vertCount-3); // degenerate tri at ends
287
+ const h = j>>1;
288
+ const point = points[j%2? h : vertCount-3-h];
289
+ glPositionData[offset++] = point.x;
290
+ glPositionData[offset++] = point.y;
291
+ glPositionData[offset++] = 0; // uvx
292
+ glPositionData[offset++] = 0; // uvy
293
+ glColorData[offset++] = 0; // nothing to tint
294
+ glColorData[offset++] = rgba; // apply rgba via additive
295
+ }
296
+ glBatchCount += vertCount;
261
297
  }
262
298
 
263
299
  ///////////////////////////////////////////////////////////////////////////////
@@ -351,14 +387,14 @@ function glRenderPostProcess()
351
387
  glContext.uniform1i(uniformLocation('iChannel0'), 0);
352
388
  glContext.uniform1f(uniformLocation('iTime'), time);
353
389
  glContext.uniform3f(uniformLocation('iResolution'), mainCanvas.width, mainCanvas.height, 1);
354
- glContext.drawArrays(gl_TRIANGLES, 0, 3);
390
+ glContext.drawArrays(gl_TRIANGLE_STRIP, 0, 3);
355
391
  }
356
392
 
357
393
  ///////////////////////////////////////////////////////////////////////////////
358
394
  // store gl constants as integers so their name doesn't use space in minifed
359
395
  const
360
396
  gl_ONE = 1,
361
- gl_TRIANGLES = 4,
397
+ gl_TRIANGLE_STRIP = 5,
362
398
  gl_SRC_ALPHA = 770,
363
399
  gl_ONE_MINUS_SRC_ALPHA = 771,
364
400
  gl_BLEND = 3042,
@@ -389,6 +425,6 @@ gl_UNPACK_FLIP_Y_WEBGL = 37440,
389
425
  // constants for batch rendering
390
426
  gl_VERTICES_PER_QUAD = 6,
391
427
  gl_INDICIES_PER_VERT = 6,
392
- gl_MAX_BATCH = 1<<16,
428
+ gl_MAX_BATCH = 1e5,
393
429
  gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2, // vec2 * 2 + (char * 4) * 2
394
- gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTICES_PER_QUAD * gl_VERTEX_BYTE_STRIDE;
430
+ gl_VERTEX_BUFFER_SIZE = gl_MAX_BATCH * gl_VERTEX_BYTE_STRIDE;
@@ -1,52 +0,0 @@
1
- rem Simple build script for LittleJS by Frank Force
2
- rem Minfies and combines index.html and index.js and zips the result
3
-
4
- set NAME=game
5
- set BUILD_FOLDER=build
6
- set BUILD_FILENAME=index.js
7
-
8
- rem remove old files
9
- rmdir /s /q %BUILD_FOLDER%
10
- mkdir %BUILD_FOLDER%
11
- pushd %BUILD_FOLDER%
12
-
13
- rem copy engine release build
14
- type ..\..\..\build\littlejs.release.js >> %BUILD_FILENAME%
15
- echo. >> %BUILD_FILENAME%
16
-
17
- rem add your game's files to include here
18
- type ..\game.js >> %BUILD_FILENAME%
19
- echo. >> %BUILD_FILENAME%
20
-
21
- rem copy images to build folder
22
- copy ..\tiles.png tiles.png
23
-
24
- rem minify code with uglify
25
- call npx uglifyjs -o %BUILD_FILENAME% --compress --mangle -- %BUILD_FILENAME%
26
- if %ERRORLEVEL% NEQ 0 (
27
- pause
28
- exit /b %ERRORLEVEL%
29
- )
30
-
31
- rem build the html, you can add html header and footers here
32
- rem type ..\header.html >> index.html
33
- echo ^<body^>^<script^> >> index.html
34
- type %BUILD_FILENAME% >> index.html
35
- echo ^</script^> >> index.html
36
-
37
- rem delete intermediate files
38
- del %BUILD_FILENAME%
39
-
40
- rem copy elecron files to build folder
41
- copy ..\electron.js electron.js
42
- copy ..\package.json package.json
43
-
44
- popd
45
-
46
- rem build with electron
47
- call npx electron-packager ./%BUILD_FOLDER% --overwrite
48
- if %ERRORLEVEL% NEQ 0 (
49
- pause
50
- exit /b %ERRORLEVEL%
51
- )
52
- pause