littlejsengine 1.1.26 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -9
- package/build.bat +1 -1
- package/engine/engine.all.js +369 -377
- package/engine/engine.all.min.js +1 -1
- package/engine/engine.all.release.js +338 -147
- package/engine/engine.js +7 -7
- package/engine/engineAudio.js +2 -2
- package/engine/engineBuild.bat +9 -1
- package/engine/engineDebug.js +32 -231
- package/engine/engineDraw.js +130 -20
- package/engine/engineFont.png +0 -0
- package/engine/engineInput.js +32 -9
- package/engine/engineMedals.js +7 -4
- package/engine/engineObject.js +30 -11
- package/engine/engineParticles.js +5 -3
- package/engine/engineSettings.js +5 -0
- package/engine/engineTileLayer.js +17 -14
- package/engine/engineUtilities.js +50 -18
- package/engine/engineWebGL.js +53 -59
- package/engine/index.d.ts +1644 -0
- package/examples/breakout/game.js +9 -7
- package/examples/breakout/gameObjects.js +3 -3
- package/examples/breakout/index.html +3 -3
- package/examples/particles/index.html +366 -0
- package/examples/particles/tiles.png +0 -0
- package/examples/platformer/game.js +5 -5
- package/examples/platformer/gameEffects.js +10 -10
- package/examples/platformer/gameLevel.js +3 -3
- package/examples/platformer/gameObjects.js +3 -3
- package/examples/platformer/gamePlayer.js +11 -6
- package/examples/platformer/index.html +6 -6
- package/examples/platformer/tiles.png +0 -0
- package/examples/puzzle/game.js +3 -3
- package/examples/puzzle/index.html +2 -2
- package/examples/screenshot.jpg +0 -0
- package/examples/stress/index.html +4 -4
- package/game.js +4 -7
- package/index.html +13 -13
- package/package.json +8 -1
- package/engine/engineBuildSetup.bat +0 -6
package/engine/engine.all.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* LittleJS Debug System
|
|
8
8
|
* <br> - Press ~ to show debug overlay with mouse pick
|
|
9
9
|
* <br> - Number keys toggle debug functions
|
|
10
|
+
* <br> - +/- apply time scale
|
|
10
11
|
* <br> - Debug primitive rendering
|
|
11
12
|
* <br> - Save a 2d canvas as an image
|
|
12
13
|
* @namespace Debug
|
|
@@ -159,27 +160,27 @@ const debugUpdate = ()=>
|
|
|
159
160
|
{
|
|
160
161
|
if (!debug)
|
|
161
162
|
return;
|
|
162
|
-
|
|
163
|
+
|
|
163
164
|
if (keyWasPressed(192)) // ~
|
|
164
165
|
debugOverlay = !debugOverlay;
|
|
165
166
|
if (debugOverlay)
|
|
166
167
|
{
|
|
168
|
+
if (keyWasPressed(48)) // 0
|
|
169
|
+
showWatermark = !showWatermark;
|
|
167
170
|
if (keyWasPressed(49)) // 1
|
|
168
171
|
debugPhysics = !debugPhysics, debugParticles = 0;
|
|
169
172
|
if (keyWasPressed(50)) // 2
|
|
170
173
|
debugParticles = !debugParticles, debugPhysics = 0;
|
|
171
174
|
if (keyWasPressed(51)) // 3
|
|
175
|
+
debugGamepads = !debugGamepads;
|
|
176
|
+
if (keyWasPressed(52)) // 4
|
|
172
177
|
godMode = !godMode;
|
|
173
178
|
if (keyWasPressed(53)) // 5
|
|
174
179
|
debugTakeScreenshot = 1;
|
|
175
180
|
//if (keyWasPressed(54)) // 6
|
|
176
|
-
//
|
|
177
|
-
if (keyWasPressed(55)) // 7
|
|
178
|
-
debugGamepads = !debugGamepads;
|
|
181
|
+
//if (keyWasPressed(55)) // 7
|
|
179
182
|
//if (keyWasPressed(56)) // 8
|
|
180
183
|
//if (keyWasPressed(57)) // 9
|
|
181
|
-
if (keyWasPressed(48)) // 0
|
|
182
|
-
showWatermark = !showWatermark;
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -200,21 +201,20 @@ const debugRender = ()=>
|
|
|
200
201
|
|
|
201
202
|
if (debugGamepads && gamepadsEnable && navigator.getGamepads)
|
|
202
203
|
{
|
|
203
|
-
//
|
|
204
|
+
// gamepad debug display
|
|
204
205
|
const gamepads = navigator.getGamepads();
|
|
205
206
|
for (let i = gamepads.length; i--;)
|
|
206
207
|
{
|
|
207
208
|
const gamepad = gamepads[i];
|
|
208
209
|
if (gamepad)
|
|
209
210
|
{
|
|
210
|
-
// gamepad debug display
|
|
211
211
|
const stickScale = 1;
|
|
212
212
|
const buttonScale = .2;
|
|
213
213
|
const centerPos = cameraPos;
|
|
214
214
|
const sticks = stickData[i];
|
|
215
215
|
for (let j = sticks.length; j--;)
|
|
216
216
|
{
|
|
217
|
-
const drawPos = centerPos.add(vec2(j*stickScale*2,i*stickScale*3));
|
|
217
|
+
const drawPos = centerPos.add(vec2(j*stickScale*2, i*stickScale*3));
|
|
218
218
|
const stickPos = drawPos.add(sticks[j].scale(stickScale));
|
|
219
219
|
debugCircle(drawPos, stickScale, '#fff7',0,1);
|
|
220
220
|
debugLine(drawPos, stickPos, '#f00');
|
|
@@ -251,16 +251,14 @@ const debugRender = ()=>
|
|
|
251
251
|
bestObject = o
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
size.y = max(size.y, .2);
|
|
256
|
-
|
|
254
|
+
// show object info
|
|
257
255
|
const color = new Color(
|
|
258
256
|
o.collideTiles?1:0,
|
|
259
257
|
o.collideSolidObjects?1:0,
|
|
260
258
|
o.isSolid?1:0,
|
|
261
259
|
o.parent ? .2 : .5);
|
|
262
|
-
|
|
263
|
-
|
|
260
|
+
size.x = max(size.x, .2);
|
|
261
|
+
size.y = max(size.y, .2);
|
|
264
262
|
drawRect(o.pos, size, color);
|
|
265
263
|
drawRect(o.pos, size.scale(.8), o.parent ? new Color(1,1,1,.5) : new Color(0,0,0,.8));
|
|
266
264
|
o.parent && drawLine(o.pos, o.parent.pos, .1, new Color(0,0,1,.5));
|
|
@@ -275,17 +273,11 @@ const debugRender = ()=>
|
|
|
275
273
|
drawRect(mousePos.floor().add(vec2(.5)), vec2(1), new Color(0,0,1,.5));
|
|
276
274
|
drawLine(mousePos, bestObject.pos, .1, !raycastHitPos ? new Color(0,1,0,.5) : new Color(1,0,0,.5));
|
|
277
275
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
+ (bestObject.angle>0?' ':' ') + (bestObject.angle*180/PI).toFixed(1) + '°',
|
|
284
|
-
pos = pos.add(height), ...args);
|
|
285
|
-
drawText('vel = ' + printVec2(bestObject.velocity), pos = pos.add(height), ...args);
|
|
286
|
-
drawText('size = ' + printVec2(bestObject.size), pos = pos.add(height), ...args);
|
|
287
|
-
drawText('type = ' + ( bestObject.constructor.name), pos = mousePos.subtract(height), ...args);
|
|
288
|
-
drawText('collision = ' + getTileCollisionData(mousePos), pos = mousePos.subtract(height.scale(2)), ...args);
|
|
276
|
+
const debugText = 'mouse pos = ' + mousePos +
|
|
277
|
+
'\nmouse collision = ' + getTileCollisionData(mousePos) +
|
|
278
|
+
'\n\n--- object info ---\n' +
|
|
279
|
+
bestObject.toString();
|
|
280
|
+
drawTextScreen(debugText, mousePosScreen, 24, new Color, .05, undefined, undefined, 'monospace');
|
|
289
281
|
mainContext = saveContext;
|
|
290
282
|
}
|
|
291
283
|
|
|
@@ -293,16 +285,16 @@ const debugRender = ()=>
|
|
|
293
285
|
}
|
|
294
286
|
|
|
295
287
|
{
|
|
296
|
-
//
|
|
297
|
-
overlayContext.lineWidth =
|
|
288
|
+
// draw debug primitives
|
|
289
|
+
overlayContext.lineWidth = 2;
|
|
298
290
|
const pointSize = debugPointSize * cameraScale;
|
|
299
291
|
debugPrimitives.forEach(p=>
|
|
300
292
|
{
|
|
293
|
+
overlayContext.save();
|
|
294
|
+
|
|
301
295
|
// create canvas transform from world space to screen space
|
|
302
296
|
const pos = worldToScreen(p.pos);
|
|
303
297
|
|
|
304
|
-
overlayContext.save();
|
|
305
|
-
overlayContext.lineWidth = 2;
|
|
306
298
|
overlayContext.translate(pos.x|0, pos.y|0);
|
|
307
299
|
overlayContext.rotate(p.angle);
|
|
308
300
|
overlayContext.fillStyle = overlayContext.strokeStyle = p.color;
|
|
@@ -335,15 +327,17 @@ const debugRender = ()=>
|
|
|
335
327
|
p.fill && overlayContext.fill();
|
|
336
328
|
overlayContext.stroke();
|
|
337
329
|
}
|
|
338
|
-
|
|
330
|
+
|
|
339
331
|
overlayContext.restore();
|
|
340
332
|
});
|
|
341
333
|
|
|
342
|
-
|
|
334
|
+
// remove expired pritives
|
|
335
|
+
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
343
336
|
}
|
|
344
337
|
|
|
345
338
|
{
|
|
346
|
-
|
|
339
|
+
// draw debug overlay
|
|
340
|
+
overlayContext.save();
|
|
347
341
|
overlayContext.fillStyle = '#fff';
|
|
348
342
|
overlayContext.textAlign = 'left';
|
|
349
343
|
overlayContext.textBaseline = 'top';
|
|
@@ -351,6 +345,7 @@ const debugRender = ()=>
|
|
|
351
345
|
overlayContext.shadowColor = '#000';
|
|
352
346
|
overlayContext.shadowBlur = 9;
|
|
353
347
|
|
|
348
|
+
let x = 9, y = -20, h = 30;
|
|
354
349
|
if (debugOverlay)
|
|
355
350
|
{
|
|
356
351
|
overlayContext.fillText(engineName, x, y += h);
|
|
@@ -363,14 +358,12 @@ const debugRender = ()=>
|
|
|
363
358
|
overlayContext.fillText('1: Debug Physics', x, y += h);
|
|
364
359
|
overlayContext.fillStyle = debugParticles ? '#f00' : '#fff';
|
|
365
360
|
overlayContext.fillText('2: Debug Particles', x, y += h);
|
|
361
|
+
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
362
|
+
overlayContext.fillText('3: Debug Gamepads', x, y += h);
|
|
366
363
|
overlayContext.fillStyle = godMode ? '#f00' : '#fff';
|
|
367
|
-
overlayContext.fillText('
|
|
364
|
+
overlayContext.fillText('4: God Mode', x, y += h);
|
|
368
365
|
overlayContext.fillStyle = '#fff';
|
|
369
366
|
overlayContext.fillText('5: Save Screenshot', x, y += h);
|
|
370
|
-
//overlayContext.fillStyle = debugParticleEditor ? '#f00' : '#fff';
|
|
371
|
-
//overlayContext.fillText('6: Particle Editor', x, y += h);
|
|
372
|
-
overlayContext.fillStyle = debugGamepads ? '#f00' : '#fff';
|
|
373
|
-
overlayContext.fillText('7: Debug Gamepads', x, y += h);
|
|
374
367
|
|
|
375
368
|
let keysPressed = '';
|
|
376
369
|
for(const i in inputData[0])
|
|
@@ -388,7 +381,6 @@ const debugRender = ()=>
|
|
|
388
381
|
buttonsPressed += i + ' ' ;
|
|
389
382
|
}
|
|
390
383
|
buttonsPressed && overlayContext.fillText('Gamepad: ' + buttonsPressed, x, y += h);
|
|
391
|
-
|
|
392
384
|
}
|
|
393
385
|
else
|
|
394
386
|
{
|
|
@@ -398,200 +390,9 @@ const debugRender = ()=>
|
|
|
398
390
|
overlayContext.fillText(debugGamepads ? 'Debug Gamepads' : '', x, y += h);
|
|
399
391
|
}
|
|
400
392
|
|
|
401
|
-
overlayContext.
|
|
393
|
+
overlayContext.restore();
|
|
402
394
|
}
|
|
403
|
-
|
|
404
|
-
debugPrimitives = debugPrimitives.filter(r=>r.time.get()<0);
|
|
405
395
|
}
|
|
406
|
-
|
|
407
|
-
///////////////////////////////////////////////////////////////////////////////
|
|
408
|
-
// particle system editor (work in progress)
|
|
409
|
-
let debugParticleEditor = 0, debugParticleSystem, debugParticleSystemDiv, particleSystemCode;
|
|
410
|
-
|
|
411
|
-
const debugToggleParticleEditor = ()=>
|
|
412
|
-
{
|
|
413
|
-
debugParticleEditor = !debugParticleEditor;
|
|
414
|
-
|
|
415
|
-
if (debugParticleEditor)
|
|
416
|
-
{
|
|
417
|
-
if (!debugParticleSystem || debugParticleSystem.destroyed)
|
|
418
|
-
debugParticleSystem = new ParticleEmitter(cameraPos);
|
|
419
|
-
}
|
|
420
|
-
else if (debugParticleSystem && !debugParticleSystem.destroyed)
|
|
421
|
-
debugParticleSystem.destroy();
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const colorToHex = (color)=>
|
|
425
|
-
{
|
|
426
|
-
const componentToHex = (c)=>
|
|
427
|
-
{
|
|
428
|
-
const hex = (c*255|0).toString(16);
|
|
429
|
-
return hex.length == 1 ? '0' + hex : hex;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
return '#' + componentToHex(color.r) + componentToHex(color.g) + componentToHex(color.b);
|
|
433
|
-
}
|
|
434
|
-
const hexToColor = (hex)=>
|
|
435
|
-
{
|
|
436
|
-
return new Color(
|
|
437
|
-
parseInt(hex.substr(1,2), 16)/255,
|
|
438
|
-
parseInt(hex.substr(3,2), 16)/255,
|
|
439
|
-
parseInt(hex.substr(5,2), 16)/255)
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
if (!debugParticleSystemDiv)
|
|
443
|
-
{
|
|
444
|
-
const div = debugParticleSystemDiv = document.createElement('div');
|
|
445
|
-
div.innerHTML = '<big><b>Particle Editor';
|
|
446
|
-
div.style = 'position:absolute;top:10;left:10;color:#fff';
|
|
447
|
-
document.body.appendChild(div);
|
|
448
|
-
|
|
449
|
-
for ( const setting of debugParticleSettings)
|
|
450
|
-
{
|
|
451
|
-
const input = setting[2] = document.createElement('input');
|
|
452
|
-
const name = setting[0];
|
|
453
|
-
const type = setting[1];
|
|
454
|
-
if (type)
|
|
455
|
-
{
|
|
456
|
-
if (type == 'color')
|
|
457
|
-
{
|
|
458
|
-
input.type = type;
|
|
459
|
-
const color = debugParticleSystem[name];
|
|
460
|
-
input.value = colorToHex(color);
|
|
461
|
-
}
|
|
462
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
463
|
-
input.value = debugParticleSystem.colorStartA.a;
|
|
464
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
465
|
-
input.value = debugParticleSystem.colorEndA.a;
|
|
466
|
-
else if (name == 'tileSizeX')
|
|
467
|
-
input.value = debugParticleSystem.tileSize.x;
|
|
468
|
-
else if (name == 'tileSizeY')
|
|
469
|
-
input.value = debugParticleSystem.tileSize.y;
|
|
470
|
-
}
|
|
471
|
-
else
|
|
472
|
-
input.value = debugParticleSystem[name] || '0';
|
|
473
|
-
|
|
474
|
-
input.oninput = (e)=>
|
|
475
|
-
{
|
|
476
|
-
const inputFloat = parseFloat(input.value) || 0;
|
|
477
|
-
if (type)
|
|
478
|
-
{
|
|
479
|
-
if (type == 'color')
|
|
480
|
-
{
|
|
481
|
-
const color = hexToColor(input.value);
|
|
482
|
-
debugParticleSystem[name].r = color.r;
|
|
483
|
-
debugParticleSystem[name].g = color.g;
|
|
484
|
-
debugParticleSystem[name].b = color.b;
|
|
485
|
-
}
|
|
486
|
-
else if (type == 'alpha' && name == 'colorStartAlpha')
|
|
487
|
-
{
|
|
488
|
-
debugParticleSystem.colorStartA.a = clamp(inputFloat);
|
|
489
|
-
debugParticleSystem.colorStartB.a = clamp(inputFloat);
|
|
490
|
-
}
|
|
491
|
-
else if (type == 'alpha' && name == 'colorEndAlpha')
|
|
492
|
-
{
|
|
493
|
-
debugParticleSystem.colorEndA.a = clamp(inputFloat);
|
|
494
|
-
debugParticleSystem.colorEndB.a = clamp(inputFloat);
|
|
495
|
-
}
|
|
496
|
-
else if (name == 'tileSizeX')
|
|
497
|
-
{
|
|
498
|
-
debugParticleSystem.tileSize = vec2(parseInt(input.value), debugParticleSystem.tileSize.y);
|
|
499
|
-
}
|
|
500
|
-
else if (name == 'tileSizeY')
|
|
501
|
-
{
|
|
502
|
-
debugParticleSystem.tileSize.y = vec2(debugParticleSystem.tileSize.x, parseInt(input.value));
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
else
|
|
506
|
-
debugParticleSystem[name] = inputFloat;
|
|
507
|
-
|
|
508
|
-
updateCode();
|
|
509
|
-
}
|
|
510
|
-
div.appendChild(document.createElement('br'));
|
|
511
|
-
div.appendChild(input);
|
|
512
|
-
div.appendChild(document.createTextNode(' ' + name));
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
div.appendChild(document.createElement('br'));
|
|
516
|
-
div.appendChild(document.createElement('br'));
|
|
517
|
-
div.appendChild(particleSystemCode = document.createElement('input'));
|
|
518
|
-
particleSystemCode.disabled = true;
|
|
519
|
-
div.appendChild(document.createTextNode(' code'));
|
|
520
|
-
|
|
521
|
-
div.appendChild(document.createElement('br'));
|
|
522
|
-
const button = document.createElement('button')
|
|
523
|
-
div.appendChild(button);
|
|
524
|
-
button.innerHTML = 'Copy To Clipboard';
|
|
525
|
-
|
|
526
|
-
button.onclick = (e)=> navigator.clipboard.writeText(particleSystemCode.value);
|
|
527
|
-
|
|
528
|
-
const updateCode = ()=>
|
|
529
|
-
{
|
|
530
|
-
let code = '';
|
|
531
|
-
let count = 0;
|
|
532
|
-
for ( const setting of debugParticleSettings)
|
|
533
|
-
{
|
|
534
|
-
const name = setting[0];
|
|
535
|
-
const type = setting[1];
|
|
536
|
-
let value;
|
|
537
|
-
if (name == 'tileSizeX' || type == 'alpha')
|
|
538
|
-
continue;
|
|
539
|
-
|
|
540
|
-
if (count++)
|
|
541
|
-
code += ', ';
|
|
542
|
-
|
|
543
|
-
if (name == 'tileSizeY')
|
|
544
|
-
{
|
|
545
|
-
value = `vec2(${debugParticleSystem.tileSize.x},${debugParticleSystem.tileSize.y})`;
|
|
546
|
-
}
|
|
547
|
-
else if (type == 'color')
|
|
548
|
-
{
|
|
549
|
-
const c = debugParticleSystem[name];
|
|
550
|
-
value = `new Color(${c.r},${c.g},${c.b},${c.a})`;
|
|
551
|
-
}
|
|
552
|
-
else
|
|
553
|
-
value = debugParticleSystem[name];
|
|
554
|
-
code += value;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
particleSystemCode.value = '...[' + code + ']';
|
|
558
|
-
}
|
|
559
|
-
updateCode();
|
|
560
|
-
}
|
|
561
|
-
debugParticleSystemDiv.style.display = debugParticleEditor ? '' : 'none'
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
const debugParticleSettings =
|
|
565
|
-
[
|
|
566
|
-
['emitSize'],
|
|
567
|
-
['emitTime'],
|
|
568
|
-
['emitRate'],
|
|
569
|
-
['emitConeAngle'],
|
|
570
|
-
['tileIndex'],
|
|
571
|
-
['tileSizeX', 'tileSize'],
|
|
572
|
-
['tileSizeY', 'tileSize'],
|
|
573
|
-
['colorStartA', 'color'],
|
|
574
|
-
['colorStartB', 'color'],
|
|
575
|
-
['colorStartAlpha', 'alpha'],
|
|
576
|
-
['colorEndA', 'color'],
|
|
577
|
-
['colorEndB', 'color'],
|
|
578
|
-
['colorEndAlpha', 'alpha'],
|
|
579
|
-
['particleTime'],
|
|
580
|
-
['sizeStart'],
|
|
581
|
-
['sizeEnd'],
|
|
582
|
-
['speed'],
|
|
583
|
-
['angleSpeed'],
|
|
584
|
-
['damping'],
|
|
585
|
-
['angleDamping'],
|
|
586
|
-
['gravityScale'],
|
|
587
|
-
['particleConeAngle'],
|
|
588
|
-
['fadeRate'],
|
|
589
|
-
['randomness'],
|
|
590
|
-
['collideTiles'],
|
|
591
|
-
['additive'],
|
|
592
|
-
['randomColorComponents'],
|
|
593
|
-
['renderOrder'],
|
|
594
|
-
];
|
|
595
396
|
/**
|
|
596
397
|
* LittleJS Utility Classes and Functions
|
|
597
398
|
* <br> - General purpose math library
|
|
@@ -643,27 +444,27 @@ const mod = (a, b=1)=> ((a % b) + b) % b;
|
|
|
643
444
|
|
|
644
445
|
/** Clamps the value beween max and min
|
|
645
446
|
* @param {Number} value
|
|
646
|
-
* @param {Number} [max=1]
|
|
647
447
|
* @param {Number} [min=0]
|
|
448
|
+
* @param {Number} [max=1]
|
|
648
449
|
* @return {Number}
|
|
649
450
|
* @memberof Utilities */
|
|
650
|
-
const clamp = (v,
|
|
451
|
+
const clamp = (v, min=0, max=1)=> v < min ? min : v > max ? max : v;
|
|
651
452
|
|
|
652
453
|
/** Returns what percentage the value is between max and min
|
|
653
454
|
* @param {Number} value
|
|
654
|
-
* @param {Number} [max=1]
|
|
655
455
|
* @param {Number} [min=0]
|
|
456
|
+
* @param {Number} [max=1]
|
|
656
457
|
* @return {Number}
|
|
657
458
|
* @memberof Utilities */
|
|
658
|
-
const percent = (v,
|
|
459
|
+
const percent = (v, min=0, max=1)=> max-min ? clamp((v-min) / (max-min)) : 0;
|
|
659
460
|
|
|
660
461
|
/** Linearly interpolates the percent value between max and min
|
|
661
462
|
* @param {Number} percent
|
|
662
|
-
* @param {Number} [max=1]
|
|
663
463
|
* @param {Number} [min=0]
|
|
464
|
+
* @param {Number} [max=1]
|
|
664
465
|
* @return {Number}
|
|
665
466
|
* @memberof Utilities */
|
|
666
|
-
const lerp = (p,
|
|
467
|
+
const lerp = (p, min=0, max=1)=> min + clamp(p) * (max-min);
|
|
667
468
|
|
|
668
469
|
/** Applies smoothstep function to the percentage value
|
|
669
470
|
* @param {Number} value
|
|
@@ -678,11 +479,11 @@ const smoothStep = (p)=> p * p * (3 - 2 * p);
|
|
|
678
479
|
const nearestPowerOfTwo = (v)=> 2**Math.ceil(Math.log2(v));
|
|
679
480
|
|
|
680
481
|
/** Returns true if two axis aligned bounding boxes are overlapping
|
|
681
|
-
* @param {Vector2} pointA
|
|
682
|
-
* @param {Vector2} sizeA
|
|
683
|
-
* @param {Vector2} pointB
|
|
684
|
-
* @param {Vector2} sizeB
|
|
685
|
-
* @return {Boolean}
|
|
482
|
+
* @param {Vector2} pointA - Center of box A
|
|
483
|
+
* @param {Vector2} sizeA - Size of box A
|
|
484
|
+
* @param {Vector2} pointB - Center of box B
|
|
485
|
+
* @param {Vector2} [sizeB] - Size of box B
|
|
486
|
+
* @return {Boolean} - True if overlapping
|
|
686
487
|
* @memberof Utilities */
|
|
687
488
|
const isOverlapping = (pA, sA, pB, sB)=> abs(pA.x - pB.x)*2 < sA.x + sB.x & abs(pA.y - pB.y)*2 < sA.y + sB.y;
|
|
688
489
|
|
|
@@ -905,7 +706,13 @@ class Vector2
|
|
|
905
706
|
/** Returns true if this vector is within the bounds of an array size passed in
|
|
906
707
|
* @param {Vector2} arraySize
|
|
907
708
|
* @return {Boolean} */
|
|
908
|
-
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
709
|
+
arrayCheck(arraySize) { return this.x >= 0 && this.y >= 0 && this.x < arraySize.x && this.y < arraySize.y; }
|
|
710
|
+
|
|
711
|
+
/** Returns this vector expressed as a string
|
|
712
|
+
* @param {float} digits - precision to display
|
|
713
|
+
* @return {String} */
|
|
714
|
+
toString(digits=3)
|
|
715
|
+
{ return `(${(this.x<0?'':' ') + this.x.toFixed(digits)},${(this.y<0?'':' ') + this.y.toFixed(digits)} )`; }
|
|
909
716
|
}
|
|
910
717
|
|
|
911
718
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -976,7 +783,7 @@ class Color
|
|
|
976
783
|
* @return {Color} */
|
|
977
784
|
lerp(c, p) { return this.add(c.subtract(this).scale(clamp(p))); }
|
|
978
785
|
|
|
979
|
-
/** Sets this color given a hue, saturation, lightness
|
|
786
|
+
/** Sets this color given a hue, saturation, lightness, and alpha
|
|
980
787
|
* @param {Number} [hue=0]
|
|
981
788
|
* @param {Number} [saturation=0]
|
|
982
789
|
* @param {Number} [lightness=1]
|
|
@@ -1012,21 +819,43 @@ class Color
|
|
|
1012
819
|
).clamp();
|
|
1013
820
|
}
|
|
1014
821
|
|
|
1015
|
-
/** Returns this color expressed as an
|
|
822
|
+
/** Returns this color expressed as an CSS color value
|
|
1016
823
|
* @return {String} */
|
|
1017
|
-
|
|
824
|
+
toString()
|
|
1018
825
|
{
|
|
1019
826
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
1020
827
|
return `rgb(${this.r*255|0},${this.g*255|0},${this.b*255|0},${this.a})`;
|
|
1021
828
|
}
|
|
1022
829
|
|
|
1023
|
-
/** Returns this color expressed as 32 bit integer value
|
|
830
|
+
/** Returns this color expressed as 32 bit integer RGBA value
|
|
1024
831
|
* @return {Number} */
|
|
1025
832
|
rgbaInt()
|
|
1026
833
|
{
|
|
1027
834
|
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1 && this.a>=0 && this.a<=1);
|
|
1028
835
|
return (this.r*255|0) + (this.g*255<<8) + (this.b*255<<16) + (this.a*255<<24);
|
|
1029
836
|
}
|
|
837
|
+
|
|
838
|
+
/** Returns this color expressed as a hex code
|
|
839
|
+
* @return {String} */
|
|
840
|
+
hex()
|
|
841
|
+
{
|
|
842
|
+
const toHex = (c)=> ((c=c*255|0)<16 ? '0' : '') + c.toString(16);
|
|
843
|
+
return '#' + toHex(this.r) + toHex(this.g) + toHex(this.b);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** Set this color from a hex code
|
|
847
|
+
* @param {String} hex - html hex code
|
|
848
|
+
* @return {Color} */
|
|
849
|
+
setHex(hex)
|
|
850
|
+
{
|
|
851
|
+
const fromHex = (a)=> parseInt(hex.slice(a,a+2), 16)/255;
|
|
852
|
+
this.r = fromHex(1);
|
|
853
|
+
this.g = fromHex(3),
|
|
854
|
+
this.b = fromHex(5);
|
|
855
|
+
this.a = 1;
|
|
856
|
+
ASSERT(this.r>=0 && this.r<=1 && this.g>=0 && this.g<=1 && this.b>=0 && this.b<=1);
|
|
857
|
+
return this;
|
|
858
|
+
}
|
|
1030
859
|
}
|
|
1031
860
|
|
|
1032
861
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -1063,7 +892,7 @@ class Timer
|
|
|
1063
892
|
|
|
1064
893
|
/** Returns true if set and elapsed
|
|
1065
894
|
* @return {Boolean} */
|
|
1066
|
-
elapsed() { return time >
|
|
895
|
+
elapsed() { return time > this.time; }
|
|
1067
896
|
|
|
1068
897
|
/** Get how long since elapsed, returns 0 if not set (returns negative if currently active)
|
|
1069
898
|
* @return {Number} */
|
|
@@ -1071,7 +900,11 @@ class Timer
|
|
|
1071
900
|
|
|
1072
901
|
/** Get percentage elapsed based on time it was set to, returns 0 if not set
|
|
1073
902
|
* @return {Number} */
|
|
1074
|
-
getPercent() { return this.isSet()? percent(this.time - time,
|
|
903
|
+
getPercent() { return this.isSet()? percent(this.time - time, this.setTime, 0) : 0; }
|
|
904
|
+
|
|
905
|
+
/** Returns this timer expressed as a string
|
|
906
|
+
* @return {String} */
|
|
907
|
+
toString() { return this.unset() ? 'unset' : Math.abs(this.get()) + ' seconds ' + (this.get()<0 ? 'before' : 'after' ); }
|
|
1075
908
|
}
|
|
1076
909
|
/**
|
|
1077
910
|
* LittleJS Engine Settings
|
|
@@ -1220,6 +1053,11 @@ let inputWASDEmulateDirection = 1;
|
|
|
1220
1053
|
* @memberof Settings */
|
|
1221
1054
|
let touchGamepadEnable = 0;
|
|
1222
1055
|
|
|
1056
|
+
/** True if touch gamepad should be analog stick or false to use if 8 way dpad
|
|
1057
|
+
* @default
|
|
1058
|
+
* @memberof Settings */
|
|
1059
|
+
let touchGamepadAnalog = 1;
|
|
1060
|
+
|
|
1223
1061
|
/** Size of virutal gamepad for touch devices in pixels
|
|
1224
1062
|
* @default
|
|
1225
1063
|
* @memberof Settings */
|
|
@@ -1319,11 +1157,11 @@ let medalDisplayIconSize = 50;
|
|
|
1319
1157
|
class EngineObject
|
|
1320
1158
|
{
|
|
1321
1159
|
/** Create an engine object and adds it to the list of objects
|
|
1322
|
-
* @param {Vector2} [position=new Vector2(
|
|
1160
|
+
* @param {Vector2} [position=new Vector2()] - World space position of the object
|
|
1323
1161
|
* @param {Vector2} [size=objectDefaultSize] - World space size of the object
|
|
1324
1162
|
* @param {Number} [tileIndex=-1] - Tile to use to render object, untextured if -1
|
|
1325
1163
|
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tile in source pixels
|
|
1326
|
-
* @param {Number} [angle=0] - Angle
|
|
1164
|
+
* @param {Number} [angle=0] - Angle the object is rotated by
|
|
1327
1165
|
* @param {Color} [color] - Color to apply to tile when rendered
|
|
1328
1166
|
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
1329
1167
|
*/
|
|
@@ -1364,12 +1202,15 @@ class EngineObject
|
|
|
1364
1202
|
this.gravityScale = 1;
|
|
1365
1203
|
/** @property {Number} [renderOrder=0] - Objects are sorted by render order */
|
|
1366
1204
|
this.renderOrder = renderOrder;
|
|
1205
|
+
/** @property {Vector2} [velocity=new Vector2()] - Velocity of the object */
|
|
1206
|
+
this.velocity = new Vector2();
|
|
1207
|
+
/** @property {Number} [angleVelocity=0] - Angular velocity of the object */
|
|
1208
|
+
this.angleVelocity = 0;
|
|
1367
1209
|
|
|
1368
1210
|
// init other internal object stuff
|
|
1369
1211
|
this.spawnTime = time;
|
|
1370
|
-
this.velocity = vec2(this.collideSolidObjects = this.angleVelocity = 0);
|
|
1371
|
-
this.collideTiles = 1;
|
|
1372
1212
|
this.children = [];
|
|
1213
|
+
this.collideTiles = 1;
|
|
1373
1214
|
|
|
1374
1215
|
// add to list of objects
|
|
1375
1216
|
engineObjects.push(this);
|
|
@@ -1388,8 +1229,8 @@ class EngineObject
|
|
|
1388
1229
|
}
|
|
1389
1230
|
|
|
1390
1231
|
// limit max speed to prevent missing collisions
|
|
1391
|
-
this.velocity.x = clamp(this.velocity.x, objectMaxSpeed,
|
|
1392
|
-
this.velocity.y = clamp(this.velocity.y, objectMaxSpeed,
|
|
1232
|
+
this.velocity.x = clamp(this.velocity.x, -objectMaxSpeed, objectMaxSpeed);
|
|
1233
|
+
this.velocity.y = clamp(this.velocity.y, -objectMaxSpeed, objectMaxSpeed);
|
|
1393
1234
|
|
|
1394
1235
|
// apply physics
|
|
1395
1236
|
const oldPos = this.pos.copy();
|
|
@@ -1479,8 +1320,8 @@ class EngineObject
|
|
|
1479
1320
|
|
|
1480
1321
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1481
1322
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1482
|
-
this.velocity.y = lerp(elasticity,
|
|
1483
|
-
o.velocity.y = lerp(elasticity,
|
|
1323
|
+
this.velocity.y = lerp(elasticity, inelastic, elastic0);
|
|
1324
|
+
o.velocity.y = lerp(elasticity, inelastic, elastic1);
|
|
1484
1325
|
}
|
|
1485
1326
|
}
|
|
1486
1327
|
if (!smallStepUp && (isBlockedX || !isBlockedY)) // resolve x collision
|
|
@@ -1500,8 +1341,8 @@ class EngineObject
|
|
|
1500
1341
|
|
|
1501
1342
|
// lerp betwen elastic or inelastic based on elasticity
|
|
1502
1343
|
const elasticity = max(this.elasticity, o.elasticity);
|
|
1503
|
-
this.velocity.x = lerp(elasticity,
|
|
1504
|
-
o.velocity.x = lerp(elasticity,
|
|
1344
|
+
this.velocity.x = lerp(elasticity, inelastic, elastic0);
|
|
1345
|
+
o.velocity.x = lerp(elasticity, inelastic, elastic1);
|
|
1505
1346
|
}
|
|
1506
1347
|
else // bounce if other object is fixed
|
|
1507
1348
|
this.velocity.x *= -this.elasticity;
|
|
@@ -1531,7 +1372,7 @@ class EngineObject
|
|
|
1531
1372
|
|
|
1532
1373
|
// adjust next velocity to settle on ground
|
|
1533
1374
|
const o = (oldPos.y - this.size.y/2|0) - (oldPos.y - this.size.y/2);
|
|
1534
|
-
if (o < 0 && o >
|
|
1375
|
+
if (o < 0 && o > this.damping * this.velocity.y + gravity * this.gravityScale)
|
|
1535
1376
|
this.velocity.y = this.damping ? (o - gravity * this.gravityScale) / this.damping : 0;
|
|
1536
1377
|
|
|
1537
1378
|
// move to previous position
|
|
@@ -1636,6 +1477,22 @@ class EngineObject
|
|
|
1636
1477
|
this.isSolid = isSolid;
|
|
1637
1478
|
this.collideTiles = collideTiles;
|
|
1638
1479
|
}
|
|
1480
|
+
|
|
1481
|
+
toString()
|
|
1482
|
+
{
|
|
1483
|
+
let text = 'type = ' + this.constructor.name;
|
|
1484
|
+
if (this.pos.x || this.pos.y)
|
|
1485
|
+
text += '\npos = ' + this.pos;
|
|
1486
|
+
if (this.velocity.x || this.velocity.y)
|
|
1487
|
+
text += '\nvelocity = ' + this.velocity;
|
|
1488
|
+
if (this.size.x || this.size.y)
|
|
1489
|
+
text += '\nsize = ' + this.size;
|
|
1490
|
+
if (this.angle)
|
|
1491
|
+
text += '\nangle = ' + this.angle.toFixed(3);
|
|
1492
|
+
if (this.color)
|
|
1493
|
+
text += '\ncolor = ' + this.color;
|
|
1494
|
+
return text;
|
|
1495
|
+
}
|
|
1639
1496
|
}
|
|
1640
1497
|
/**
|
|
1641
1498
|
* LittleJS Drawing System
|
|
@@ -1749,7 +1606,7 @@ function drawTile(pos, size=vec2(1), tileIndex=-1, tileSize=tileSizeDefault, col
|
|
|
1749
1606
|
if (tileIndex < 0)
|
|
1750
1607
|
{
|
|
1751
1608
|
// if negative tile index, force untextured
|
|
1752
|
-
context.fillStyle = color
|
|
1609
|
+
context.fillStyle = color;
|
|
1753
1610
|
context.fillRect(-.5, -.5, 1, 1);
|
|
1754
1611
|
}
|
|
1755
1612
|
else
|
|
@@ -1842,7 +1699,20 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1842
1699
|
context.restore();
|
|
1843
1700
|
}
|
|
1844
1701
|
|
|
1845
|
-
/**
|
|
1702
|
+
/** Enable normal or additive blend mode
|
|
1703
|
+
* @param {Boolean} [additive=0]
|
|
1704
|
+
* @param {Boolean} [useWebGL=glEnable]
|
|
1705
|
+
* @memberof Draw */
|
|
1706
|
+
function setBlendMode(additive, useWebGL=glEnable)
|
|
1707
|
+
{
|
|
1708
|
+
if (glEnable && useWebGL)
|
|
1709
|
+
glSetBlendMode(additive);
|
|
1710
|
+
else
|
|
1711
|
+
mainContext.globalCompositeOperation = additive ? 'lighter' : 'source-over';
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
/** Draw text on overlay canvas in screen space
|
|
1715
|
+
* Automatically splits new lines into rows
|
|
1846
1716
|
* @param {String} text
|
|
1847
1717
|
* @param {Vector2} pos
|
|
1848
1718
|
* @param {Number} [size=1]
|
|
@@ -1851,32 +1721,129 @@ function drawCanvas2D(pos, size, angle, mirror, drawFunction, context = mainCont
|
|
|
1851
1721
|
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1852
1722
|
* @param {String} [textAlign='center']
|
|
1853
1723
|
* @memberof Draw */
|
|
1854
|
-
function
|
|
1724
|
+
function drawTextScreen(text, pos, size=1, color=new Color, lineWidth=0, lineColor=new Color(0,0,0), textAlign='center', font=fontDefault)
|
|
1855
1725
|
{
|
|
1856
|
-
|
|
1857
|
-
overlayContext.
|
|
1726
|
+
overlayContext.fillStyle = color;
|
|
1727
|
+
overlayContext.lineWidth = lineWidth *= cameraScale;
|
|
1728
|
+
overlayContext.strokeStyle = lineColor;
|
|
1858
1729
|
overlayContext.textAlign = textAlign;
|
|
1730
|
+
overlayContext.font = size + 'px '+ font;
|
|
1859
1731
|
overlayContext.textBaseline = 'middle';
|
|
1860
|
-
|
|
1732
|
+
|
|
1733
|
+
pos = pos.copy();
|
|
1734
|
+
text.split('\n').forEach(line=>
|
|
1861
1735
|
{
|
|
1862
|
-
overlayContext.
|
|
1863
|
-
overlayContext.
|
|
1864
|
-
|
|
1865
|
-
}
|
|
1866
|
-
overlayContext.fillStyle = color.rgba();
|
|
1867
|
-
overlayContext.fillText(text, pos.x, pos.y);
|
|
1736
|
+
lineWidth && overlayContext.strokeText(line, pos.x, pos.y);
|
|
1737
|
+
overlayContext.fillText(line, pos.x, pos.y);
|
|
1738
|
+
pos.y += size;
|
|
1739
|
+
});
|
|
1868
1740
|
}
|
|
1869
1741
|
|
|
1870
|
-
/**
|
|
1871
|
-
*
|
|
1872
|
-
* @param {
|
|
1742
|
+
/** Draw text on overlay canvas in world space
|
|
1743
|
+
* Automatically splits new lines into rows
|
|
1744
|
+
* @param {String} text
|
|
1745
|
+
* @param {Vector2} pos
|
|
1746
|
+
* @param {Number} [size=1]
|
|
1747
|
+
* @param {Color} [color=new Color(1,1,1)]
|
|
1748
|
+
* @param {Number} [lineWidth=0]
|
|
1749
|
+
* @param {Color} [lineColor=new Color(0,0,0)]
|
|
1750
|
+
* @param {String} [textAlign='center']
|
|
1873
1751
|
* @memberof Draw */
|
|
1874
|
-
function
|
|
1752
|
+
function drawText(text, pos, size=1, color, lineWidth, lineColor, textAlign, font)
|
|
1875
1753
|
{
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1754
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth, lineColor, textAlign, font);
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Font Image Object - Draw text on a 2D canvas by using characters in an image
|
|
1761
|
+
* <br> - 96 characters (from space to tilde) are stored in an image
|
|
1762
|
+
* <br> - Uses a default 8x8 font if none is supplied
|
|
1763
|
+
* <br> - You can also use fonts from the main tile sheet
|
|
1764
|
+
* @example
|
|
1765
|
+
* // use built in font
|
|
1766
|
+
* const font = new ImageFont;
|
|
1767
|
+
*
|
|
1768
|
+
* // draw text
|
|
1769
|
+
* font.drawTextScreen("LittleJS\nHello World!", vec2(200, 50));
|
|
1770
|
+
*/
|
|
1771
|
+
|
|
1772
|
+
let engineFontImage;
|
|
1773
|
+
|
|
1774
|
+
class FontImage
|
|
1775
|
+
{
|
|
1776
|
+
/** Create an image font
|
|
1777
|
+
* @param {Image} [image] - The image the font is stored in, if undefined the default font is used
|
|
1778
|
+
* @param {Vector2} [tileSize=vec2(8)] - The size of the font source tiles
|
|
1779
|
+
* @param {Vector2} [paddingSize=vec2(0,1)] - How much extra space to add between characters
|
|
1780
|
+
* @param {Number} [startTileIndex=0] - Tile index in image where font starts
|
|
1781
|
+
* @param {CanvasRenderingContext2D} [context=overlayContext] - context to draw to
|
|
1782
|
+
*/
|
|
1783
|
+
constructor(image, tileSize=vec2(8), paddingSize=vec2(0,1), startTileIndex=0, context=overlayContext)
|
|
1784
|
+
{
|
|
1785
|
+
if (!image && !engineFontImage)
|
|
1786
|
+
{
|
|
1787
|
+
// load default font image
|
|
1788
|
+
engineFontImage = new Image();
|
|
1789
|
+
engineFontImage.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAAYAQAAAAA9+x6JAAAAAnRSTlMAAHaTzTgAAAGiSURBVHjaZZABhxxBEIUf6ECLBdFY+Q0PMNgf0yCgsSAGZcT9sgIPtBWwIA5wgAPEoHUyJeeSlW+gjK+fegWwtROWpVQEyWh2npdpBmTUFVhb29RINgLIukoXr5LIAvYQ5ve+1FqWEMqNKTX3FAJHyQDRZvmKWubAACcv5z5Gtg2oyCWE+Yk/8JZQX1jTTCpKAFGIgza+dJCNBF2UskRlsgwitHbSV0QLgt9sTPtsRlvJjEr8C/FARWA2bJ/TtJ7lko34dNDn6usJUMzuErP89UUBJbWeozrwLLncXczd508deAjLWipLO4Q5XGPcJvPu92cNDaN0P5G1FL0nSOzddZOrJ6rNhbXGmeDvO3TF7DeJWl4bvaYQTNHCTeuqKZmbjHaSOFes+IX/+IhHrnAkXOAsfn24EM68XieIECoccD4KZLk/odiwzeo2rovYdhvb2HYFgyznJyDpYJdYOmfXgVdJTaUi4xA2uWYNYec9BLeqdl9EsoTw582mSFDX2DxVLbNt9U3YYoeatBad1c2Tj8t2akrjaIGJNywKB/7h75/gN3vCMSaadIUTAAAAAElFTkSuQmCC';
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
this.image = image || engineFontImage;
|
|
1793
|
+
this.tileSize = tileSize;
|
|
1794
|
+
this.paddingSize = paddingSize;
|
|
1795
|
+
this.startTileIndex = startTileIndex;
|
|
1796
|
+
this.context = context;
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
/** Draw text in screen space using the image font
|
|
1800
|
+
* @param {String} text
|
|
1801
|
+
* @param {Vector2} pos
|
|
1802
|
+
* @param {Number} [scale=4]
|
|
1803
|
+
* @param {Boolean} [center]
|
|
1804
|
+
*/
|
|
1805
|
+
drawTextScreen(text, pos, scale=4, center)
|
|
1806
|
+
{
|
|
1807
|
+
const context = this.context;
|
|
1808
|
+
context.save();
|
|
1809
|
+
context.imageSmoothingEnabled = !cavasPixelated;
|
|
1810
|
+
|
|
1811
|
+
const size = this.tileSize;
|
|
1812
|
+
const drawSize = size.add(this.paddingSize).scale(scale);
|
|
1813
|
+
const cols = this.image.width / this.tileSize.x |0;
|
|
1814
|
+
text.split('\n').forEach((line, i)=>
|
|
1815
|
+
{
|
|
1816
|
+
const centerOffset = center ? line.length * size.x * scale / 2 |0 : 0;
|
|
1817
|
+
for(let j=line.length; j--;)
|
|
1818
|
+
{
|
|
1819
|
+
// draw each character
|
|
1820
|
+
let charCode = line[j].charCodeAt();
|
|
1821
|
+
if (charCode < 32 || charCode > 127)
|
|
1822
|
+
charCode = 127; // unknown character
|
|
1823
|
+
|
|
1824
|
+
// get the character source location and draw it
|
|
1825
|
+
const tile = this.startTileIndex + charCode - 32;
|
|
1826
|
+
const x = tile % cols;
|
|
1827
|
+
const y = tile / cols |0;
|
|
1828
|
+
const drawPos = pos.add(vec2(j,i).multiply(drawSize));
|
|
1829
|
+
context.drawImage(this.image, x * size.x, y * size.y, size.x, size.y,
|
|
1830
|
+
drawPos.x - centerOffset, drawPos.y, size.x * scale, size.y * scale);
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
|
|
1834
|
+
context.restore();
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/** Draw text in world space using the image font
|
|
1838
|
+
* @param {String} text
|
|
1839
|
+
* @param {Vector2} pos
|
|
1840
|
+
* @param {Number} [scale=.25]
|
|
1841
|
+
* @param {Boolean} [center]
|
|
1842
|
+
*/
|
|
1843
|
+
drawText(text, pos, scale=1, center)
|
|
1844
|
+
{
|
|
1845
|
+
this.drawTextScreen(text, worldToScreen(pos).floor(), scale*cameraScale|0, center);
|
|
1846
|
+
}
|
|
1880
1847
|
}
|
|
1881
1848
|
|
|
1882
1849
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2067,7 +2034,7 @@ const mouseToScreen = (mousePos)=>
|
|
|
2067
2034
|
|
|
2068
2035
|
const rect = mainCanvas.getBoundingClientRect();
|
|
2069
2036
|
return mainCanvasSize.multiply(
|
|
2070
|
-
vec2(percent(mousePos.x, rect.
|
|
2037
|
+
vec2(percent(mousePos.x, rect.left, rect.right), percent(mousePos.y, rect.top, rect.bottom)));
|
|
2071
2038
|
}
|
|
2072
2039
|
|
|
2073
2040
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -2108,8 +2075,8 @@ function gamepadsUpdate()
|
|
|
2108
2075
|
// read clamp dead zone of analog sticks
|
|
2109
2076
|
const deadZone = .3, deadZoneMax = .8;
|
|
2110
2077
|
const applyDeadZone = (v)=>
|
|
2111
|
-
v > deadZone ? percent( v,
|
|
2112
|
-
v < -deadZone ? -percent(-v,
|
|
2078
|
+
v > deadZone ? percent( v, deadZone, deadZoneMax) :
|
|
2079
|
+
v < -deadZone ? -percent(-v, deadZone, deadZoneMax) : 0;
|
|
2113
2080
|
|
|
2114
2081
|
// read analog sticks
|
|
2115
2082
|
for (let j = 0; j < gamepad.axes.length-1; j+=2)
|
|
@@ -2235,7 +2202,14 @@ function touchGamepadCreate()
|
|
|
2235
2202
|
if (touchPos.distance(stickCenter) < touchGamepadSize)
|
|
2236
2203
|
{
|
|
2237
2204
|
// virtual analog stick
|
|
2238
|
-
|
|
2205
|
+
if (touchGamepadAnalog)
|
|
2206
|
+
touchGamepadStick = touchPos.subtract(stickCenter).scale(2/touchGamepadSize).clampLength();
|
|
2207
|
+
else
|
|
2208
|
+
{
|
|
2209
|
+
// 8 way dpad
|
|
2210
|
+
const angle = touchPos.subtract(stickCenter).angle();
|
|
2211
|
+
touchGamepadStick.setAngle((angle * 4 / PI + 8.5 | 0) * PI / 4);
|
|
2212
|
+
}
|
|
2239
2213
|
}
|
|
2240
2214
|
else if (touchPos.distance(buttonCenter) < touchGamepadSize)
|
|
2241
2215
|
{
|
|
@@ -2259,7 +2233,7 @@ function touchGamepadRender()
|
|
|
2259
2233
|
return;
|
|
2260
2234
|
|
|
2261
2235
|
// fade off when not touching or paused
|
|
2262
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
2236
|
+
const alpha = percent(touchGamepadTimer.get(), 4, 3);
|
|
2263
2237
|
if (!alpha || paused)
|
|
2264
2238
|
return;
|
|
2265
2239
|
|
|
@@ -2270,12 +2244,28 @@ function touchGamepadRender()
|
|
|
2270
2244
|
overlayContext.lineWidth = 3;
|
|
2271
2245
|
|
|
2272
2246
|
// draw left analog stick
|
|
2273
|
-
overlayContext.fillStyle =
|
|
2247
|
+
overlayContext.fillStyle = touchGamepadStick.lengthSquared() > 0 ? '#fff' : '#000';
|
|
2274
2248
|
overlayContext.beginPath();
|
|
2275
|
-
overlayContext.arc(touchGamepadSize, mainCanvasSize.y-touchGamepadSize, touchGamepadSize/2, 0,9);
|
|
2276
|
-
overlayContext.fill();
|
|
2277
|
-
overlayContext.stroke();
|
|
2278
2249
|
|
|
2250
|
+
const leftCenter = vec2(touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2251
|
+
if (touchGamepadAnalog)
|
|
2252
|
+
{
|
|
2253
|
+
overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize/2, 0, 9);
|
|
2254
|
+
overlayContext.fill();
|
|
2255
|
+
overlayContext.stroke();
|
|
2256
|
+
}
|
|
2257
|
+
else // draw cross shaped gamepad
|
|
2258
|
+
{
|
|
2259
|
+
for(let i=10; i--;)
|
|
2260
|
+
{
|
|
2261
|
+
const angle = i*PI/4;
|
|
2262
|
+
overlayContext.arc(leftCenter.x, leftCenter.y,touchGamepadSize*.6, angle + PI/8, angle + PI/8);
|
|
2263
|
+
i%2 && overlayContext.arc(leftCenter.x, leftCenter.y, touchGamepadSize*.33, angle, angle);
|
|
2264
|
+
i==1 && overlayContext.fill();
|
|
2265
|
+
}
|
|
2266
|
+
overlayContext.stroke();
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2279
2269
|
// draw right face buttons
|
|
2280
2270
|
const rightCenter = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
2281
2271
|
for (let i=4; i--;)
|
|
@@ -2362,7 +2352,7 @@ class Sound
|
|
|
2362
2352
|
return; // out of range
|
|
2363
2353
|
|
|
2364
2354
|
// attenuate volume by distance
|
|
2365
|
-
volume *= percent(lengthSquared**.5, range*this.taper
|
|
2355
|
+
volume *= percent(lengthSquared**.5, range, range*this.taper);
|
|
2366
2356
|
}
|
|
2367
2357
|
|
|
2368
2358
|
// get pan from screen space coords
|
|
@@ -2544,7 +2534,7 @@ function playSamples(sampleChannels, volume=1, rate=1, pan=0, loop=0)
|
|
|
2544
2534
|
// connect source to gain
|
|
2545
2535
|
(
|
|
2546
2536
|
window.StereoPannerNode ? // create pan node if possible
|
|
2547
|
-
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, 1,
|
|
2537
|
+
source.connect(new StereoPannerNode(audioContext, {'pan':clamp(pan, -1, 1)}))
|
|
2548
2538
|
: source
|
|
2549
2539
|
)
|
|
2550
2540
|
.connect(gainNode);
|
|
@@ -2916,14 +2906,14 @@ class TileLayerData
|
|
|
2916
2906
|
*/
|
|
2917
2907
|
class TileLayer extends EngineObject
|
|
2918
2908
|
{
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2909
|
+
/** Create a tile layer object
|
|
2910
|
+
* @param {Vector2} [position=new Vector2()] - World space position
|
|
2911
|
+
* @param {Vector2} [size=tileCollisionSize] - World space size
|
|
2912
|
+
* @param {Vector2} [tileSize=tileSizeDefault] - Size of tiles in source pixels
|
|
2913
|
+
* @param {Vector2} [scale=new Vector2(1,1)] - How much to scale this layer when rendered
|
|
2914
|
+
* @param {Number} [renderOrder=0] - Objects sorted by renderOrder before being rendered
|
|
2915
|
+
*/
|
|
2916
|
+
constructor(pos, size=tileCollisionSize, tileSize=tileSizeDefault, scale=vec2(1), renderOrder=0)
|
|
2927
2917
|
{
|
|
2928
2918
|
super(pos, size, -1, tileSize, 0, undefined, renderOrder);
|
|
2929
2919
|
|
|
@@ -2981,17 +2971,19 @@ class TileLayer extends EngineObject
|
|
|
2981
2971
|
);
|
|
2982
2972
|
}
|
|
2983
2973
|
|
|
2984
|
-
/** Draw all the tile data to an offscreen canvas
|
|
2974
|
+
/** Draw all the tile data to an offscreen canvas
|
|
2975
|
+
* - This may be slow in some browsers
|
|
2976
|
+
*/
|
|
2985
2977
|
redraw()
|
|
2986
2978
|
{
|
|
2987
|
-
this.redrawStart();
|
|
2979
|
+
this.redrawStart(1);
|
|
2988
2980
|
this.drawAllTileData();
|
|
2989
2981
|
this.redrawEnd();
|
|
2990
2982
|
}
|
|
2991
2983
|
|
|
2992
2984
|
/** Call to start the redraw process
|
|
2993
|
-
* @param {Boolean} [clear=
|
|
2994
|
-
redrawStart(clear =
|
|
2985
|
+
* @param {Boolean} [clear=0] - Should it clear the canvas before drawing */
|
|
2986
|
+
redrawStart(clear = 0)
|
|
2995
2987
|
{
|
|
2996
2988
|
if (clear)
|
|
2997
2989
|
{
|
|
@@ -3081,7 +3073,7 @@ class TileLayer extends EngineObject
|
|
|
3081
3073
|
if (tileIndex < 0)
|
|
3082
3074
|
{
|
|
3083
3075
|
// untextured
|
|
3084
|
-
context.fillStyle = color
|
|
3076
|
+
context.fillStyle = color;
|
|
3085
3077
|
context.fillRect(-.5, -.5, 1, 1);
|
|
3086
3078
|
}
|
|
3087
3079
|
else
|
|
@@ -3100,7 +3092,8 @@ class TileLayer extends EngineObject
|
|
|
3100
3092
|
* @param {Vector2} [size=new Vector2(1,1)]
|
|
3101
3093
|
* @param {Color} [color=new Color(1,1,1)]
|
|
3102
3094
|
* @param {Number} [angle=0] */
|
|
3103
|
-
drawRect(pos, size, color, angle)
|
|
3095
|
+
drawRect(pos, size, color, angle)
|
|
3096
|
+
{ this.drawTile(pos, size, -1, 0, color, angle); }
|
|
3104
3097
|
}
|
|
3105
3098
|
/*
|
|
3106
3099
|
LittleJS Particle System
|
|
@@ -3119,9 +3112,9 @@ class TileLayer extends EngineObject
|
|
|
3119
3112
|
* let pos = vec2(2,3);
|
|
3120
3113
|
* let particleEmiter = new ParticleEmitter
|
|
3121
3114
|
* (
|
|
3122
|
-
* pos, 1, 0, 500, PI, // pos, emitSize, emitTime, emitRate, emiteCone
|
|
3115
|
+
* pos, 0, 1, 0, 500, PI, // pos, angle, emitSize, emitTime, emitRate, emiteCone
|
|
3123
3116
|
* 0, vec2(16), // tileIndex, tileSize
|
|
3124
|
-
* new Color,
|
|
3117
|
+
* new Color(1,1,1), new Color(0,0,0), // colorStartA, colorStartB
|
|
3125
3118
|
* new Color(1,1,1,0), new Color(0,0,0,0), // colorEndA, colorEndB
|
|
3126
3119
|
* 2, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
3127
3120
|
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate,
|
|
@@ -3132,6 +3125,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3132
3125
|
{
|
|
3133
3126
|
/** Create a particle system with the given settings
|
|
3134
3127
|
* @param {Vector2} position - World space position of the emitter
|
|
3128
|
+
* @param {Number} [angle=0] - Angle to emit the particles
|
|
3135
3129
|
* @param {Number} [emitSize=0] - World space size of the emitter (float for circle diameter, vec2 for rect)
|
|
3136
3130
|
* @param {Number} [emitTime=0] - How long to stay alive (0 is forever)
|
|
3137
3131
|
* @param {Number} [emitRate=100] - How many particles per second to spawn, does not emit if 0
|
|
@@ -3161,6 +3155,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3161
3155
|
constructor
|
|
3162
3156
|
(
|
|
3163
3157
|
pos,
|
|
3158
|
+
angle,
|
|
3164
3159
|
emitSize = 0,
|
|
3165
3160
|
emitTime = 0,
|
|
3166
3161
|
emitRate = 100,
|
|
@@ -3188,7 +3183,7 @@ class ParticleEmitter extends EngineObject
|
|
|
3188
3183
|
renderOrder = additive ? 1e9 : 0
|
|
3189
3184
|
)
|
|
3190
3185
|
{
|
|
3191
|
-
super(pos, new Vector2, tileIndex, tileSize,
|
|
3186
|
+
super(pos, new Vector2, tileIndex, tileSize, angle, undefined, renderOrder);
|
|
3192
3187
|
|
|
3193
3188
|
// emitter settings
|
|
3194
3189
|
/** @property {Number} - World space size of the emitter (float for circle diameter, vec2 for rect) */
|
|
@@ -3421,7 +3416,7 @@ function medalsInit(saveName)
|
|
|
3421
3416
|
{
|
|
3422
3417
|
// check if medals are unlocked
|
|
3423
3418
|
medalsSaveName = saveName;
|
|
3424
|
-
debugMedals || medals.forEach(medal=>
|
|
3419
|
+
debugMedals || medals.forEach(medal=> localStorage[medal.storageKey()]);
|
|
3425
3420
|
}
|
|
3426
3421
|
|
|
3427
3422
|
/**
|
|
@@ -3610,6 +3605,9 @@ class Newgrounds
|
|
|
3610
3605
|
const scoreboardResult = this.call('ScoreBoard.getBoards');
|
|
3611
3606
|
this.scoreboards = scoreboardResult ? scoreboardResult.result.data.scoreboards : [];
|
|
3612
3607
|
debugMedals && console.log(this.scoreboards);
|
|
3608
|
+
|
|
3609
|
+
const keepAliveMS = 5 * 60 * 1e3;
|
|
3610
|
+
setInterval(()=>this.call('Gateway.ping', 0, 1), keepAliveMS);
|
|
3613
3611
|
}
|
|
3614
3612
|
|
|
3615
3613
|
/** Send message to unlock a medal by id
|
|
@@ -3621,9 +3619,6 @@ class Newgrounds
|
|
|
3621
3619
|
* @param {Number} value - The score value */
|
|
3622
3620
|
postScore(id, value) { return this.call('ScoreBoard.postScore', {'id':id, 'value':value}, 1); }
|
|
3623
3621
|
|
|
3624
|
-
/** Send message to log a view */
|
|
3625
|
-
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3626
|
-
|
|
3627
3622
|
/** Get scores from a scoreboard
|
|
3628
3623
|
* @param {Number} id - The scoreboard id
|
|
3629
3624
|
* @param {String} [user=0] - A user's id or name
|
|
@@ -3635,6 +3630,9 @@ class Newgrounds
|
|
|
3635
3630
|
getScores(id, user=0, social=0, skip=0, limit=10)
|
|
3636
3631
|
{ return this.call('ScoreBoard.getScores', {'id':id, 'user':user, 'social':social, 'skip':skip, 'limit':limit}); }
|
|
3637
3632
|
|
|
3633
|
+
/** Send message to log a view */
|
|
3634
|
+
logView() { return this.call('App.logView', {'host':this.host}, 1); }
|
|
3635
|
+
|
|
3638
3636
|
/** Send a message to call a component of the Newgrounds API
|
|
3639
3637
|
* @param {String} component - Name of the component
|
|
3640
3638
|
* @param {Object} [parameters=0] - Parameters to use for call
|
|
@@ -3722,8 +3720,8 @@ function glInit()
|
|
|
3722
3720
|
|
|
3723
3721
|
// create the canvas and tile texture
|
|
3724
3722
|
glCanvas = document.createElement('canvas');
|
|
3725
|
-
glContext = glCanvas.getContext('webgl');
|
|
3726
|
-
glCanvas.style =
|
|
3723
|
+
glContext = glCanvas.getContext('webgl', {antialias: false});
|
|
3724
|
+
glCanvas.style = styleCanvas;
|
|
3727
3725
|
glTileTexture = glCreateTexture(tileImage);
|
|
3728
3726
|
|
|
3729
3727
|
// some browsers are much faster without copying the gl buffer so we just overlay it instead
|
|
@@ -3731,25 +3729,24 @@ function glInit()
|
|
|
3731
3729
|
|
|
3732
3730
|
// setup vertex and fragment shaders
|
|
3733
3731
|
glShader = glCreateProgram(
|
|
3734
|
-
'precision highp float;'+
|
|
3735
|
-
'uniform mat4 m;'+
|
|
3736
|
-
'attribute
|
|
3737
|
-
'attribute
|
|
3738
|
-
'
|
|
3739
|
-
'varying
|
|
3740
|
-
'
|
|
3741
|
-
'
|
|
3742
|
-
'
|
|
3743
|
-
'
|
|
3744
|
-
'}' // end of shader
|
|
3732
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3733
|
+
'uniform mat4 m;'+ // transform matrix
|
|
3734
|
+
'attribute vec2 p,t;'+ // position, uv
|
|
3735
|
+
'attribute vec4 c,a;'+ // color, additiveColor
|
|
3736
|
+
'varying vec2 v;'+ // return uv
|
|
3737
|
+
'varying vec4 d,e;'+ // return color, additiveColor
|
|
3738
|
+
'void main(){'+ // shader entry point
|
|
3739
|
+
'gl_Position=m*vec4(p,1,1);'+ // transform position
|
|
3740
|
+
'v=t;d=c;e=a;'+ // pass stuff to fragment shader
|
|
3741
|
+
'}' // end of shader
|
|
3745
3742
|
,
|
|
3746
|
-
'precision highp float;'+
|
|
3747
|
-
'varying vec2 v;'+
|
|
3748
|
-
'varying vec4 d,e;'+
|
|
3749
|
-
'uniform sampler2D
|
|
3750
|
-
'void main(){'+
|
|
3751
|
-
'gl_FragColor=texture2D(
|
|
3752
|
-
'}'
|
|
3743
|
+
'precision highp float;'+ // use highp for better accuracy
|
|
3744
|
+
'varying vec2 v;'+ // uv
|
|
3745
|
+
'varying vec4 d,e;'+ // color, additiveColor
|
|
3746
|
+
'uniform sampler2D s;'+ // texture
|
|
3747
|
+
'void main(){'+ // shader entry point
|
|
3748
|
+
'gl_FragColor=texture2D(s,v)*d+e;'+ // modulate texture by color plus additive
|
|
3749
|
+
'}' // end of shader
|
|
3753
3750
|
);
|
|
3754
3751
|
|
|
3755
3752
|
// init buffers
|
|
@@ -3767,12 +3764,10 @@ function glInit()
|
|
|
3767
3764
|
glContext.vertexAttribPointer(location, size, type, normalize, gl_VERTEX_BYTE_STRIDE, offset);
|
|
3768
3765
|
offset += size*typeSize;
|
|
3769
3766
|
}
|
|
3770
|
-
initVertexAttribArray('a', gl_FLOAT, 4, 1); // angle
|
|
3771
3767
|
initVertexAttribArray('p', gl_FLOAT, 4, 2); // position
|
|
3772
|
-
initVertexAttribArray('s', gl_FLOAT, 4, 2); // size
|
|
3773
3768
|
initVertexAttribArray('t', gl_FLOAT, 4, 2); // texture coords
|
|
3774
3769
|
initVertexAttribArray('c', gl_UNSIGNED_BYTE, 1, 4, 1); // color
|
|
3775
|
-
initVertexAttribArray('
|
|
3770
|
+
initVertexAttribArray('a', gl_UNSIGNED_BYTE, 1, 4, 1); // additiveColor
|
|
3776
3771
|
}
|
|
3777
3772
|
|
|
3778
3773
|
/** Set the WebGl blend mode, normally you should call setBlendMode instead
|
|
@@ -3887,6 +3882,7 @@ function glPreRender(width, height, cameraX, cameraY, cameraScale)
|
|
|
3887
3882
|
|
|
3888
3883
|
// clear and set to same size as main canvas
|
|
3889
3884
|
glContext.viewport(0, 0, glCanvas.width = width, glCanvas.height = height);
|
|
3885
|
+
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3890
3886
|
|
|
3891
3887
|
// set up the shader
|
|
3892
3888
|
glContext.bindTexture(gl_TEXTURE_2D, glActiveTexture = glTileTexture);
|
|
@@ -3933,12 +3929,10 @@ function glCopyToContext(context, forceDraw)
|
|
|
3933
3929
|
if (!glEnable || !glBatchCount) return;
|
|
3934
3930
|
|
|
3935
3931
|
glFlush();
|
|
3932
|
+
|
|
3933
|
+
// do not draw in overlay mode because the canvas is visible
|
|
3936
3934
|
if (!glOverlay || forceDraw)
|
|
3937
|
-
{
|
|
3938
|
-
// do not draw/clear in overlay mode because the canvas is visible
|
|
3939
3935
|
context.drawImage(glCanvas, 0, 0);
|
|
3940
|
-
glContext.clear(gl_COLOR_BUFFER_BIT);
|
|
3941
|
-
}
|
|
3942
3936
|
}
|
|
3943
3937
|
|
|
3944
3938
|
/** Add a sprite to the gl draw list, used by all gl draw functions
|
|
@@ -3961,51 +3955,49 @@ function glDraw(x, y, sizeX, sizeY, angle, uv0X, uv0Y, uv1X, uv1Y, rgba=0xffffff
|
|
|
3961
3955
|
// flush if there is no room for more verts or if different blend mode
|
|
3962
3956
|
if (glBatchCount == gl_MAX_BATCH || glBatchAdditive != glAdditive)
|
|
3963
3957
|
glFlush();
|
|
3958
|
+
|
|
3959
|
+
// prepare to create the verts from size and angle
|
|
3960
|
+
const c = Math.cos(angle)/2, s = Math.sin(angle)/2;
|
|
3961
|
+
const cx = c*sizeX, cy = c*sizeY, sx = s*sizeX, sy = s*sizeY;
|
|
3964
3962
|
|
|
3965
3963
|
// setup 2 triangles to form a quad
|
|
3966
3964
|
let offset = glBatchCount++ * gl_VERTICES_PER_QUAD * gl_INDICIES_PER_VERT;
|
|
3967
3965
|
|
|
3968
3966
|
// vertex 0
|
|
3969
|
-
glPositionData[offset++] =
|
|
3970
|
-
glPositionData[offset++] =
|
|
3971
|
-
glPositionData[offset++] =
|
|
3972
|
-
|
|
3973
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3967
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3968
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3969
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3970
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3974
3971
|
|
|
3975
3972
|
// vertex 1
|
|
3976
|
-
glPositionData[offset++] =
|
|
3977
|
-
glPositionData[offset++] =
|
|
3978
|
-
glPositionData[offset++] =
|
|
3979
|
-
|
|
3980
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3973
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3974
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3975
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
3976
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3981
3977
|
|
|
3982
3978
|
// vertex 2
|
|
3983
|
-
glPositionData[offset++] =
|
|
3984
|
-
glPositionData[offset++] =
|
|
3985
|
-
glPositionData[offset++] =
|
|
3986
|
-
|
|
3987
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3979
|
+
glPositionData[offset++] = x - cx + sy;
|
|
3980
|
+
glPositionData[offset++] = y + cy + sx;
|
|
3981
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv0Y;
|
|
3982
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3988
3983
|
|
|
3989
3984
|
// vertex 0
|
|
3990
|
-
glPositionData[offset++] =
|
|
3991
|
-
glPositionData[offset++] =
|
|
3992
|
-
glPositionData[offset++] =
|
|
3993
|
-
|
|
3994
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3985
|
+
glPositionData[offset++] = x - cx - sy;
|
|
3986
|
+
glPositionData[offset++] = y - cy + sx;
|
|
3987
|
+
glPositionData[offset++] = uv0X; glPositionData[offset++] = uv1Y;
|
|
3988
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3995
3989
|
|
|
3996
3990
|
// vertex 3
|
|
3997
|
-
glPositionData[offset++] =
|
|
3998
|
-
glPositionData[offset++] =
|
|
3999
|
-
glPositionData[offset++] =
|
|
4000
|
-
|
|
4001
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3991
|
+
glPositionData[offset++] = x + cx - sy;
|
|
3992
|
+
glPositionData[offset++] = y - cy - sx;
|
|
3993
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv1Y;
|
|
3994
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
4002
3995
|
|
|
4003
3996
|
// vertex 1
|
|
4004
|
-
glPositionData[offset++] =
|
|
4005
|
-
glPositionData[offset++] =
|
|
4006
|
-
glPositionData[offset++] =
|
|
4007
|
-
|
|
4008
|
-
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
3997
|
+
glPositionData[offset++] = x + cx + sy;
|
|
3998
|
+
glPositionData[offset++] = y + cy - sx;
|
|
3999
|
+
glPositionData[offset++] = uv1X; glPositionData[offset++] = uv0Y;
|
|
4000
|
+
glColorData[offset++] = rgba; glColorData[offset++] = rgbaAdditive;
|
|
4009
4001
|
}
|
|
4010
4002
|
|
|
4011
4003
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4037,9 +4029,9 @@ gl_LINK_STATUS = 35714,
|
|
|
4037
4029
|
|
|
4038
4030
|
// constants for batch rendering
|
|
4039
4031
|
gl_VERTICES_PER_QUAD = 6,
|
|
4040
|
-
gl_INDICIES_PER_VERT =
|
|
4032
|
+
gl_INDICIES_PER_VERT = 6,
|
|
4041
4033
|
gl_MAX_BATCH = 1<<16,
|
|
4042
|
-
gl_VERTEX_BYTE_STRIDE =
|
|
4034
|
+
gl_VERTEX_BYTE_STRIDE = (4 * 2) * 2 + (4) * 2; // vec2 * 2 + (char * 4) * 2
|
|
4043
4035
|
/*
|
|
4044
4036
|
LittleJS - The Tiny JavaScript Game Engine That Can!
|
|
4045
4037
|
MIT License - Copyright 2021 Frank Force
|
|
@@ -4064,7 +4056,7 @@ gl_VERTEX_BYTE_STRIDE = 4 + (4 * 2) * 3 + (4) * 2; // float + vec2 * 3 + (char *
|
|
|
4064
4056
|
const engineName = 'LittleJS';
|
|
4065
4057
|
|
|
4066
4058
|
/** Version of engine */
|
|
4067
|
-
const engineVersion = '1.
|
|
4059
|
+
const engineVersion = '1.2.8';
|
|
4068
4060
|
|
|
4069
4061
|
/** Frames per second to update objects
|
|
4070
4062
|
* @default */
|
|
@@ -4101,7 +4093,7 @@ let averageFPS, drawCount;
|
|
|
4101
4093
|
// css text used for elements created by engine
|
|
4102
4094
|
const styleBody = 'margin:0;overflow:hidden;background:#000' +
|
|
4103
4095
|
';touch-action:none;user-select:none;-webkit-user-select:none;-moz-user-select:none';
|
|
4104
|
-
const
|
|
4096
|
+
const styleCanvas = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)';
|
|
4105
4097
|
|
|
4106
4098
|
///////////////////////////////////////////////////////////////////////////////
|
|
4107
4099
|
|
|
@@ -4126,7 +4118,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4126
4118
|
document.body.style = styleBody;
|
|
4127
4119
|
document.body.appendChild(mainCanvas = document.createElement('canvas'));
|
|
4128
4120
|
mainContext = mainCanvas.getContext('2d');
|
|
4129
|
-
mainCanvas.style =
|
|
4121
|
+
mainCanvas.style = styleCanvas;
|
|
4130
4122
|
|
|
4131
4123
|
// init stuff and start engine
|
|
4132
4124
|
debugInit();
|
|
@@ -4135,7 +4127,7 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4135
4127
|
// create overlay canvas for hud to appear above gl canvas
|
|
4136
4128
|
document.body.appendChild(overlayCanvas = document.createElement('canvas'));
|
|
4137
4129
|
overlayContext = overlayCanvas.getContext('2d');
|
|
4138
|
-
overlayCanvas.style =
|
|
4130
|
+
overlayCanvas.style = styleCanvas;
|
|
4139
4131
|
|
|
4140
4132
|
gameInit();
|
|
4141
4133
|
touchGamepadCreate();
|
|
@@ -4145,13 +4137,11 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4145
4137
|
// main update loop
|
|
4146
4138
|
const engineUpdate = (frameTimeMS=0)=>
|
|
4147
4139
|
{
|
|
4148
|
-
requestAnimationFrame(engineUpdate);
|
|
4149
|
-
|
|
4150
4140
|
// update time keeping
|
|
4151
4141
|
let frameTimeDeltaMS = frameTimeMS - frameTimeLastMS;
|
|
4152
4142
|
frameTimeLastMS = frameTimeMS;
|
|
4153
4143
|
if (debug || showWatermark)
|
|
4154
|
-
averageFPS = lerp(.05, 1e3/(frameTimeDeltaMS||1)
|
|
4144
|
+
averageFPS = lerp(.05, averageFPS || 0, 1e3/(frameTimeDeltaMS||1));
|
|
4155
4145
|
if (debug)
|
|
4156
4146
|
frameTimeDeltaMS *= keyIsDown(107) ? 5 : keyIsDown(109) ? .2 : 1; // +/- to speed/slow time
|
|
4157
4147
|
timeReal += frameTimeDeltaMS / 1e3;
|
|
@@ -4245,6 +4235,8 @@ function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRender
|
|
|
4245
4235
|
overlayContext.fillText(text, mainCanvas.width-2, 2);
|
|
4246
4236
|
drawCount = 0;
|
|
4247
4237
|
}
|
|
4238
|
+
|
|
4239
|
+
requestAnimationFrame(engineUpdate);
|
|
4248
4240
|
}
|
|
4249
4241
|
|
|
4250
4242
|
// set tile image source to load the image and start the engine
|