littlejsengine 1.15.2 → 1.15.3

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.
@@ -70,8 +70,9 @@ function LOG(...output) { console.log(...output); }
70
70
  * @param {number} [time]
71
71
  * @param {number} [angle]
72
72
  * @param {boolean} [fill]
73
+ * @param {boolean} [screenSpace]
73
74
  * @memberof Debug */
74
- function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
75
+ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false, screenSpace=false)
75
76
  {
76
77
  ASSERT(isVector2(pos), 'pos must be a vec2');
77
78
  ASSERT(isVector2(size), 'size must be a vec2');
@@ -86,7 +87,7 @@ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
86
87
  pos = pos.copy();
87
88
  size = size.copy();
88
89
  const timer = new Timer(time);
89
- debugPrimitives.push({pos:pos.copy(), size:size.copy(), color, timer, angle, fill});
90
+ debugPrimitives.push({pos:pos.copy(), size:size.copy(), color, timer, angle, fill, screenSpace});
90
91
  }
91
92
 
92
93
  /** Draw a debug poly in world space
@@ -96,8 +97,9 @@ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
96
97
  * @param {number} [time]
97
98
  * @param {number} [angle]
98
99
  * @param {boolean} [fill]
100
+ * @param {boolean} [screenSpace]
99
101
  * @memberof Debug */
100
- function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
102
+ function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false, screenSpace=false)
101
103
  {
102
104
  ASSERT(isVector2(pos), 'pos must be a vec2');
103
105
  ASSERT(isArray(points), 'points must be an array');
@@ -110,7 +112,7 @@ function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
110
112
  pos = pos.copy();
111
113
  points = points.map(p=>p.copy());
112
114
  const timer = new Timer(time);
113
- debugPrimitives.push({pos, points, color, timer, angle, fill});
115
+ debugPrimitives.push({pos, points, color, timer, angle, fill, screenSpace});
114
116
  }
115
117
 
116
118
  /** Draw a debug circle in world space
@@ -119,8 +121,9 @@ function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
119
121
  * @param {Color|string} [color]
120
122
  * @param {number} [time]
121
123
  * @param {boolean} [fill]
124
+ * @param {boolean} [screenSpace]
122
125
  * @memberof Debug */
123
- function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
126
+ function debugCircle(pos, size=0, color=WHITE, time=0, fill=false, screenSpace=false)
124
127
  {
125
128
  ASSERT(isVector2(pos), 'pos must be a vec2');
126
129
  ASSERT(isNumber(size), 'size must be a number');
@@ -131,7 +134,7 @@ function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
131
134
  color = color.toString();
132
135
  pos = pos.copy();
133
136
  const timer = new Timer(time);
134
- debugPrimitives.push({pos, size, color, timer, angle:0, fill});
137
+ debugPrimitives.push({pos, size, color, timer, angle:0, fill, screenSpace});
135
138
  }
136
139
 
137
140
  /** Draw a debug point in world space
@@ -139,9 +142,10 @@ function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
139
142
  * @param {Color|string} [color]
140
143
  * @param {number} [time]
141
144
  * @param {number} [angle]
145
+ * @param {boolean} [screenSpace]
142
146
  * @memberof Debug */
143
- function debugPoint(pos, color, time, angle)
144
- { debugRect(pos, undefined, color, time, angle); }
147
+ function debugPoint(pos, color, time, angle, screenSpace=false)
148
+ { debugRect(pos, undefined, color, time, angle, false, screenSpace); }
145
149
 
146
150
  /** Draw a debug line in world space
147
151
  * @param {Vector2} posA
@@ -149,8 +153,9 @@ function debugPoint(pos, color, time, angle)
149
153
  * @param {Color|string} [color]
150
154
  * @param {number} [width]
151
155
  * @param {number} [time]
156
+ * @param {boolean} [screenSpace]
152
157
  * @memberof Debug */
153
- function debugLine(posA, posB, color, width=.1, time)
158
+ function debugLine(posA, posB, color, width=.1, time, screenSpace=false)
154
159
  {
155
160
  ASSERT(isVector2(posA), 'posA must be a vec2');
156
161
  ASSERT(isVector2(posB), 'posB must be sa vec2');
@@ -158,7 +163,7 @@ function debugLine(posA, posB, color, width=.1, time)
158
163
 
159
164
  const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
160
165
  const size = vec2(width, halfDelta.length()*2);
161
- debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
166
+ debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true, screenSpace);
162
167
  }
163
168
 
164
169
  /** Draw a debug combined axis aligned bounding box in world space
@@ -167,8 +172,10 @@ function debugLine(posA, posB, color, width=.1, time)
167
172
  * @param {Vector2} posB
168
173
  * @param {Vector2} sizeB
169
174
  * @param {Color|string} [color]
175
+ * @param {number} [time]
176
+ * @param {boolean} [screenSpace]
170
177
  * @memberof Debug */
171
- function debugOverlap(posA, sizeA, posB, sizeB, color)
178
+ function debugOverlap(posA, sizeA, posB, sizeB, color, time, screenSpace=false)
172
179
  {
173
180
  ASSERT(isVector2(posA), 'posA must be a vec2');
174
181
  ASSERT(isVector2(posB), 'posB must be a vec2');
@@ -183,7 +190,7 @@ function debugOverlap(posA, sizeA, posB, sizeB, color)
183
190
  max(posA.x + sizeA.x/2, posB.x + sizeB.x/2),
184
191
  max(posA.y + sizeA.y/2, posB.y + sizeB.y/2)
185
192
  );
186
- debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color);
193
+ debugRect(minPos.lerp(maxPos,.5), maxPos.subtract(minPos), color, time, 0, false, screenSpace);
187
194
  }
188
195
 
189
196
  /** Draw a debug axis aligned bounding box in world space
@@ -194,8 +201,9 @@ function debugOverlap(posA, sizeA, posB, sizeB, color)
194
201
  * @param {number} [time]
195
202
  * @param {number} [angle]
196
203
  * @param {string} [font]
204
+ * @param {boolean} [screenSpace]
197
205
  * @memberof Debug */
198
- function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monospace')
206
+ function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monospace', screenSpace=false)
199
207
  {
200
208
  ASSERT(isString(text), 'text must be a string');
201
209
  ASSERT(isVector2(pos), 'pos must be a vec2');
@@ -209,7 +217,7 @@ function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monosp
209
217
  color = color.toString();
210
218
  pos = pos.copy();
211
219
  const timer = new Timer(time);
212
- debugPrimitives.push({text, pos, size, color, timer, angle, font});
220
+ debugPrimitives.push({text, pos, size, color, timer, angle, font, screenSpace});
213
221
  }
214
222
 
215
223
  /** Clear all debug primitives in the list
@@ -419,21 +427,26 @@ function debugRender()
419
427
  {
420
428
  // draw debug primitives
421
429
  overlayContext.lineWidth = 2;
422
- const pointSize = debugPointSize * cameraScale;
423
430
  debugPrimitives.forEach(p=>
424
431
  {
425
432
  overlayContext.save();
426
433
 
427
434
  // create canvas transform from world space to screen space
428
- const pos = worldToScreen(p.pos);
435
+ // without scaling because we want consistent pixel sizes
436
+ let pos = p.pos, scale = 1, angle = p.angle;
437
+ if (!p.screenSpace)
438
+ {
439
+ pos = worldToScreen(p.pos);
440
+ scale = cameraScale;
441
+ angle -= cameraAngle;
442
+ }
429
443
  overlayContext.translate(pos.x|0, pos.y|0);
430
- overlayContext.rotate(p.angle);
444
+ overlayContext.rotate(angle);
431
445
  overlayContext.scale(1, p.text ? 1 : -1);
432
446
  overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
433
-
434
447
  if (p.text !== undefined)
435
448
  {
436
- overlayContext.font = p.size*cameraScale + 'px '+ p.font;
449
+ overlayContext.font = p.size*scale + 'px '+ p.font;
437
450
  overlayContext.textAlign = 'center';
438
451
  overlayContext.textBaseline = 'middle';
439
452
  overlayContext.fillText(p.text, 0, 0);
@@ -444,7 +457,7 @@ function debugRender()
444
457
  overlayContext.beginPath();
445
458
  for (const point of p.points)
446
459
  {
447
- const p2 = point.scale(cameraScale).floor();
460
+ const p2 = point.scale(scale).floor();
448
461
  overlayContext.lineTo(p2.x, p2.y);
449
462
  }
450
463
  overlayContext.closePath();
@@ -454,13 +467,14 @@ function debugRender()
454
467
  else if (p.size === 0 || (p.size.x === 0 && p.size.y === 0))
455
468
  {
456
469
  // point
470
+ const pointSize = debugPointSize * scale;
457
471
  overlayContext.fillRect(-pointSize/2, -1, pointSize, 3);
458
472
  overlayContext.fillRect(-1, -pointSize/2, 3, pointSize);
459
473
  }
460
474
  else if (p.size.x !== undefined)
461
475
  {
462
476
  // rect
463
- const s = p.size.scale(cameraScale).floor();
477
+ const s = p.size.scale(scale).floor();
464
478
  const w = s.x, h = s.y;
465
479
  p.fill && overlayContext.fillRect(-w/2|0, -h/2|0, w, h);
466
480
  overlayContext.strokeRect(-w/2|0, -h/2|0, w, h);
@@ -469,7 +483,7 @@ function debugRender()
469
483
  {
470
484
  // circle
471
485
  overlayContext.beginPath();
472
- overlayContext.arc(0, 0, p.size*cameraScale/2, 0, 9);
486
+ overlayContext.arc(0, 0, p.size*scale/2, 0, 9);
473
487
  p.fill && overlayContext.fill();
474
488
  overlayContext.stroke();
475
489
  }
package/src/engineDraw.js CHANGED
@@ -250,15 +250,11 @@ function drawTile(pos, size=new Vector2(1), tileInfo, color=WHITE,
250
250
 
251
251
  const textureInfo = tileInfo && tileInfo.textureInfo;
252
252
  const bleedScale = tileInfo ? tileInfo.bleedScale : 0;
253
- if (useWebGL)
253
+ if (useWebGL && glEnable)
254
254
  {
255
255
  ASSERT(!!glContext, 'WebGL is not enabled!');
256
256
  if (screenSpace)
257
- {
258
- // convert to world space
259
- pos = screenToWorld(pos);
260
- size = size.scale(1/cameraScale);
261
- }
257
+ [pos, size, angle] = screenToWorldTransform(pos, size, angle);
262
258
  if (textureInfo)
263
259
  {
264
260
  // calculate uvs and render
@@ -346,7 +342,8 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
346
342
  ASSERT(isColor(colorTop) && isColor(colorBottom), 'color is invalid');
347
343
  ASSERT(isNumber(angle), 'angle must be a number');
348
344
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
349
- if (useWebGL)
345
+
346
+ if (useWebGL && glEnable)
350
347
  {
351
348
  ASSERT(!!glContext, 'WebGL is not enabled!');
352
349
  if (screenSpace)
@@ -354,6 +351,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
354
351
  // convert to world space
355
352
  pos = screenToWorld(pos);
356
353
  size = size.scale(1/cameraScale);
354
+ angle += cameraAngle;
357
355
  }
358
356
  // build 4 corner points for the rectangle
359
357
  const points = [], colors = [];
@@ -409,17 +407,14 @@ function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0,
409
407
  ASSERT(isVector2(pos), 'pos must be a vec2');
410
408
  ASSERT(isNumber(angle), 'angle must be a number');
411
409
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
412
- if (useWebGL)
410
+
411
+ if (useWebGL && glEnable)
413
412
  {
414
413
  ASSERT(!!glContext, 'WebGL is not enabled!');
415
- let scale = 1;
414
+ let size = vec2(1);
416
415
  if (screenSpace)
417
- {
418
- // convert to world space
419
- pos = screenToWorld(pos);
420
- scale = 1/cameraScale;
421
- }
422
- glDrawOutlineTransform(points, color.rgbaInt(), width, pos.x, pos.y, scale, scale, angle, wrap);
416
+ [pos, size, angle] = screenToWorldTransform(pos, size, angle);
417
+ glDrawOutlineTransform(points, color.rgbaInt(), width, pos.x, pos.y, size.x, size.y, angle, wrap);
423
418
  }
424
419
  else
425
420
  {
@@ -515,19 +510,15 @@ function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(),
515
510
  ASSERT(isNumber(angle), 'angle must be a number');
516
511
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
517
512
 
518
- if (useWebGL)
513
+ if (useWebGL && glEnable)
519
514
  {
520
515
  ASSERT(!!glContext, 'WebGL is not enabled!');
521
- let scale = 1;
516
+ let size = vec2(1);
522
517
  if (screenSpace)
523
- {
524
- // convert to world space
525
- pos = screenToWorld(pos);
526
- scale = 1/cameraScale;
527
- }
528
- glDrawPointsTransform(points, color.rgbaInt(), pos.x, pos.y, scale, scale, angle);
518
+ [pos, size, angle] = screenToWorldTransform(pos, size, angle);
519
+ glDrawPointsTransform(points, color.rgbaInt(), pos.x, pos.y, size.x, size.y, angle);
529
520
  if (lineWidth > 0)
530
- glDrawOutlineTransform(points, lineColor.rgbaInt(), lineWidth, pos.x, pos.y, scale, scale, angle);
521
+ glDrawOutlineTransform(points, lineColor.rgbaInt(), lineWidth, pos.x, pos.y, size.x, size.y, angle);
531
522
  }
532
523
  else
533
524
  {
@@ -570,7 +561,7 @@ function drawEllipse(pos, size=vec2(1), color=WHITE, angle=0, lineWidth=0, lineC
570
561
  ASSERT(lineWidth >= 0 && lineWidth < size.x && lineWidth < size.y, 'invalid lineWidth');
571
562
  ASSERT(!context || !useWebGL, 'context only supported in canvas 2D mode');
572
563
 
573
- if (useWebGL)
564
+ if (useWebGL && glEnable)
574
565
  {
575
566
  // draw as a regular polygon
576
567
  const sides = glCircleSides;
@@ -633,14 +624,10 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
633
624
  ASSERT(typeof drawFunction === 'function', 'drawFunction must be a function');
634
625
 
635
626
  if (!screenSpace)
636
- {
637
- // transform from world space to screen space
638
- pos = worldToScreen(pos);
639
- size = size.scale(cameraScale);
640
- }
627
+ [pos, size, angle] = worldToScreenTransform(pos, size, angle);
641
628
  context.save();
642
629
  context.translate(pos.x+.5, pos.y+.5);
643
- context.rotate(angle-cameraAngle);
630
+ context.rotate(angle);
644
631
  context.scale(mirror ? -size.x : size.x, -size.y);
645
632
  drawFunction(context);
646
633
  context.restore();
@@ -666,7 +653,14 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
666
653
  * @memberof Draw */
667
654
  function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0, context=drawContext)
668
655
  {
669
- drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
656
+ // convert to screen space
657
+ pos = worldToScreen(pos);
658
+ size *= cameraScale;
659
+ lineWidth *= cameraScale;
660
+ angle -= cameraAngle;
661
+ angle *= -1;
662
+
663
+ drawTextScreen(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
670
664
  }
671
665
 
672
666
  /** Draw text on overlay canvas in world space
@@ -825,6 +819,36 @@ function worldToScreenDelta(worldDelta)
825
819
  return new Vector2(x * cameraScale, y * -cameraScale);
826
820
  }
827
821
 
822
+ /** Convert screen space transform to world space
823
+ * @param {Vector2} screenPos
824
+ * @param {Vector2} screenSize
825
+ * @param {number} [screenAngle]
826
+ * @return {[Vector2, Vector2, number]} - [pos, size, angle]
827
+ * @memberof Draw */
828
+ function screenToWorldTransform(screenPos, screenSize, screenAngle=0)
829
+ {
830
+ return [
831
+ screenToWorld(screenPos),
832
+ screenSize.scale(1/cameraScale),
833
+ screenAngle + cameraAngle
834
+ ];
835
+ }
836
+
837
+ /** Convert world space transform to screen space
838
+ * @param {Vector2} worldPos
839
+ * @param {Vector2} worldSize
840
+ * @param {number} [worldAngle]
841
+ * @return {[Vector2, Vector2, number]} - [pos, size, angle]
842
+ * @memberof Draw */
843
+ function worldToScreenTransform(worldPos, worldSize, worldAngle=0)
844
+ {
845
+ return [
846
+ worldToScreen(worldPos),
847
+ worldSize.scale(cameraScale),
848
+ worldAngle - cameraAngle
849
+ ];
850
+ }
851
+
828
852
  /** Get the camera's visible area in world space
829
853
  * @return {Vector2}
830
854
  * @memberof Draw */
@@ -184,7 +184,7 @@ function inputUpdate()
184
184
  if (headlessMode) return;
185
185
 
186
186
  // clear input when lost focus (prevent stuck keys)
187
- if(!(touchInputEnable && isTouchDevice) && !document.hasFocus())
187
+ if (!(touchInputEnable && isTouchDevice) && !document.hasFocus())
188
188
  inputClear();
189
189
 
190
190
  // update mouse world space position and delta
@@ -448,7 +448,9 @@ class EngineObject
448
448
  {
449
449
  ASSERT(child.parent === this && this.children.includes(child));
450
450
  ASSERT(child instanceof EngineObject, 'child must be an EngineObject');
451
- this.children.splice(this.children.indexOf(child), 1);
451
+ const index = this.children.indexOf(child);
452
+ ASSERT(index >= 0, 'child not found in children array');
453
+ index >= 0 && this.children.splice(index, 1);
452
454
  child.parent = 0;
453
455
  }
454
456
 
@@ -553,7 +553,7 @@ class TileCollisionLayer extends TileLayer
553
553
  // remove from collision layers array and destroy
554
554
  const index = tileCollisionLayers.indexOf(this);
555
555
  ASSERT(index >= 0, 'tile collision layer not found in array');
556
- tileCollisionLayers.splice(index, 1);
556
+ index >= 0 && tileCollisionLayers.splice(index, 1);
557
557
  super.destroy();
558
558
  }
559
559