littlejsengine 1.18.18 → 1.18.21
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 +9 -1
- package/dist/littlejs.d.ts +84 -19
- package/dist/littlejs.esm.js +233 -53
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +228 -53
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +225 -53
- package/package.json +1 -1
- package/plugins/box2d.js +12 -11
- package/plugins/newgrounds.js +2 -1
- package/plugins/pluginExport.js +5 -0
- package/plugins/postProcess.js +5 -4
- package/plugins/threejs.js +155 -0
- package/plugins/tweenSystem.js +5 -1
- package/plugins/uiSystem.js +4 -3
- package/src/engine.js +6 -2
- package/src/engineAudio.js +2 -2
- package/src/engineBuild.mjs +3 -2
- package/src/engineDebug.js +3 -0
- package/src/engineDraw.js +8 -8
- package/src/engineInput.js +8 -4
- package/src/engineMath.js +2 -2
- package/src/engineObject.js +0 -2
- package/src/engineParticles.js +5 -6
- package/src/engineSettings.js +1 -0
- package/src/engineTileLayer.js +10 -6
- package/src/engineUtilities.js +1 -1
package/dist/littlejs.release.js
CHANGED
|
@@ -35,7 +35,7 @@ const engineName = 'LittleJS';
|
|
|
35
35
|
* @type {string}
|
|
36
36
|
* @default
|
|
37
37
|
* @memberof Engine */
|
|
38
|
-
const engineVersion = '1.18.
|
|
38
|
+
const engineVersion = '1.18.21';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -474,6 +474,11 @@ function engineObjectsUpdate()
|
|
|
474
474
|
// get list of solid objects for physics optimization
|
|
475
475
|
engineObjectsCollide = engineObjects.filter(o=>o.collideSolidObjects);
|
|
476
476
|
|
|
477
|
+
// update physics before object update
|
|
478
|
+
for (const o of engineObjects)
|
|
479
|
+
if (!o.parent && !o.destroyed)
|
|
480
|
+
o.updatePhysics();
|
|
481
|
+
|
|
477
482
|
// recursive object update
|
|
478
483
|
function updateChildObject(o)
|
|
479
484
|
{
|
|
@@ -489,7 +494,6 @@ function engineObjectsUpdate()
|
|
|
489
494
|
|
|
490
495
|
// update top level objects
|
|
491
496
|
o.update();
|
|
492
|
-
o.updatePhysics();
|
|
493
497
|
for (const child of o.children)
|
|
494
498
|
updateChildObject(child);
|
|
495
499
|
o.updateTransforms();
|
|
@@ -815,7 +819,7 @@ function smoothStep(percent) { return percent * percent * (3 - 2 * percent); }
|
|
|
815
819
|
* @param {number} value
|
|
816
820
|
* @return {boolean}
|
|
817
821
|
* @memberof Math */
|
|
818
|
-
function isPowerOfTwo(value) { return !(value & (value - 1)); }
|
|
822
|
+
function isPowerOfTwo(value) { return value > 0 && !(value & (value - 1)); }
|
|
819
823
|
|
|
820
824
|
/** Returns the nearest power of two not less than the value
|
|
821
825
|
* @param {number} value
|
|
@@ -892,7 +896,7 @@ function isIntersecting(start, end, pos, size)
|
|
|
892
896
|
* @memberof Math */
|
|
893
897
|
function oscillate(frequency=1, amplitude=1, t=time, offset=0, type=0)
|
|
894
898
|
{
|
|
895
|
-
const phase = (offset + t*frequency
|
|
899
|
+
const phase = mod(offset + t*frequency, 1);
|
|
896
900
|
let value;
|
|
897
901
|
|
|
898
902
|
if (type === 1) // triangle
|
|
@@ -1828,7 +1832,7 @@ const MAGENTA = debugProtectConstant(rgb(1,0,1));
|
|
|
1828
1832
|
class Timer
|
|
1829
1833
|
{
|
|
1830
1834
|
/** Create a timer object set time passed in
|
|
1831
|
-
* @param {number} [timeLeft] - How much time left before the timer
|
|
1835
|
+
* @param {number} [timeLeft] - How much time left before the timer is elapsed in seconds (undefined = unset)
|
|
1832
1836
|
* @param {boolean} [useRealTime] - Should the timer keep running even when the game is paused? (useful for UI) */
|
|
1833
1837
|
constructor(timeLeft, useRealTime=false)
|
|
1834
1838
|
{
|
|
@@ -2427,6 +2431,7 @@ let vibrateEnable = true;
|
|
|
2427
2431
|
let soundEnable = true;
|
|
2428
2432
|
|
|
2429
2433
|
/** Volume scale to apply to all sound, music and speech
|
|
2434
|
+
* Use setSoundVolume to also update the audio master gain immediately
|
|
2430
2435
|
* @type {number}
|
|
2431
2436
|
* @default
|
|
2432
2437
|
* @memberof Settings */
|
|
@@ -2981,8 +2986,6 @@ class EngineObject
|
|
|
2981
2986
|
|
|
2982
2987
|
// notify objects of collision and check if should be resolved
|
|
2983
2988
|
const collide1 = this.collideWithObject(o);
|
|
2984
|
-
// callback may have destroyed us; stop resolving against more objects
|
|
2985
|
-
if (this.destroyed) return;
|
|
2986
2989
|
const collide2 = o.collideWithObject(this);
|
|
2987
2990
|
if (!collide1 || !collide2) continue;
|
|
2988
2991
|
|
|
@@ -3508,6 +3511,14 @@ class TileInfo
|
|
|
3508
3511
|
return this.offset(new Vector2(x));
|
|
3509
3512
|
}
|
|
3510
3513
|
|
|
3514
|
+
/**
|
|
3515
|
+
* Returns a tile info for an index using this tile as reference
|
|
3516
|
+
* @param {Vector2|number} [index=0]
|
|
3517
|
+
* @return {TileInfo}
|
|
3518
|
+
*/
|
|
3519
|
+
index(index)
|
|
3520
|
+
{ return tile(index, this.size, this.textureInfo, this.padding, this.bleed); }
|
|
3521
|
+
|
|
3511
3522
|
/**
|
|
3512
3523
|
* Set this tile to use a full image in a texture info
|
|
3513
3524
|
* @param {TextureInfo} [textureInfo]
|
|
@@ -3521,14 +3532,6 @@ class TileInfo
|
|
|
3521
3532
|
this.bleed = this.padding = 0;
|
|
3522
3533
|
return this;
|
|
3523
3534
|
}
|
|
3524
|
-
|
|
3525
|
-
/**
|
|
3526
|
-
* Returns a tile info for an index using this tile as reference
|
|
3527
|
-
* @param {Vector2|number} [index=0]
|
|
3528
|
-
* @return {TileInfo}
|
|
3529
|
-
*/
|
|
3530
|
-
tile(index)
|
|
3531
|
-
{ return tile(index, this.size, this.textureInfo, this.padding, this.bleed); }
|
|
3532
3535
|
}
|
|
3533
3536
|
|
|
3534
3537
|
/**
|
|
@@ -5226,7 +5229,10 @@ function inputInit()
|
|
|
5226
5229
|
{
|
|
5227
5230
|
inputData[0][e.code] = (inputData[0][e.code]&2) | 4;
|
|
5228
5231
|
if (inputWASDEmulateDirection)
|
|
5229
|
-
|
|
5232
|
+
{
|
|
5233
|
+
const remap = remapKey(e.code);
|
|
5234
|
+
inputData[0][remap] = (inputData[0][remap]&2) | 4;
|
|
5235
|
+
}
|
|
5230
5236
|
}
|
|
5231
5237
|
function remapKey(k)
|
|
5232
5238
|
{
|
|
@@ -5301,7 +5307,7 @@ function inputInit()
|
|
|
5301
5307
|
document.addEventListener('touchend', (e)=> handleTouch(e), { passive: false });
|
|
5302
5308
|
|
|
5303
5309
|
// handle all touch events the same way
|
|
5304
|
-
let wasTouching;
|
|
5310
|
+
let wasTouching, touchIdentifier;
|
|
5305
5311
|
function handleTouch(e)
|
|
5306
5312
|
{
|
|
5307
5313
|
if (!touchInputEnable) return;
|
|
@@ -5332,10 +5338,11 @@ function inputInit()
|
|
|
5332
5338
|
const pos = vec2(gameTouches[0].clientX, gameTouches[0].clientY);
|
|
5333
5339
|
const mousePosScreenLast = mousePosScreen;
|
|
5334
5340
|
mousePosScreen = mouseEventToScreen(pos);
|
|
5335
|
-
if (wasTouching)
|
|
5341
|
+
if (wasTouching && gameTouches[0].identifier === touchIdentifier)
|
|
5336
5342
|
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
5337
|
-
else
|
|
5343
|
+
else if (!wasTouching)
|
|
5338
5344
|
inputData[0][button] = 3;
|
|
5345
|
+
touchIdentifier = gameTouches[0].identifier;
|
|
5339
5346
|
}
|
|
5340
5347
|
else if (wasTouching)
|
|
5341
5348
|
inputData[0][button] = inputData[0][button] & 2 | 4;
|
|
@@ -6493,7 +6500,7 @@ class SoundInstance
|
|
|
6493
6500
|
* @param {number} [rate] - How quickly to speak
|
|
6494
6501
|
* @param {number} [pitch] - How much to change the pitch by
|
|
6495
6502
|
* @param {string} [language] - The language/accent to use (examples: en, it, ru, ja, zh)
|
|
6496
|
-
* @return {SpeechSynthesisUtterance} - The utterance that was spoken
|
|
6503
|
+
* @return {SpeechSynthesisUtterance|undefined} - The utterance that was spoken, or undefined if speech is unavailable
|
|
6497
6504
|
* @memberof Audio */
|
|
6498
6505
|
function speak(text, volume=1, rate=1, pitch=1, language='')
|
|
6499
6506
|
{
|
|
@@ -6546,7 +6553,7 @@ function getNoteFrequency(semitoneOffset, rootFrequency=220)
|
|
|
6546
6553
|
* @param {number} [pan] - How much to apply stereo panning
|
|
6547
6554
|
* @param {boolean} [loop] - True if the sound should loop when it reaches the end
|
|
6548
6555
|
* @param {number} [sampleRate=44100] - Sample rate for the sound
|
|
6549
|
-
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing
|
|
6556
|
+
* @param {GainNode} [gainNode] - Optional gain node for volume control while playing (disconnected when the sound ends)
|
|
6550
6557
|
* @param {number} [offset] - Offset in seconds to start playback from
|
|
6551
6558
|
* @param {AudioEndedCallback} [onended] - Callback for when the sound ends
|
|
6552
6559
|
* @return {AudioBufferSourceNode} - The source node of the sound played, may be undefined if play fails
|
|
@@ -6788,10 +6795,14 @@ function tileCollisionGetData(pos, solidOnly=true)
|
|
|
6788
6795
|
// check all tile collision layers
|
|
6789
6796
|
for (const layer of tileCollisionLayers)
|
|
6790
6797
|
if (!solidOnly || layer.isSolid)
|
|
6791
|
-
if (pos.arrayCheck(layer.size))
|
|
6792
6798
|
{
|
|
6793
|
-
|
|
6794
|
-
|
|
6799
|
+
// convert world pos to layer local space
|
|
6800
|
+
const layerPos = pos.subtract(layer.pos);
|
|
6801
|
+
if (layerPos.arrayCheck(layer.size))
|
|
6802
|
+
{
|
|
6803
|
+
const data = layer.getCollisionData(layerPos);
|
|
6804
|
+
if (data) return data;
|
|
6805
|
+
}
|
|
6795
6806
|
}
|
|
6796
6807
|
return 0;
|
|
6797
6808
|
}
|
|
@@ -7066,7 +7077,7 @@ class TileLayer extends CanvasLayer
|
|
|
7066
7077
|
|
|
7067
7078
|
/** @property {TileInfo} - Default tile info for layer */
|
|
7068
7079
|
this.tileInfo = undefined;
|
|
7069
|
-
/** @property {Array<TileLayerData>} -
|
|
7080
|
+
/** @property {Array<TileLayerData>} - Array of tile data for the layer */
|
|
7070
7081
|
this.data = [];
|
|
7071
7082
|
/** @property {boolean} - Is this layer using a webgl texture? */
|
|
7072
7083
|
this.isUsingWebGL = false;
|
|
@@ -7151,7 +7162,7 @@ class TileLayer extends CanvasLayer
|
|
|
7151
7162
|
|
|
7152
7163
|
const size = this.drawSize || this.size;
|
|
7153
7164
|
const pos = this.pos.add(size.scale(.5));
|
|
7154
|
-
this.draw(pos,
|
|
7165
|
+
this.draw(pos, size, this.color, this.angle, this.mirror, this.additiveColor);
|
|
7155
7166
|
}
|
|
7156
7167
|
|
|
7157
7168
|
/** Called after this layer is redrawn, does nothing by default */
|
|
@@ -7240,7 +7251,7 @@ class TileLayer extends CanvasLayer
|
|
|
7240
7251
|
const d = this.getData(layerPos);
|
|
7241
7252
|
if (!d || !d.tile) return;
|
|
7242
7253
|
|
|
7243
|
-
const tileInfo = this.tileInfo && this.tileInfo.
|
|
7254
|
+
const tileInfo = this.tileInfo && this.tileInfo.index(d.tile);
|
|
7244
7255
|
this.drawLayerTile(drawPos, drawSize, tileInfo, d.color, d.direction*PI/2, d.mirror);
|
|
7245
7256
|
}
|
|
7246
7257
|
|
|
@@ -7534,8 +7545,8 @@ class TileCollisionLayer extends TileLayer
|
|
|
7534
7545
|
* rgb(1,1,1,1), rgb(0,0,0,1), // colorStartA, colorStartB
|
|
7535
7546
|
* rgb(1,1,1,0), rgb(0,0,0,0), // colorEndA, colorEndB
|
|
7536
7547
|
* 1, .2, .2, .1, .05, // particleTime, sizeStart, sizeEnd, particleSpeed, particleAngleSpeed
|
|
7537
|
-
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate
|
|
7538
|
-
* .5, 1 // randomness, collide
|
|
7548
|
+
* .99, 1, 1, PI, .05, // damping, angleDamping, gravityScale, particleCone, fadeRate
|
|
7549
|
+
* .5, 1 // randomness, collide
|
|
7539
7550
|
* );
|
|
7540
7551
|
*/
|
|
7541
7552
|
class ParticleEmitter extends EngineObject
|
|
@@ -7927,13 +7938,12 @@ class Particle
|
|
|
7927
7938
|
const hitLayer = tileCollisionTest(this.pos);
|
|
7928
7939
|
if (!testCollision(oldPos))
|
|
7929
7940
|
{
|
|
7930
|
-
// testCollision already invoked collideCallback with the
|
|
7931
|
-
// correct (this, data, pos) args; no need to re-check here.
|
|
7932
7941
|
// test which side we bounced off (or both if a corner)
|
|
7933
7942
|
const isBlockedX = testCollision(vec2(this.pos.x, oldPos.y));
|
|
7934
7943
|
const isBlockedY = testCollision(vec2(oldPos.x, this.pos.y));
|
|
7935
|
-
|
|
7936
|
-
const
|
|
7944
|
+
// collide callback may hit where the layer test does not, so hitLayer can be undefined
|
|
7945
|
+
const hitRestitution = hitLayer ? max(restitution, hitLayer.restitution) : restitution;
|
|
7946
|
+
const hitFriction = hitLayer ? max(friction, hitLayer.friction) : friction;
|
|
7937
7947
|
if (isBlockedX)
|
|
7938
7948
|
{
|
|
7939
7949
|
// move to previous X position and bounce
|
|
@@ -9582,7 +9592,8 @@ class NewgroundsPlugin
|
|
|
9582
9592
|
return;
|
|
9583
9593
|
}
|
|
9584
9594
|
debugMedals && LOG(xmlHttp.responseText);
|
|
9585
|
-
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
9595
|
+
try { return xmlHttp.responseText && JSON.parse(xmlHttp.responseText); }
|
|
9596
|
+
catch(e) { debugMedals && LOG('newgrounds response is not valid JSON', e); }
|
|
9586
9597
|
}
|
|
9587
9598
|
}
|
|
9588
9599
|
/**
|
|
@@ -9601,8 +9612,8 @@ class NewgroundsPlugin
|
|
|
9601
9612
|
let postProcess;
|
|
9602
9613
|
|
|
9603
9614
|
/////////////////////////////////////////////////////////////////////////
|
|
9604
|
-
/**
|
|
9605
|
-
*
|
|
9615
|
+
/**
|
|
9616
|
+
* Post Process Plugin - Applies a full screen shader to the rendered output
|
|
9606
9617
|
* @memberof PostProcess
|
|
9607
9618
|
*/
|
|
9608
9619
|
class PostProcessPlugin
|
|
@@ -9716,8 +9727,9 @@ class PostProcessPlugin
|
|
|
9716
9727
|
workCanvas.height = mainCanvasSize.y;
|
|
9717
9728
|
glCopyToContext(workContext);
|
|
9718
9729
|
workContext.drawImage(mainCanvas, 0, 0);
|
|
9719
|
-
mainCanvas.width |= 0
|
|
9720
|
-
|
|
9730
|
+
mainCanvas.width |= 0; // setting size clears the main canvas
|
|
9731
|
+
|
|
9732
|
+
|
|
9721
9733
|
// copy work canvas to texture
|
|
9722
9734
|
glContext.texImage2D(glContext.TEXTURE_2D, 0, glContext.RGBA, glContext.RGBA, glContext.UNSIGNED_BYTE, workCanvas);
|
|
9723
9735
|
}
|
|
@@ -10822,7 +10834,7 @@ class UISystemPlugin
|
|
|
10822
10834
|
}
|
|
10823
10835
|
|
|
10824
10836
|
/** Get other axis navigation direction from gamepad or keyboard
|
|
10825
|
-
* @return {
|
|
10837
|
+
* @return {number} */
|
|
10826
10838
|
getNavigationOtherDirection()
|
|
10827
10839
|
{
|
|
10828
10840
|
if (uiSystem.navigationDirection === 2)
|
|
@@ -10911,6 +10923,7 @@ class UISystemPlugin
|
|
|
10911
10923
|
uiSystem.navigationDirection = savedNavigationDirection;
|
|
10912
10924
|
inputClear();
|
|
10913
10925
|
}
|
|
10926
|
+
return confirmMenu;
|
|
10914
10927
|
}
|
|
10915
10928
|
}
|
|
10916
10929
|
|
|
@@ -11344,7 +11357,7 @@ class UITextInput extends UIObject
|
|
|
11344
11357
|
this.onClick();
|
|
11345
11358
|
}
|
|
11346
11359
|
|
|
11347
|
-
/** Stop editing the text
|
|
11360
|
+
/** Stop editing the text */
|
|
11348
11361
|
stopEditing()
|
|
11349
11362
|
{
|
|
11350
11363
|
if (!this.isKeyInputObject())
|
|
@@ -11507,7 +11520,7 @@ class UICheckbox extends UIObject
|
|
|
11507
11520
|
ASSERT(isStringLike(text), 'ui checkbox must be a string');
|
|
11508
11521
|
ASSERT(isColor(color), 'ui checkbox color must be a color');
|
|
11509
11522
|
|
|
11510
|
-
/** @property {boolean} -
|
|
11523
|
+
/** @property {boolean} - Is the checkbox currently checked? */
|
|
11511
11524
|
this.checked = checked;
|
|
11512
11525
|
// set properties
|
|
11513
11526
|
this.text = text;
|
|
@@ -12272,7 +12285,7 @@ class Box2dObject extends EngineObject
|
|
|
12272
12285
|
shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
|
|
12273
12286
|
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
12274
12287
|
fixtures.push(f);
|
|
12275
|
-
|
|
12288
|
+
edgePoints.push(points[i].copy());
|
|
12276
12289
|
}
|
|
12277
12290
|
this.edgeLoops.push(edgePoints);
|
|
12278
12291
|
return fixtures;
|
|
@@ -12338,7 +12351,7 @@ class Box2dObject extends EngineObject
|
|
|
12338
12351
|
/** Sets the position
|
|
12339
12352
|
* @param {Vector2} pos */
|
|
12340
12353
|
setPosition(pos)
|
|
12341
|
-
{ this.setTransform(pos, this.body.GetAngle()); }
|
|
12354
|
+
{ this.setTransform(pos, -this.body.GetAngle()); }
|
|
12342
12355
|
|
|
12343
12356
|
/** Sets the angle
|
|
12344
12357
|
* @param {number} angle */
|
|
@@ -12435,6 +12448,7 @@ class Box2dObject extends EngineObject
|
|
|
12435
12448
|
filter.set_categoryBits(categoryBits);
|
|
12436
12449
|
filter.set_maskBits(0xffff & ~ignoreCategoryBits);
|
|
12437
12450
|
filter.set_groupIndex(groupIndex);
|
|
12451
|
+
fixture.SetFilterData(filter); // applies and refilters contacts
|
|
12438
12452
|
});
|
|
12439
12453
|
}
|
|
12440
12454
|
|
|
@@ -12970,7 +12984,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
12970
12984
|
jointDef.set_bodyB(objectB.body);
|
|
12971
12985
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
12972
12986
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
12973
|
-
jointDef.set_referenceAngle(
|
|
12987
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
12974
12988
|
jointDef.set_collideConnected(collide);
|
|
12975
12989
|
super(jointDef);
|
|
12976
12990
|
}
|
|
@@ -13117,14 +13131,14 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
13117
13131
|
anchor ||= box2d.vec2From(objectB.body.GetPosition());
|
|
13118
13132
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
13119
13133
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
13120
|
-
const localAxisA =
|
|
13134
|
+
const localAxisA = objectA.worldToLocalVector(worldAxis);
|
|
13121
13135
|
const jointDef = new box2d.instance.b2PrismaticJointDef();
|
|
13122
13136
|
jointDef.set_bodyA(objectA.body);
|
|
13123
13137
|
jointDef.set_bodyB(objectB.body);
|
|
13124
13138
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
13125
13139
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
13126
13140
|
jointDef.set_localAxisA(box2d.vec2dTo(localAxisA));
|
|
13127
|
-
jointDef.set_referenceAngle(
|
|
13141
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
13128
13142
|
jointDef.set_collideConnected(collide);
|
|
13129
13143
|
super(jointDef);
|
|
13130
13144
|
}
|
|
@@ -13227,7 +13241,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
13227
13241
|
anchor ||= box2d.vec2From(objectB.body.GetPosition());
|
|
13228
13242
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
13229
13243
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
13230
|
-
const localAxisA =
|
|
13244
|
+
const localAxisA = objectA.worldToLocalVector(worldAxis);
|
|
13231
13245
|
const jointDef = new box2d.instance.b2WheelJointDef();
|
|
13232
13246
|
jointDef.set_bodyA(objectA.body);
|
|
13233
13247
|
jointDef.set_bodyB(objectB.body);
|
|
@@ -13327,7 +13341,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
13327
13341
|
jointDef.set_bodyB(objectB.body);
|
|
13328
13342
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
13329
13343
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
13330
|
-
jointDef.set_referenceAngle(
|
|
13344
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
13331
13345
|
jointDef.set_collideConnected(collide);
|
|
13332
13346
|
super(jointDef);
|
|
13333
13347
|
}
|
|
@@ -13774,7 +13788,7 @@ class Box2dPlugin
|
|
|
13774
13788
|
* @param {number} [lineWidth]
|
|
13775
13789
|
* @param {boolean} [useWebGL=glEnable]
|
|
13776
13790
|
* @param {CanvasRenderingContext2D} [context] */
|
|
13777
|
-
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1,
|
|
13791
|
+
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, useWebGL, context)
|
|
13778
13792
|
{
|
|
13779
13793
|
const shape = box2d.castShapeObject(fixture.GetShape());
|
|
13780
13794
|
switch (shape.GetType())
|
|
@@ -13784,20 +13798,20 @@ class Box2dPlugin
|
|
|
13784
13798
|
let points = [];
|
|
13785
13799
|
for (let i=shape.GetVertexCount(); i--;)
|
|
13786
13800
|
points.push(box2d.vec2From(shape.GetVertex(i)));
|
|
13787
|
-
drawPoly(points, color, lineWidth, lineColor, pos, angle,
|
|
13801
|
+
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, false, context);
|
|
13788
13802
|
break;
|
|
13789
13803
|
}
|
|
13790
13804
|
case box2d.instance.b2Shape.e_circle:
|
|
13791
13805
|
{
|
|
13792
13806
|
const radius = shape.get_m_radius();
|
|
13793
|
-
drawCircle(pos, radius*2, color, lineWidth, lineColor,
|
|
13807
|
+
drawCircle(pos, radius*2, color, lineWidth, lineColor, useWebGL, false, context);
|
|
13794
13808
|
break;
|
|
13795
13809
|
}
|
|
13796
13810
|
case box2d.instance.b2Shape.e_edge:
|
|
13797
13811
|
{
|
|
13798
13812
|
const v1 = box2d.vec2From(shape.get_m_vertex1());
|
|
13799
13813
|
const v2 = box2d.vec2From(shape.get_m_vertex2());
|
|
13800
|
-
drawLine(v1, v2, lineWidth, lineColor, pos, angle,
|
|
13814
|
+
drawLine(v1, v2, lineWidth, lineColor, pos, angle, useWebGL, false, context);
|
|
13801
13815
|
break;
|
|
13802
13816
|
}
|
|
13803
13817
|
}
|
|
@@ -14628,7 +14642,11 @@ function tweenProperty(target, propertyPath, start, end, duration = 1, options =
|
|
|
14628
14642
|
const callback = (value) =>
|
|
14629
14643
|
{
|
|
14630
14644
|
let obj = target;
|
|
14631
|
-
for (const k of parts)
|
|
14645
|
+
for (const k of parts)
|
|
14646
|
+
{
|
|
14647
|
+
obj = obj[k];
|
|
14648
|
+
ASSERT(obj != null, 'tweenProperty path does not resolve: ' + propertyPath);
|
|
14649
|
+
}
|
|
14632
14650
|
obj[lastKey] = value;
|
|
14633
14651
|
};
|
|
14634
14652
|
return new Tween(callback, start, end, duration, options);
|
|
@@ -15511,3 +15529,157 @@ class PathFinder
|
|
|
15511
15529
|
}
|
|
15512
15530
|
}
|
|
15513
15531
|
|
|
15532
|
+
/**
|
|
15533
|
+
* LittleJS Three.js Plugin
|
|
15534
|
+
* - Renders a three.js scene on a canvas behind the LittleJS canvases
|
|
15535
|
+
* - The three.js module is passed in by the user, nothing is bundled
|
|
15536
|
+
* - Keep canvasClearColor transparent so the 3D scene shows through
|
|
15537
|
+
* - Aligned camera mode locks the 3D camera to the LittleJS 2D camera
|
|
15538
|
+
* - ThreeJSObject lets LittleJS physics drive a three.js mesh
|
|
15539
|
+
* - Call new ThreeJSPlugin(THREE) in gameInit to set up
|
|
15540
|
+
* @namespace ThreeJS
|
|
15541
|
+
*/
|
|
15542
|
+
|
|
15543
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
15544
|
+
|
|
15545
|
+
/** Global ThreeJS plugin object
|
|
15546
|
+
* @type {ThreeJSPlugin}
|
|
15547
|
+
* @memberof ThreeJS */
|
|
15548
|
+
let threeJS;
|
|
15549
|
+
|
|
15550
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
15551
|
+
/**
|
|
15552
|
+
* ThreeJS Plugin - Renders a three.js scene behind the LittleJS canvas
|
|
15553
|
+
* @example
|
|
15554
|
+
* // in gameInit, with three.js loaded by the user
|
|
15555
|
+
* new ThreeJSPlugin(THREE);
|
|
15556
|
+
* threeJS.scene.add(new THREE.AmbientLight);
|
|
15557
|
+
* @memberof ThreeJS
|
|
15558
|
+
*/
|
|
15559
|
+
class ThreeJSPlugin
|
|
15560
|
+
{
|
|
15561
|
+
/** Set up the three.js rendering layer, call in gameInit
|
|
15562
|
+
* @param {Object} THREE - The three.js module, supplied by the user
|
|
15563
|
+
* @param {number} [cameraFOV] - Vertical field of view in degrees */
|
|
15564
|
+
constructor(THREE, cameraFOV=60)
|
|
15565
|
+
{
|
|
15566
|
+
ASSERT(!threeJS, 'ThreeJS plugin already initialized');
|
|
15567
|
+
threeJS = this;
|
|
15568
|
+
if (headlessMode) return;
|
|
15569
|
+
ASSERT(mainCanvas, 'ThreeJS plugin must be created after engineInit, call in gameInit');
|
|
15570
|
+
ASSERT(THREE && THREE.WebGLRenderer, 'three.js module must be passed in');
|
|
15571
|
+
|
|
15572
|
+
/** @property {Object} - The three.js module passed into the constructor */
|
|
15573
|
+
this.THREE = THREE;
|
|
15574
|
+
/** @property {Object} - The three.js renderer */
|
|
15575
|
+
this.renderer = new THREE.WebGLRenderer({antialias: true});
|
|
15576
|
+
/** @property {Object} - The three.js scene, add lights and meshes here */
|
|
15577
|
+
this.scene = new THREE.Scene();
|
|
15578
|
+
/** @property {Object} - The three.js perspective camera */
|
|
15579
|
+
this.camera = new THREE.PerspectiveCamera(cameraFOV, 1, .1, 1e3);
|
|
15580
|
+
/** @property {boolean} - Lock the camera to the LittleJS 2D camera so the z=0 plane matches world space */
|
|
15581
|
+
this.cameraAlign2D = true;
|
|
15582
|
+
|
|
15583
|
+
// insert the canvas below the engine canvases and match the layout
|
|
15584
|
+
const threeCanvas = this.renderer.domElement;
|
|
15585
|
+
const rootElement = mainCanvas.parentElement;
|
|
15586
|
+
rootElement.insertBefore(threeCanvas, rootElement.firstChild);
|
|
15587
|
+
threeCanvas.style.cssText = mainCanvas.style.cssText;
|
|
15588
|
+
|
|
15589
|
+
// render automatically each frame after the engine renders
|
|
15590
|
+
engineAddPlugin(undefined, ()=> this.render());
|
|
15591
|
+
}
|
|
15592
|
+
|
|
15593
|
+
/** Position the camera so the z=0 plane exactly matches LittleJS world space,
|
|
15594
|
+
* called automatically when cameraAlign2D is set */
|
|
15595
|
+
alignCamera2D()
|
|
15596
|
+
{
|
|
15597
|
+
const halfHeight = mainCanvasSize.y / 2 / cameraScale; // half visible height in world units
|
|
15598
|
+
const distance = halfHeight / tan(this.camera.fov/2 * PI/180);
|
|
15599
|
+
this.camera.position.set(cameraPos.x, cameraPos.y, distance);
|
|
15600
|
+
// reset all axes in case a free camera was used, littlejs angles are clockwise
|
|
15601
|
+
this.camera.rotation.set(0, 0, -cameraAngle);
|
|
15602
|
+
}
|
|
15603
|
+
|
|
15604
|
+
/** Sync the canvas layout and render the scene, called automatically each frame */
|
|
15605
|
+
render()
|
|
15606
|
+
{
|
|
15607
|
+
if (!this.renderer) return; // headless mode
|
|
15608
|
+
|
|
15609
|
+
// keep renderer size and css in sync with the LittleJS canvas
|
|
15610
|
+
const threeCanvas = this.renderer.domElement;
|
|
15611
|
+
if (threeCanvas.width != mainCanvasSize.x || threeCanvas.height != mainCanvasSize.y)
|
|
15612
|
+
{
|
|
15613
|
+
this.renderer.setSize(mainCanvasSize.x, mainCanvasSize.y, false);
|
|
15614
|
+
this.camera.aspect = mainCanvasSize.x / mainCanvasSize.y;
|
|
15615
|
+
this.camera.updateProjectionMatrix();
|
|
15616
|
+
}
|
|
15617
|
+
if (threeCanvas.style.cssText != mainCanvas.style.cssText)
|
|
15618
|
+
threeCanvas.style.cssText = mainCanvas.style.cssText;
|
|
15619
|
+
|
|
15620
|
+
if (this.cameraAlign2D)
|
|
15621
|
+
this.alignCamera2D();
|
|
15622
|
+
this.renderer.render(this.scene, this.camera);
|
|
15623
|
+
}
|
|
15624
|
+
}
|
|
15625
|
+
|
|
15626
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
15627
|
+
/**
|
|
15628
|
+
* ThreeJS Object - EngineObject that drives a three.js mesh
|
|
15629
|
+
* - LittleJS physics moves the object and the mesh follows automatically
|
|
15630
|
+
* - Destroying the object removes the mesh from the scene
|
|
15631
|
+
* @extends EngineObject
|
|
15632
|
+
* @memberof ThreeJS
|
|
15633
|
+
*/
|
|
15634
|
+
class ThreeJSObject extends EngineObject
|
|
15635
|
+
{
|
|
15636
|
+
/** Create an engine object that drives a three.js mesh
|
|
15637
|
+
* @param {Vector2} [pos] - World space position
|
|
15638
|
+
* @param {Vector2} [size] - World space size
|
|
15639
|
+
* @param {Object} [mesh] - The three.js object3d to drive
|
|
15640
|
+
* @param {number} [z] - Mesh height above the 2D plane */
|
|
15641
|
+
constructor(pos, size, mesh, z=0)
|
|
15642
|
+
{
|
|
15643
|
+
super(pos, size);
|
|
15644
|
+
ASSERT(threeJS, 'ThreeJS plugin must be initialized first');
|
|
15645
|
+
|
|
15646
|
+
/** @property {Object} - The three.js object3d this object drives */
|
|
15647
|
+
this.mesh = mesh;
|
|
15648
|
+
/** @property {number} - Mesh height above the 2D plane */
|
|
15649
|
+
this.z = z;
|
|
15650
|
+
if (mesh)
|
|
15651
|
+
{
|
|
15652
|
+
threeJS.scene.add(mesh);
|
|
15653
|
+
this.syncMesh();
|
|
15654
|
+
}
|
|
15655
|
+
}
|
|
15656
|
+
|
|
15657
|
+
/** Update the object and sync the mesh to its transform */
|
|
15658
|
+
update()
|
|
15659
|
+
{
|
|
15660
|
+
super.update();
|
|
15661
|
+
this.syncMesh();
|
|
15662
|
+
}
|
|
15663
|
+
|
|
15664
|
+
/** Copy this object's transform to the mesh */
|
|
15665
|
+
syncMesh()
|
|
15666
|
+
{
|
|
15667
|
+
if (!this.mesh) return;
|
|
15668
|
+
this.mesh.position.set(this.pos.x, this.pos.y, this.z);
|
|
15669
|
+
this.mesh.rotation.z = -this.angle; // littlejs angles are clockwise
|
|
15670
|
+
}
|
|
15671
|
+
|
|
15672
|
+
/** The mesh is this object's visual, the default 2D rendering is skipped */
|
|
15673
|
+
render() {}
|
|
15674
|
+
|
|
15675
|
+
/** Destroy this object and remove its mesh from the scene
|
|
15676
|
+
* @param {boolean} [immediate] */
|
|
15677
|
+
destroy(immediate)
|
|
15678
|
+
{
|
|
15679
|
+
if (this.destroyed) return;
|
|
15680
|
+
// note: frequently destroyed objects should also dispose geometry and material
|
|
15681
|
+
this.mesh && threeJS.scene.remove(this.mesh);
|
|
15682
|
+
super.destroy(immediate);
|
|
15683
|
+
}
|
|
15684
|
+
}
|
|
15685
|
+
|
package/package.json
CHANGED
package/plugins/box2d.js
CHANGED
|
@@ -345,7 +345,7 @@ class Box2dObject extends EngineObject
|
|
|
345
345
|
shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
|
|
346
346
|
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
347
347
|
fixtures.push(f);
|
|
348
|
-
|
|
348
|
+
edgePoints.push(points[i].copy());
|
|
349
349
|
}
|
|
350
350
|
this.edgeLoops.push(edgePoints);
|
|
351
351
|
return fixtures;
|
|
@@ -411,7 +411,7 @@ class Box2dObject extends EngineObject
|
|
|
411
411
|
/** Sets the position
|
|
412
412
|
* @param {Vector2} pos */
|
|
413
413
|
setPosition(pos)
|
|
414
|
-
{ this.setTransform(pos, this.body.GetAngle()); }
|
|
414
|
+
{ this.setTransform(pos, -this.body.GetAngle()); }
|
|
415
415
|
|
|
416
416
|
/** Sets the angle
|
|
417
417
|
* @param {number} angle */
|
|
@@ -508,6 +508,7 @@ class Box2dObject extends EngineObject
|
|
|
508
508
|
filter.set_categoryBits(categoryBits);
|
|
509
509
|
filter.set_maskBits(0xffff & ~ignoreCategoryBits);
|
|
510
510
|
filter.set_groupIndex(groupIndex);
|
|
511
|
+
fixture.SetFilterData(filter); // applies and refilters contacts
|
|
511
512
|
});
|
|
512
513
|
}
|
|
513
514
|
|
|
@@ -1043,7 +1044,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
1043
1044
|
jointDef.set_bodyB(objectB.body);
|
|
1044
1045
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
1045
1046
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
1046
|
-
jointDef.set_referenceAngle(
|
|
1047
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
1047
1048
|
jointDef.set_collideConnected(collide);
|
|
1048
1049
|
super(jointDef);
|
|
1049
1050
|
}
|
|
@@ -1190,14 +1191,14 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
1190
1191
|
anchor ||= box2d.vec2From(objectB.body.GetPosition());
|
|
1191
1192
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
1192
1193
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
1193
|
-
const localAxisA =
|
|
1194
|
+
const localAxisA = objectA.worldToLocalVector(worldAxis);
|
|
1194
1195
|
const jointDef = new box2d.instance.b2PrismaticJointDef();
|
|
1195
1196
|
jointDef.set_bodyA(objectA.body);
|
|
1196
1197
|
jointDef.set_bodyB(objectB.body);
|
|
1197
1198
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
1198
1199
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
1199
1200
|
jointDef.set_localAxisA(box2d.vec2dTo(localAxisA));
|
|
1200
|
-
jointDef.set_referenceAngle(
|
|
1201
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
1201
1202
|
jointDef.set_collideConnected(collide);
|
|
1202
1203
|
super(jointDef);
|
|
1203
1204
|
}
|
|
@@ -1300,7 +1301,7 @@ class Box2dWheelJoint extends Box2dJoint
|
|
|
1300
1301
|
anchor ||= box2d.vec2From(objectB.body.GetPosition());
|
|
1301
1302
|
const localAnchorA = objectA.worldToLocal(anchor);
|
|
1302
1303
|
const localAnchorB = objectB.worldToLocal(anchor);
|
|
1303
|
-
const localAxisA =
|
|
1304
|
+
const localAxisA = objectA.worldToLocalVector(worldAxis);
|
|
1304
1305
|
const jointDef = new box2d.instance.b2WheelJointDef();
|
|
1305
1306
|
jointDef.set_bodyA(objectA.body);
|
|
1306
1307
|
jointDef.set_bodyB(objectB.body);
|
|
@@ -1400,7 +1401,7 @@ class Box2dWeldJoint extends Box2dJoint
|
|
|
1400
1401
|
jointDef.set_bodyB(objectB.body);
|
|
1401
1402
|
jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
|
|
1402
1403
|
jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
|
|
1403
|
-
jointDef.set_referenceAngle(
|
|
1404
|
+
jointDef.set_referenceAngle(objectB.body.GetAngle() - objectA.body.GetAngle());
|
|
1404
1405
|
jointDef.set_collideConnected(collide);
|
|
1405
1406
|
super(jointDef);
|
|
1406
1407
|
}
|
|
@@ -1847,7 +1848,7 @@ class Box2dPlugin
|
|
|
1847
1848
|
* @param {number} [lineWidth]
|
|
1848
1849
|
* @param {boolean} [useWebGL=glEnable]
|
|
1849
1850
|
* @param {CanvasRenderingContext2D} [context] */
|
|
1850
|
-
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1,
|
|
1851
|
+
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, useWebGL, context)
|
|
1851
1852
|
{
|
|
1852
1853
|
const shape = box2d.castShapeObject(fixture.GetShape());
|
|
1853
1854
|
switch (shape.GetType())
|
|
@@ -1857,20 +1858,20 @@ class Box2dPlugin
|
|
|
1857
1858
|
let points = [];
|
|
1858
1859
|
for (let i=shape.GetVertexCount(); i--;)
|
|
1859
1860
|
points.push(box2d.vec2From(shape.GetVertex(i)));
|
|
1860
|
-
drawPoly(points, color, lineWidth, lineColor, pos, angle,
|
|
1861
|
+
drawPoly(points, color, lineWidth, lineColor, pos, angle, useWebGL, false, context);
|
|
1861
1862
|
break;
|
|
1862
1863
|
}
|
|
1863
1864
|
case box2d.instance.b2Shape.e_circle:
|
|
1864
1865
|
{
|
|
1865
1866
|
const radius = shape.get_m_radius();
|
|
1866
|
-
drawCircle(pos, radius*2, color, lineWidth, lineColor,
|
|
1867
|
+
drawCircle(pos, radius*2, color, lineWidth, lineColor, useWebGL, false, context);
|
|
1867
1868
|
break;
|
|
1868
1869
|
}
|
|
1869
1870
|
case box2d.instance.b2Shape.e_edge:
|
|
1870
1871
|
{
|
|
1871
1872
|
const v1 = box2d.vec2From(shape.get_m_vertex1());
|
|
1872
1873
|
const v2 = box2d.vec2From(shape.get_m_vertex2());
|
|
1873
|
-
drawLine(v1, v2, lineWidth, lineColor, pos, angle,
|
|
1874
|
+
drawLine(v1, v2, lineWidth, lineColor, pos, angle, useWebGL, false, context);
|
|
1874
1875
|
break;
|
|
1875
1876
|
}
|
|
1876
1877
|
}
|
package/plugins/newgrounds.js
CHANGED
|
@@ -193,6 +193,7 @@ class NewgroundsPlugin
|
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
195
195
|
debugMedals && LOG(xmlHttp.responseText);
|
|
196
|
-
return xmlHttp.responseText && JSON.parse(xmlHttp.responseText);
|
|
196
|
+
try { return xmlHttp.responseText && JSON.parse(xmlHttp.responseText); }
|
|
197
|
+
catch(e) { debugMedals && LOG('newgrounds response is not valid JSON', e); }
|
|
197
198
|
}
|
|
198
199
|
}
|