littlejsengine 1.14.23 → 1.14.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/littlejs.d.ts +99 -20
- package/dist/littlejs.esm.js +378 -130
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +373 -130
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +336 -130
- package/examples/box2d/gameObjects.js +6 -10
- package/examples/box2d/scenes.js +2 -2
- package/examples/breakout/gameObjects.js +1 -1
- package/examples/electron/index.html +2 -2
- package/examples/index.html +10 -7
- package/examples/platformer/gameCharacter.js +3 -2
- package/examples/platformer/gameObjects.js +3 -8
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/box2d.js +2 -2
- package/examples/shorts/box2dCar.js +18 -7
- package/examples/shorts/box2dPool.js +108 -0
- package/examples/shorts/debugDraw.js +2 -3
- package/examples/shorts/flappyGame.js +1 -0
- package/examples/shorts/fps.js +1 -1
- package/examples/shorts/hillGlideGame.js +0 -2
- package/examples/shorts/input.js +59 -0
- package/examples/shorts/landerGame.js +1 -3
- package/examples/shorts/medals.js +15 -9
- package/examples/shorts/music.js +15 -17
- package/examples/shorts/musicPlayer.js +24 -24
- package/examples/shorts/platformer.js +0 -2
- package/examples/shorts/slidingPuzzle.js +1 -1
- package/examples/shorts/spaceGame.js +0 -2
- package/examples/shorts/spriteAtlas.js +0 -2
- package/examples/shorts/tiltedView.js +0 -3
- package/examples/shorts/timers.js +1 -2
- package/examples/shorts/topDown.js +0 -2
- package/examples/starter/index.html +2 -2
- package/package.json +1 -1
- package/plugins/box2d.js +170 -50
- package/plugins/pluginExport.js +2 -0
- package/plugins/uiSystem.js +55 -27
- package/src/engine.js +17 -14
- package/src/engineAudio.js +14 -2
- package/src/engineDebug.js +37 -0
- package/src/engineDraw.js +20 -12
- package/src/engineExport.js +3 -0
- package/src/engineInput.js +24 -12
- package/src/engineMedals.js +2 -2
- package/src/engineObject.js +24 -4
- package/src/engineParticles.js +1 -6
- package/src/engineTileLayer.js +2 -0
- package/src/engineUtilities.js +7 -1
package/dist/littlejs.esm.js
CHANGED
|
@@ -33,7 +33,7 @@ const engineName = 'LittleJS';
|
|
|
33
33
|
* @type {string}
|
|
34
34
|
* @default
|
|
35
35
|
* @memberof Engine */
|
|
36
|
-
const engineVersion = '1.
|
|
36
|
+
const engineVersion = '1.15.0';
|
|
37
37
|
|
|
38
38
|
/** Frames per second to update
|
|
39
39
|
* @type {number}
|
|
@@ -164,7 +164,7 @@ function engineAddPlugin(update, render, glContextLost, glContextRestored)
|
|
|
164
164
|
async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources=[], rootElement=document.body)
|
|
165
165
|
{
|
|
166
166
|
ASSERT(!mainContext, 'engine already initialized');
|
|
167
|
-
ASSERT(
|
|
167
|
+
ASSERT(isArray(imageSources), 'pass in images as array');
|
|
168
168
|
|
|
169
169
|
// allow passing in empty functions
|
|
170
170
|
gameInit ||= ()=>{};
|
|
@@ -456,21 +456,24 @@ function engineObjectsUpdate()
|
|
|
456
456
|
// recursive object update
|
|
457
457
|
function updateObject(o)
|
|
458
458
|
{
|
|
459
|
-
if (
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
459
|
+
if (o.destroyed)
|
|
460
|
+
return;
|
|
461
|
+
|
|
462
|
+
o.update();
|
|
463
|
+
for (const child of o.children)
|
|
464
|
+
updateObject(child);
|
|
465
465
|
}
|
|
466
466
|
for (const o of engineObjects)
|
|
467
467
|
{
|
|
468
|
+
if (o.parent)
|
|
469
|
+
continue;
|
|
470
|
+
|
|
468
471
|
// update top level objects
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
472
|
+
o.update();
|
|
473
|
+
o.updatePhysics();
|
|
474
|
+
for (const child of o.children)
|
|
475
|
+
updateObject(child);
|
|
476
|
+
o.updateTransforms();
|
|
474
477
|
}
|
|
475
478
|
|
|
476
479
|
// remove destroyed objects
|
|
@@ -503,7 +506,7 @@ function engineObjectsCollect(pos, size, objects=engineObjects)
|
|
|
503
506
|
else if (size instanceof Vector2) // bounding box test
|
|
504
507
|
{
|
|
505
508
|
for (const o of objects)
|
|
506
|
-
isOverlapping(pos, size
|
|
509
|
+
o.isOverlapping(pos, size) && collectedObjects.push(o);
|
|
507
510
|
}
|
|
508
511
|
else // circle test
|
|
509
512
|
{
|
|
@@ -790,6 +793,12 @@ function LOG(...output) { console.log(...output); }
|
|
|
790
793
|
* @memberof Debug */
|
|
791
794
|
function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
|
|
792
795
|
{
|
|
796
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
797
|
+
ASSERT(isVector2(size), 'size must be a vec2');
|
|
798
|
+
ASSERT(isString(color) || isColor(color), 'color is invalid');
|
|
799
|
+
ASSERT(isNumber(time), 'time must be a number');
|
|
800
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
801
|
+
|
|
793
802
|
if (typeof size === 'number')
|
|
794
803
|
size = vec2(size); // allow passing in floats
|
|
795
804
|
if (isColor(color))
|
|
@@ -810,6 +819,12 @@ function debugRect(pos, size=vec2(), color=WHITE, time=0, angle=0, fill=false)
|
|
|
810
819
|
* @memberof Debug */
|
|
811
820
|
function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
|
|
812
821
|
{
|
|
822
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
823
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
824
|
+
ASSERT(isString(color) || isColor(color), 'color is invalid');
|
|
825
|
+
ASSERT(isNumber(time), 'time must be a number');
|
|
826
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
827
|
+
|
|
813
828
|
if (isColor(color))
|
|
814
829
|
color = color.toString();
|
|
815
830
|
pos = pos.copy();
|
|
@@ -827,6 +842,11 @@ function debugPoly(pos, points, color=WHITE, time=0, angle=0, fill=false)
|
|
|
827
842
|
* @memberof Debug */
|
|
828
843
|
function debugCircle(pos, size=0, color=WHITE, time=0, fill=false)
|
|
829
844
|
{
|
|
845
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
846
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
847
|
+
ASSERT(isString(color) || isColor(color), 'color is invalid');
|
|
848
|
+
ASSERT(isNumber(time), 'time must be a number');
|
|
849
|
+
|
|
830
850
|
if (isColor(color))
|
|
831
851
|
color = color.toString();
|
|
832
852
|
pos = pos.copy();
|
|
@@ -852,6 +872,10 @@ function debugPoint(pos, color, time, angle)
|
|
|
852
872
|
* @memberof Debug */
|
|
853
873
|
function debugLine(posA, posB, color, width=.1, time)
|
|
854
874
|
{
|
|
875
|
+
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
876
|
+
ASSERT(isVector2(posB), 'posB must be sa vec2');
|
|
877
|
+
ASSERT(isNumber(width), 'width must be a number');
|
|
878
|
+
|
|
855
879
|
const halfDelta = vec2((posB.x - posA.x)/2, (posB.y - posA.y)/2);
|
|
856
880
|
const size = vec2(width, halfDelta.length()*2);
|
|
857
881
|
debugRect(posA.add(halfDelta), size, color, time, halfDelta.angle(), true);
|
|
@@ -866,6 +890,11 @@ function debugLine(posA, posB, color, width=.1, time)
|
|
|
866
890
|
* @memberof Debug */
|
|
867
891
|
function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
868
892
|
{
|
|
893
|
+
ASSERT(isVector2(posA), 'posA must be a vec2');
|
|
894
|
+
ASSERT(isVector2(posB), 'posB must be a vec2');
|
|
895
|
+
ASSERT(isVector2(sizeA), 'sizeA must be a vec2');
|
|
896
|
+
ASSERT(isVector2(sizeB), 'sizeB must be a vec2');
|
|
897
|
+
|
|
869
898
|
const minPos = vec2(
|
|
870
899
|
min(posA.x - sizeA.x/2, posB.x - sizeB.x/2),
|
|
871
900
|
min(posA.y - sizeA.y/2, posB.y - sizeB.y/2)
|
|
@@ -888,6 +917,14 @@ function debugOverlap(posA, sizeA, posB, sizeB, color)
|
|
|
888
917
|
* @memberof Debug */
|
|
889
918
|
function debugText(text, pos, size=1, color=WHITE, time=0, angle=0, font='monospace')
|
|
890
919
|
{
|
|
920
|
+
ASSERT(isString(text), 'text must be a string');
|
|
921
|
+
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
922
|
+
ASSERT(isNumber(size), 'size must be a number');
|
|
923
|
+
ASSERT(isString(color) || isColor(color), 'color is invalid');
|
|
924
|
+
ASSERT(isNumber(time), 'time must be a number');
|
|
925
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
926
|
+
ASSERT(isString(font), 'font must be a string');
|
|
927
|
+
|
|
891
928
|
if (isColor(color))
|
|
892
929
|
color = color.toString();
|
|
893
930
|
pos = pos.copy();
|
|
@@ -1341,6 +1378,9 @@ function debugVideoCaptureUpdate()
|
|
|
1341
1378
|
debugVideoCaptureIcon.textContent = '● REC ' + formatTime(debugVideoCaptureTimer);
|
|
1342
1379
|
}
|
|
1343
1380
|
|
|
1381
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
1382
|
+
// debug utility functions
|
|
1383
|
+
|
|
1344
1384
|
// make color constants immutable with debug assertions
|
|
1345
1385
|
function debugProtectConstant(obj)
|
|
1346
1386
|
{
|
|
@@ -1672,6 +1712,13 @@ function isNumber(n) { return typeof n === 'number' && !isNaN(n); }
|
|
|
1672
1712
|
* @memberof Utilities */
|
|
1673
1713
|
function isString(s) { return s !== undefined && s !== null && typeof s.toString() === 'string'; }
|
|
1674
1714
|
|
|
1715
|
+
/**
|
|
1716
|
+
* Check if object is an array
|
|
1717
|
+
* @param {any} a
|
|
1718
|
+
* @return {boolean}
|
|
1719
|
+
* @memberof Utilities */
|
|
1720
|
+
function isArray(a) { return Array.isArray(a); }
|
|
1721
|
+
|
|
1675
1722
|
/**
|
|
1676
1723
|
* @callback LineTestFunction - Checks if a position is colliding
|
|
1677
1724
|
* @param {Vector2} pos
|
|
@@ -2371,7 +2418,6 @@ class Color
|
|
|
2371
2418
|
* @return {string} */
|
|
2372
2419
|
toString(useAlpha = true)
|
|
2373
2420
|
{
|
|
2374
|
-
ASSERT(typeof useAlpha === 'boolean', 'Use alpha boolean is invalid.', useAlpha);
|
|
2375
2421
|
if (debug && !this.isValid())
|
|
2376
2422
|
return `#000`;
|
|
2377
2423
|
const toHex = (c)=> ((c=clamp(c)*255|0)<16 ? '0' : '') + c.toString(16);
|
|
@@ -3269,11 +3315,10 @@ class EngineObject
|
|
|
3269
3315
|
}
|
|
3270
3316
|
|
|
3271
3317
|
/** Update the object physics, called automatically by engine once each frame */
|
|
3272
|
-
|
|
3318
|
+
updatePhysics()
|
|
3273
3319
|
{
|
|
3274
3320
|
// child objects do not have physics
|
|
3275
|
-
|
|
3276
|
-
return;
|
|
3321
|
+
ASSERT(!this.parent);
|
|
3277
3322
|
|
|
3278
3323
|
if (this.clampSpeed)
|
|
3279
3324
|
{
|
|
@@ -3323,7 +3368,7 @@ class EngineObject
|
|
|
3323
3368
|
continue;
|
|
3324
3369
|
|
|
3325
3370
|
// check collision
|
|
3326
|
-
if (!
|
|
3371
|
+
if (!this.isOverlappingObject(o))
|
|
3327
3372
|
continue;
|
|
3328
3373
|
|
|
3329
3374
|
// notify objects of collision and check if should be resolved
|
|
@@ -3471,6 +3516,9 @@ class EngineObject
|
|
|
3471
3516
|
}
|
|
3472
3517
|
}
|
|
3473
3518
|
|
|
3519
|
+
/** Update the object, called automatically by engine once each frame. Does nothing by default. */
|
|
3520
|
+
update() {}
|
|
3521
|
+
|
|
3474
3522
|
/** Render the object, draws a tile by default, automatically called each frame, sorted by renderOrder */
|
|
3475
3523
|
render()
|
|
3476
3524
|
{
|
|
@@ -3536,6 +3584,10 @@ class EngineObject
|
|
|
3536
3584
|
* @return {number} */
|
|
3537
3585
|
getAliveTime() { return time - this.spawnTime; }
|
|
3538
3586
|
|
|
3587
|
+
/** Get the speed of this object
|
|
3588
|
+
* @return {number} */
|
|
3589
|
+
getSpeed() { return this.velocity.length(); }
|
|
3590
|
+
|
|
3539
3591
|
/** Apply acceleration to this object (adjust velocity, not affected by mass)
|
|
3540
3592
|
* @param {Vector2} acceleration */
|
|
3541
3593
|
applyAcceleration(acceleration)
|
|
@@ -3580,6 +3632,20 @@ class EngineObject
|
|
|
3580
3632
|
child.parent = 0;
|
|
3581
3633
|
}
|
|
3582
3634
|
|
|
3635
|
+
/** Check if overlapping another engine object
|
|
3636
|
+
* Collisions are resoloved to prevent overlaps
|
|
3637
|
+
* @param {EngineObject} object
|
|
3638
|
+
* @return {boolean} */
|
|
3639
|
+
isOverlappingObject(object)
|
|
3640
|
+
{ return this.isOverlapping(object.pos, object.size); }
|
|
3641
|
+
|
|
3642
|
+
/** Check if overlapping a point or aligned bounding box
|
|
3643
|
+
* @param {Vector2} pos - Center of box
|
|
3644
|
+
* @param {Vector2} [size=(0,0)] - Size of box, uses a point if undefined
|
|
3645
|
+
* @return {boolean} */
|
|
3646
|
+
isOverlapping(pos, size=vec2())
|
|
3647
|
+
{ return isOverlapping(this.pos, this.size, pos, size); }
|
|
3648
|
+
|
|
3583
3649
|
/** Set how this object collides
|
|
3584
3650
|
* @param {boolean} [collideSolidObjects] - Does it collide with solid objects?
|
|
3585
3651
|
* @param {boolean} [isSolid] - Does it collide with and block other objects? (expensive in large numbers)
|
|
@@ -4034,7 +4100,7 @@ function drawRectGradient(pos, size, colorTop=WHITE, colorBottom=BLACK, angle=0,
|
|
|
4034
4100
|
* @memberof Draw */
|
|
4035
4101
|
function drawLineList(points, width=.1, color, wrap=false, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace, context)
|
|
4036
4102
|
{
|
|
4037
|
-
ASSERT(
|
|
4103
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
4038
4104
|
ASSERT(isNumber(width), 'width must be a number');
|
|
4039
4105
|
ASSERT(isColor(color), 'color is invalid');
|
|
4040
4106
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4140,7 +4206,7 @@ function drawRegularPoly(pos, size=vec2(1), sides=3, color=WHITE, lineWidth=0, l
|
|
|
4140
4206
|
function drawPoly(points, color=WHITE, lineWidth=0, lineColor=BLACK, pos=vec2(), angle=0, useWebGL=glEnable, screenSpace=false, context=undefined)
|
|
4141
4207
|
{
|
|
4142
4208
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
4143
|
-
ASSERT(
|
|
4209
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
4144
4210
|
ASSERT(isColor(color) && isColor(lineColor), 'color is invalid');
|
|
4145
4211
|
ASSERT(isNumber(lineWidth), 'lineWidth must be a number');
|
|
4146
4212
|
ASSERT(isNumber(angle), 'angle must be a number');
|
|
@@ -4292,11 +4358,12 @@ function drawCanvas2D(pos, size, angle=0, mirror=false, drawFunction, screenSpac
|
|
|
4292
4358
|
* @param {string} [font=fontDefault]
|
|
4293
4359
|
* @param {string} [fontStyle]
|
|
4294
4360
|
* @param {number} [maxWidth]
|
|
4361
|
+
* @param {number} [angle]
|
|
4295
4362
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=drawContext]
|
|
4296
4363
|
* @memberof Draw */
|
|
4297
|
-
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, context=drawContext)
|
|
4364
|
+
function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0, context=drawContext)
|
|
4298
4365
|
{
|
|
4299
|
-
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, context);
|
|
4366
|
+
drawTextScreen(text, worldToScreen(pos), size*cameraScale, color, lineWidth*cameraScale, lineColor, textAlign, font, fontStyle, maxWidth, angle, context);
|
|
4300
4367
|
}
|
|
4301
4368
|
|
|
4302
4369
|
/** Draw text on overlay canvas in world space
|
|
@@ -4311,10 +4378,11 @@ function drawText(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, f
|
|
|
4311
4378
|
* @param {string} [font=fontDefault]
|
|
4312
4379
|
* @param {string} [fontStyle]
|
|
4313
4380
|
* @param {number} [maxWidth]
|
|
4381
|
+
* @param {number} [angle]
|
|
4314
4382
|
* @memberof Draw */
|
|
4315
|
-
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth)
|
|
4383
|
+
function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textAlign, font, fontStyle, maxWidth, angle=0)
|
|
4316
4384
|
{
|
|
4317
|
-
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, overlayContext);
|
|
4385
|
+
drawText(text, pos, size, color, lineWidth, lineColor, textAlign, font, fontStyle, maxWidth, angle, overlayContext);
|
|
4318
4386
|
}
|
|
4319
4387
|
|
|
4320
4388
|
/** Draw text on overlay canvas in screen space
|
|
@@ -4329,9 +4397,10 @@ function drawTextOverlay(text, pos, size=1, color, lineWidth=0, lineColor, textA
|
|
|
4329
4397
|
* @param {string} [font=fontDefault]
|
|
4330
4398
|
* @param {string} [fontStyle]
|
|
4331
4399
|
* @param {number} [maxWidth]
|
|
4400
|
+
* @param {number} [angle]
|
|
4332
4401
|
* @param {CanvasRenderingContext2D|OffscreenCanvasRenderingContext2D} [context=overlayContext]
|
|
4333
4402
|
* @memberof Draw */
|
|
4334
|
-
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, context=overlayContext)
|
|
4403
|
+
function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=BLACK, textAlign='center', font=fontDefault, fontStyle='', maxWidth, angle=0, context=overlayContext)
|
|
4335
4404
|
{
|
|
4336
4405
|
ASSERT(isString(text), 'text must be a string');
|
|
4337
4406
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
@@ -4342,6 +4411,7 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4342
4411
|
ASSERT(['left','center','right'].includes(textAlign), 'align must be left, center, or right');
|
|
4343
4412
|
ASSERT(isString(font), 'font must be a string');
|
|
4344
4413
|
ASSERT(isString(fontStyle), 'fontStyle must be a string');
|
|
4414
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
4345
4415
|
|
|
4346
4416
|
context.fillStyle = color.toString();
|
|
4347
4417
|
context.strokeStyle = lineColor.toString();
|
|
@@ -4351,14 +4421,18 @@ function drawTextScreen(text, pos, size=1, color=WHITE, lineWidth=0, lineColor=B
|
|
|
4351
4421
|
context.textBaseline = 'middle';
|
|
4352
4422
|
|
|
4353
4423
|
const lines = (text+'').split('\n');
|
|
4354
|
-
|
|
4355
|
-
|
|
4424
|
+
const posY = pos.y - (lines.length-1) * size/2; // center vertically
|
|
4425
|
+
context.save();
|
|
4426
|
+
context.translate(pos.x, posY);
|
|
4427
|
+
context.rotate(-angle);
|
|
4428
|
+
let yOffset = 0;
|
|
4356
4429
|
lines.forEach(line=>
|
|
4357
4430
|
{
|
|
4358
|
-
lineWidth && context.strokeText(line,
|
|
4359
|
-
context.fillText(line,
|
|
4360
|
-
|
|
4431
|
+
lineWidth && context.strokeText(line, 0, yOffset, maxWidth);
|
|
4432
|
+
context.fillText(line, 0, yOffset, maxWidth);
|
|
4433
|
+
yOffset += size;
|
|
4361
4434
|
});
|
|
4435
|
+
context.restore();
|
|
4362
4436
|
}
|
|
4363
4437
|
|
|
4364
4438
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -4790,6 +4864,11 @@ let mouseDeltaScreen = vec2();
|
|
|
4790
4864
|
* @memberof Input */
|
|
4791
4865
|
let mouseWheel = 0;
|
|
4792
4866
|
|
|
4867
|
+
/** True if mouse was inside the document window, set to false when mouse leaves
|
|
4868
|
+
* @type {boolean}
|
|
4869
|
+
* @memberof Input */
|
|
4870
|
+
let mouseInWindow = true;
|
|
4871
|
+
|
|
4793
4872
|
/** Returns true if user is using gamepad (has more recently pressed a gamepad button)
|
|
4794
4873
|
* @type {boolean}
|
|
4795
4874
|
* @memberof Input */
|
|
@@ -4884,6 +4963,7 @@ function inputInit()
|
|
|
4884
4963
|
document.addEventListener('mousedown', onMouseDown);
|
|
4885
4964
|
document.addEventListener('mouseup', onMouseUp);
|
|
4886
4965
|
document.addEventListener('mousemove', onMouseMove);
|
|
4966
|
+
document.addEventListener('mouseleave', onMouseLeave);
|
|
4887
4967
|
document.addEventListener('wheel', onMouseWheel);
|
|
4888
4968
|
document.addEventListener('contextmenu', onContextMenu);
|
|
4889
4969
|
document.addEventListener('blur', onBlur);
|
|
@@ -4908,14 +4988,14 @@ function inputInit()
|
|
|
4908
4988
|
if (inputWASDEmulateDirection)
|
|
4909
4989
|
inputData[0][remapKey(e.code)] = 4;
|
|
4910
4990
|
}
|
|
4911
|
-
function remapKey(
|
|
4991
|
+
function remapKey(k)
|
|
4912
4992
|
{
|
|
4913
4993
|
// handle remapping wasd keys to directions
|
|
4914
4994
|
return inputWASDEmulateDirection ?
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4995
|
+
k === 'KeyW' ? 'ArrowUp' :
|
|
4996
|
+
k === 'KeyS' ? 'ArrowDown' :
|
|
4997
|
+
k === 'KeyA' ? 'ArrowLeft' :
|
|
4998
|
+
k === 'KeyD' ? 'ArrowRight' : k : k;
|
|
4919
4999
|
}
|
|
4920
5000
|
function onMouseDown(e)
|
|
4921
5001
|
{
|
|
@@ -4929,7 +5009,7 @@ function inputInit()
|
|
|
4929
5009
|
isUsingGamepad = false;
|
|
4930
5010
|
inputData[0][e.button] = 3;
|
|
4931
5011
|
|
|
4932
|
-
|
|
5012
|
+
const mousePosScreenLast = mousePosScreen;
|
|
4933
5013
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
4934
5014
|
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
4935
5015
|
inputPreventDefault && e.button && e.preventDefault();
|
|
@@ -4942,10 +5022,17 @@ function inputInit()
|
|
|
4942
5022
|
}
|
|
4943
5023
|
function onMouseMove(e)
|
|
4944
5024
|
{
|
|
4945
|
-
|
|
5025
|
+
mouseInWindow = true;
|
|
5026
|
+
const mousePosScreenLast = mousePosScreen;
|
|
4946
5027
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
4947
|
-
|
|
5028
|
+
|
|
5029
|
+
// when pointer is locked use movementX/Y for delta
|
|
5030
|
+
const movement = pointerLockIsActive() ?
|
|
5031
|
+
vec2(e.movementX, e.movementY) :
|
|
5032
|
+
mousePosScreen.subtract(mousePosScreenLast);
|
|
5033
|
+
mouseDeltaScreen = mouseDeltaScreen.add(movement);
|
|
4948
5034
|
}
|
|
5035
|
+
function onMouseLeave() { mouseInWindow = false; } // mouse moved off window
|
|
4949
5036
|
function onMouseWheel(e) { mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY); }
|
|
4950
5037
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
4951
5038
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
@@ -5084,7 +5171,7 @@ let touchGamepadTimer = new Timer, touchGamepadButtons = [], touchGamepadStick =
|
|
|
5084
5171
|
function touchGamepadButtonCenter()
|
|
5085
5172
|
{
|
|
5086
5173
|
// draw right face buttons
|
|
5087
|
-
|
|
5174
|
+
const center = vec2(mainCanvasSize.x-touchGamepadSize, mainCanvasSize.y-touchGamepadSize);
|
|
5088
5175
|
if (touchGamepadButtonCount <= 2)
|
|
5089
5176
|
center.x += touchGamepadSize/2;
|
|
5090
5177
|
return center;
|
|
@@ -5094,14 +5181,13 @@ function touchGamepadButtonCenter()
|
|
|
5094
5181
|
function touchInputInit()
|
|
5095
5182
|
{
|
|
5096
5183
|
// add non passive touch event listeners
|
|
5097
|
-
let handleTouch = handleTouchDefault;
|
|
5098
5184
|
document.addEventListener('touchstart', (e) => handleTouch(e), { passive: false });
|
|
5099
5185
|
document.addEventListener('touchmove', (e) => handleTouch(e), { passive: false });
|
|
5100
5186
|
document.addEventListener('touchend', (e) => handleTouch(e), { passive: false });
|
|
5101
5187
|
|
|
5102
5188
|
// handle all touch events the same way
|
|
5103
5189
|
let wasTouching;
|
|
5104
|
-
function
|
|
5190
|
+
function handleTouch(e)
|
|
5105
5191
|
{
|
|
5106
5192
|
if (!touchInputEnable)
|
|
5107
5193
|
return;
|
|
@@ -5252,7 +5338,7 @@ function touchGamepadRender()
|
|
|
5252
5338
|
const buttonSize = touchGamepadButtonCount > 1 ? touchGamepadSize/4 : touchGamepadSize/2;
|
|
5253
5339
|
for (let i=0; i<touchGamepadButtonCount; i++)
|
|
5254
5340
|
{
|
|
5255
|
-
|
|
5341
|
+
const j = mod(i-1, 4);
|
|
5256
5342
|
let button = touchGamepadButtonCount > 2 ?
|
|
5257
5343
|
j : min(j, touchGamepadButtonCount-1);
|
|
5258
5344
|
// fix button locations (swap 2 and 3 to match gamepad layout)
|
|
@@ -5354,6 +5440,10 @@ class Sound
|
|
|
5354
5440
|
{
|
|
5355
5441
|
if (!soundEnable || headlessMode) return;
|
|
5356
5442
|
|
|
5443
|
+
ASSERT(!zzfxSound || isArray(zzfxSound), 'zzfxSound is invalid');
|
|
5444
|
+
ASSERT(isNumber(range), 'range must be a number');
|
|
5445
|
+
ASSERT(isNumber(taper), 'taper must be a number');
|
|
5446
|
+
|
|
5357
5447
|
/** @property {number} - World space max range of sound */
|
|
5358
5448
|
this.range = range;
|
|
5359
5449
|
/** @property {number} - At what percentage of range should it start tapering */
|
|
@@ -5392,6 +5482,12 @@ class Sound
|
|
|
5392
5482
|
*/
|
|
5393
5483
|
play(pos, volume=1, pitch=1, randomnessScale=1, loop=false, paused=false)
|
|
5394
5484
|
{
|
|
5485
|
+
ASSERT(!pos || isVector2(pos), 'pos must be a vec2');
|
|
5486
|
+
ASSERT(isNumber(volume), 'volume must be a number');
|
|
5487
|
+
ASSERT(isNumber(pitch), 'pitch must be a number');
|
|
5488
|
+
ASSERT(isNumber(randomnessScale), 'randomnessScale must be a number');
|
|
5489
|
+
|
|
5490
|
+
|
|
5395
5491
|
if (!soundEnable || headlessMode) return;
|
|
5396
5492
|
if (!this.sampleChannels) return;
|
|
5397
5493
|
|
|
@@ -5437,6 +5533,7 @@ class Sound
|
|
|
5437
5533
|
*/
|
|
5438
5534
|
playNote(semitoneOffset=0, pos, volume)
|
|
5439
5535
|
{
|
|
5536
|
+
ASSERT(isNumber(semitoneOffset), 'semitoneOffset must be a number');
|
|
5440
5537
|
const pitch = getNoteFrequency(semitoneOffset, 1);
|
|
5441
5538
|
return this.play(pos, volume, pitch, 0);
|
|
5442
5539
|
}
|
|
@@ -5445,7 +5542,7 @@ class Sound
|
|
|
5445
5542
|
* @return {number} - How long the sound is in seconds (undefined if loading)
|
|
5446
5543
|
*/
|
|
5447
5544
|
getDuration()
|
|
5448
|
-
{ return this.sampleChannels && this.sampleChannels[0].length / this.sampleRate; }
|
|
5545
|
+
{ return this.sampleChannels && this.sampleRate ? this.sampleChannels[0].length / this.sampleRate : 0; }
|
|
5449
5546
|
|
|
5450
5547
|
/** Check if sound is loaded, for sounds fetched from a url
|
|
5451
5548
|
* @return {boolean} - True if sound is loaded and ready to play
|
|
@@ -5487,6 +5584,7 @@ class SoundWave extends Sound
|
|
|
5487
5584
|
super(undefined, range, taper);
|
|
5488
5585
|
if (!soundEnable || headlessMode) return;
|
|
5489
5586
|
ASSERT(!filename || isString(filename), 'filename must be a string');
|
|
5587
|
+
ASSERT(isNumber(randomness), 'randomness must be a number');
|
|
5490
5588
|
|
|
5491
5589
|
/** @property {SoundLoadCallback} - callback function to call when sound is loaded */
|
|
5492
5590
|
this.onloadCallback = onloadCallback;
|
|
@@ -5703,7 +5801,7 @@ class SoundInstance
|
|
|
5703
5801
|
/** Get the total duration of this sound
|
|
5704
5802
|
* @return {number} - Total duration in seconds
|
|
5705
5803
|
*/
|
|
5706
|
-
getDuration() { return this.sound.getDuration() / this.rate; }
|
|
5804
|
+
getDuration() { return this.rate ? this.sound.getDuration() / this.rate : 0; }
|
|
5707
5805
|
|
|
5708
5806
|
/** Get source of this sound instance
|
|
5709
5807
|
* @return {AudioBufferSourceNode}
|
|
@@ -6017,6 +6115,7 @@ function tileCollisionTest(pos, size=vec2(), object, solidOnly=true)
|
|
|
6017
6115
|
}
|
|
6018
6116
|
|
|
6019
6117
|
/** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
|
|
6118
|
+
* The point will be inside the colliding tile if it hits (may have a tiny shift)
|
|
6020
6119
|
* @param {Vector2} posStart
|
|
6021
6120
|
* @param {Vector2} posEnd
|
|
6022
6121
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
@@ -6590,6 +6689,7 @@ class TileCollisionLayer extends TileLayer
|
|
|
6590
6689
|
}
|
|
6591
6690
|
|
|
6592
6691
|
/** Return the exact position of the boudnary of first tile hit, undefined if nothing was hit.
|
|
6692
|
+
* The point will be inside the colliding tile if it hits (may have a tiny shift)
|
|
6593
6693
|
* @param {Vector2} posStart
|
|
6594
6694
|
* @param {Vector2} posEnd
|
|
6595
6695
|
* @param {EngineObject} [object] - An object or undefined for generic test
|
|
@@ -6785,9 +6885,6 @@ class ParticleEmitter extends EngineObject
|
|
|
6785
6885
|
/** Update the emitter to spawn particles, called automatically by engine once each frame */
|
|
6786
6886
|
update()
|
|
6787
6887
|
{
|
|
6788
|
-
// only do default update to apply parent transforms
|
|
6789
|
-
this.parent && super.update();
|
|
6790
|
-
|
|
6791
6888
|
if (this.velocityInheritance)
|
|
6792
6889
|
{
|
|
6793
6890
|
// pass emitter velocity to particles
|
|
@@ -6804,7 +6901,7 @@ class ParticleEmitter extends EngineObject
|
|
|
6804
6901
|
if (!this.emitTime || this.getAliveTime() <= this.emitTime)
|
|
6805
6902
|
{
|
|
6806
6903
|
// emit particles
|
|
6807
|
-
if (this.emitRate
|
|
6904
|
+
if (this.emitRate && particleEmitRateScale)
|
|
6808
6905
|
{
|
|
6809
6906
|
const rate = 1/this.emitRate/particleEmitRateScale;
|
|
6810
6907
|
for (this.emitTimeBuffer += timeDelta; this.emitTimeBuffer > 0; this.emitTimeBuffer -= rate)
|
|
@@ -6942,8 +7039,6 @@ class Particle extends EngineObject
|
|
|
6942
7039
|
/** Update the object physics, called automatically by engine once each frame */
|
|
6943
7040
|
update()
|
|
6944
7041
|
{
|
|
6945
|
-
super.update();
|
|
6946
|
-
|
|
6947
7042
|
if (this.collideTiles || this.collideSolidObjects)
|
|
6948
7043
|
{
|
|
6949
7044
|
// only apply max circular speed if particle can collide
|
|
@@ -7187,11 +7282,11 @@ class Medal
|
|
|
7187
7282
|
const descriptionSize = height*.3;
|
|
7188
7283
|
const pos = vec2(x + medalDisplayIconSize + 2*gap.x, y + gap.y*2 + nameSize/2);
|
|
7189
7284
|
const textWidth = width - medalDisplayIconSize - 3*gap.x;
|
|
7190
|
-
drawTextScreen(this.name, pos, nameSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
7285
|
+
drawTextScreen(this.name, pos, nameSize, BLACK, 0, undefined, 'left', undefined, undefined, textWidth);
|
|
7191
7286
|
|
|
7192
7287
|
// draw the description
|
|
7193
7288
|
pos.y = y + height - gap.y*2 - descriptionSize/2;
|
|
7194
|
-
drawTextScreen(this.description, pos, descriptionSize, BLACK, 0, undefined, 'left', undefined, textWidth);
|
|
7289
|
+
drawTextScreen(this.description, pos, descriptionSize, BLACK, 0, undefined, 'left', undefined, undefined, textWidth);
|
|
7195
7290
|
context.restore();
|
|
7196
7291
|
}
|
|
7197
7292
|
|
|
@@ -8677,12 +8772,12 @@ class UISystemPlugin
|
|
|
8677
8772
|
/** Draw a rectangle to the UI context
|
|
8678
8773
|
* @param {Vector2} pos
|
|
8679
8774
|
* @param {Vector2} size
|
|
8680
|
-
* @param {Color} [color
|
|
8681
|
-
* @param {number} [lineWidth
|
|
8682
|
-
* @param {Color} [lineColor
|
|
8683
|
-
* @param {number} [cornerRadius
|
|
8684
|
-
* @param {Color} [gradientColor
|
|
8685
|
-
drawRect(pos, size, color=
|
|
8775
|
+
* @param {Color} [color]
|
|
8776
|
+
* @param {number} [lineWidth]
|
|
8777
|
+
* @param {Color} [lineColor]
|
|
8778
|
+
* @param {number} [cornerRadius]
|
|
8779
|
+
* @param {Color} [gradientColor] */
|
|
8780
|
+
drawRect(pos, size, color=WHITE, lineWidth=0, lineColor=BLACK, cornerRadius=0, gradientColor)
|
|
8686
8781
|
{
|
|
8687
8782
|
ASSERT(isVector2(pos), 'pos must be a vec2');
|
|
8688
8783
|
ASSERT(isVector2(size), 'size must be a vec2');
|
|
@@ -8761,10 +8856,14 @@ class UISystemPlugin
|
|
|
8761
8856
|
* @param {string} [align]
|
|
8762
8857
|
* @param {string} [font=uiSystem.defaultFont]
|
|
8763
8858
|
* @param {string} [fontStyle]
|
|
8764
|
-
* @param {boolean} [applyMaxWidth=true]
|
|
8765
|
-
|
|
8859
|
+
* @param {boolean} [applyMaxWidth=true]
|
|
8860
|
+
* @param {Vector2} [textShadow]
|
|
8861
|
+
*/
|
|
8862
|
+
drawText(text, pos, size, color=uiSystem.defaultColor, lineWidth=uiSystem.defaultLineWidth, lineColor=uiSystem.defaultLineColor, align='center', font=uiSystem.defaultFont, fontStyle='', applyMaxWidth=true, textShadow=undefined)
|
|
8766
8863
|
{
|
|
8767
|
-
|
|
8864
|
+
if (textShadow)
|
|
8865
|
+
drawTextScreen(text, pos.add(textShadow), size.y, BLACK, lineWidth, lineColor, align, font, fontStyle, applyMaxWidth ? size.x : undefined, 0, uiSystem.uiContext);
|
|
8866
|
+
drawTextScreen(text, pos, size.y, color, lineWidth, lineColor, align, font, fontStyle, applyMaxWidth ? size.x : undefined, 0, uiSystem.uiContext);
|
|
8768
8867
|
}
|
|
8769
8868
|
|
|
8770
8869
|
/**
|
|
@@ -8791,6 +8890,25 @@ class UISystemPlugin
|
|
|
8791
8890
|
setCallback(onDragLeave, 'dragleave');
|
|
8792
8891
|
setCallback(onDragOver, 'dragover');
|
|
8793
8892
|
}
|
|
8893
|
+
|
|
8894
|
+
/** Convert a screen space position to native UI position
|
|
8895
|
+
* @param {Vector2} pos
|
|
8896
|
+
* @return {Vector2}
|
|
8897
|
+
*/
|
|
8898
|
+
screenToNative(pos)
|
|
8899
|
+
{
|
|
8900
|
+
if (!uiSystem.nativeHeight)
|
|
8901
|
+
return pos;
|
|
8902
|
+
|
|
8903
|
+
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8904
|
+
const sInv = 1/s;
|
|
8905
|
+
const p = pos.copy();
|
|
8906
|
+
p.x += s*mainCanvasSize.x/2;
|
|
8907
|
+
p.x *= sInv;
|
|
8908
|
+
p.y *= sInv;
|
|
8909
|
+
p.x -= sInv*mainCanvasSize.x/2;
|
|
8910
|
+
return p;
|
|
8911
|
+
}
|
|
8794
8912
|
}
|
|
8795
8913
|
|
|
8796
8914
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -8867,6 +8985,9 @@ class UIObject
|
|
|
8867
8985
|
/** @property {boolean} - True if this can be a hover object */
|
|
8868
8986
|
this.canBeHover = true;
|
|
8869
8987
|
uiSystem.uiObjects.push(this);
|
|
8988
|
+
|
|
8989
|
+
/** @property {Vector2} - How much to offset the text shadow or undefined */
|
|
8990
|
+
this.textShadow = undefined;
|
|
8870
8991
|
}
|
|
8871
8992
|
|
|
8872
8993
|
/** Add a child UIObject to this object
|
|
@@ -8894,24 +9015,20 @@ class UIObject
|
|
|
8894
9015
|
*/
|
|
8895
9016
|
isMouseOverlapping()
|
|
8896
9017
|
{
|
|
9018
|
+
if (!mouseInWindow) return false;
|
|
9019
|
+
|
|
8897
9020
|
const size = !isTouchDevice ? this.size :
|
|
8898
9021
|
this.size.add(vec2(this.extraTouchSize || 0));
|
|
8899
|
-
|
|
8900
|
-
return isOverlapping(this.pos, size, mousePosScreen);
|
|
8901
|
-
|
|
8902
|
-
const s = mainCanvasSize.y / uiSystem.nativeHeight;
|
|
8903
|
-
const sInv = 1/s;
|
|
8904
|
-
let pos = mousePosScreen.copy();
|
|
8905
|
-
pos.x += s*mainCanvasSize.x/2;
|
|
8906
|
-
pos.x *= sInv;
|
|
8907
|
-
pos.y *= sInv;
|
|
8908
|
-
pos.x -= sInv*mainCanvasSize.x/2;
|
|
9022
|
+
const pos = uiSystem.screenToNative(mousePosScreen);
|
|
8909
9023
|
return isOverlapping(this.pos, size, pos);
|
|
8910
9024
|
}
|
|
8911
9025
|
|
|
8912
9026
|
/** Update the object, called automatically by plugin once each frame */
|
|
8913
9027
|
update()
|
|
8914
9028
|
{
|
|
9029
|
+
// call the custom update callback
|
|
9030
|
+
this.onUpdate();
|
|
9031
|
+
|
|
8915
9032
|
const wasHover = uiSystem.lastHoverObject === this;
|
|
8916
9033
|
const isActive = this.isActiveObject();
|
|
8917
9034
|
const mouseDown = mouseIsDown(0);
|
|
@@ -8968,7 +9085,7 @@ class UIObject
|
|
|
8968
9085
|
|
|
8969
9086
|
const lineColor = this.interactive && this.isActiveObject() && !this.disabled ? this.color : this.lineColor;
|
|
8970
9087
|
const color = this.disabled ? this.disabledColor : this.interactive ? this.isActiveObject() ? this.activeColor || this.color : this.isHoverObject() ? this.hoverColor : this.color : this.color;
|
|
8971
|
-
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius);
|
|
9088
|
+
uiSystem.drawRect(this.pos, this.size, color, this.lineWidth, lineColor, this.cornerRadius, this.gradientColor);
|
|
8972
9089
|
}
|
|
8973
9090
|
|
|
8974
9091
|
/** Special update when object is not visible */
|
|
@@ -8995,6 +9112,9 @@ class UIObject
|
|
|
8995
9112
|
/** @return {boolean} - Is the mouse held onto this element */
|
|
8996
9113
|
isActiveObject() { return uiSystem.activeObject === this; }
|
|
8997
9114
|
|
|
9115
|
+
/** Called each frame when object updates */
|
|
9116
|
+
onUpdate() {}
|
|
9117
|
+
|
|
8998
9118
|
/** Called when the mouse enters the object */
|
|
8999
9119
|
onEnter() {}
|
|
9000
9120
|
|
|
@@ -9049,8 +9169,9 @@ class UIText extends UIObject
|
|
|
9049
9169
|
}
|
|
9050
9170
|
render()
|
|
9051
9171
|
{
|
|
9172
|
+
// only render the text
|
|
9052
9173
|
const textSize = this.getTextSize();
|
|
9053
|
-
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font, this.fontStyle);
|
|
9174
|
+
uiSystem.drawText(this.text, this.pos, textSize, this.textColor, this.lineWidth, this.lineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
9054
9175
|
}
|
|
9055
9176
|
}
|
|
9056
9177
|
|
|
@@ -9126,7 +9247,7 @@ class UIButton extends UIObject
|
|
|
9126
9247
|
// draw the text scaled to fit
|
|
9127
9248
|
const textSize = this.getTextSize();
|
|
9128
9249
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
9129
|
-
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
9250
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
9130
9251
|
}
|
|
9131
9252
|
}
|
|
9132
9253
|
|
|
@@ -9180,7 +9301,7 @@ class UICheckbox extends UIObject
|
|
|
9180
9301
|
const textSize = this.getTextSize();
|
|
9181
9302
|
const pos = this.pos.add(vec2(this.size.x,0));
|
|
9182
9303
|
uiSystem.drawText(this.text, pos, textSize,
|
|
9183
|
-
this.textColor, 0, undefined, 'left', this.font, this.fontStyle, false);
|
|
9304
|
+
this.textColor, 0, undefined, 'left', this.font, this.fontStyle, false, this.textShadow);
|
|
9184
9305
|
}
|
|
9185
9306
|
}
|
|
9186
9307
|
|
|
@@ -9235,9 +9356,11 @@ class UIScrollbar extends UIObject
|
|
|
9235
9356
|
const p1 = centerPos - handleWidth/2;
|
|
9236
9357
|
const p2 = centerPos + handleWidth/2;
|
|
9237
9358
|
const oldValue = this.value;
|
|
9359
|
+
|
|
9360
|
+
const p = uiSystem.screenToNative(mousePosScreen);
|
|
9238
9361
|
this.value = isHorizontal ?
|
|
9239
|
-
percent(
|
|
9240
|
-
percent(
|
|
9362
|
+
percent(p.x, p1, p2) :
|
|
9363
|
+
percent(p.y, p2, p1);
|
|
9241
9364
|
this.value === oldValue || this.onChange();
|
|
9242
9365
|
}
|
|
9243
9366
|
}
|
|
@@ -9259,20 +9382,20 @@ class UIScrollbar extends UIObject
|
|
|
9259
9382
|
vec2(lerp(p1, p2, this.value), this.pos.y) :
|
|
9260
9383
|
vec2(this.pos.x, lerp(p2, p1, this.value))
|
|
9261
9384
|
const handleColor = this.disabled ? this.disabledColor : this.handleColor;
|
|
9262
|
-
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius);
|
|
9385
|
+
uiSystem.drawRect(handlePos, vec2(handleSize), handleColor, this.lineWidth, this.lineColor, this.cornerRadius, this.gradientColor);
|
|
9263
9386
|
|
|
9264
9387
|
// draw the text scaled to fit on the scrollbar
|
|
9265
9388
|
const textSize = this.getTextSize();
|
|
9266
9389
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
9267
|
-
this.textColor, 0, undefined, this.align, this.font, this.fontStyle);
|
|
9390
|
+
this.textColor, 0, undefined, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
9268
9391
|
}
|
|
9269
9392
|
}
|
|
9270
9393
|
/**
|
|
9271
9394
|
* LittleJS Box2D Physics Plugin
|
|
9272
9395
|
* - Box2dObject extends EngineObject with Box2D physics
|
|
9273
|
-
* - Call box2dInit()
|
|
9396
|
+
* - Call box2dInit() to enable
|
|
9274
9397
|
* - You will also need to include box2d.wasm.js
|
|
9275
|
-
* - Uses a super fast web assembly port of Box2D
|
|
9398
|
+
* - Uses a super fast web assembly port of Box2D v2.3.1
|
|
9276
9399
|
* - More info: https://github.com/kripken/box2d.js
|
|
9277
9400
|
* - Functions to create polygon, circle, and edge shapes
|
|
9278
9401
|
* - Contact begin and end callbacks
|
|
@@ -9302,9 +9425,9 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
|
|
|
9302
9425
|
///////////////////////////////////////////////////////////////////////////////
|
|
9303
9426
|
/**
|
|
9304
9427
|
* Box2D Object - extend with your own custom physics objects
|
|
9305
|
-
* - A LittleJS object with Box2D physics
|
|
9306
|
-
* - Each object has a Box2D body which can have multiple fixtures and joints
|
|
9428
|
+
* - A LittleJS object with Box2D physics, dynamic by default
|
|
9307
9429
|
* - Provides interface for Box2D body and fixture functions
|
|
9430
|
+
* - Each object can have multiple fixtures and joints
|
|
9308
9431
|
* @extends EngineObject
|
|
9309
9432
|
* @memberof Box2D
|
|
9310
9433
|
*/
|
|
@@ -9318,7 +9441,7 @@ class Box2dObject extends EngineObject
|
|
|
9318
9441
|
* @param {Color} [color]
|
|
9319
9442
|
* @param {number} [bodyType]
|
|
9320
9443
|
* @param {number} [renderOrder] */
|
|
9321
|
-
constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
9444
|
+
constructor(pos=vec2(), size=vec2(), tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
|
|
9322
9445
|
{
|
|
9323
9446
|
super(pos, size, tileInfo, angle, color, renderOrder);
|
|
9324
9447
|
|
|
@@ -9330,24 +9453,27 @@ class Box2dObject extends EngineObject
|
|
|
9330
9453
|
this.body = box2d.world.CreateBody(bodyDef);
|
|
9331
9454
|
this.body.object = this;
|
|
9332
9455
|
this.lineColor = BLACK;
|
|
9456
|
+
box2d.objects.push(this);
|
|
9457
|
+
|
|
9458
|
+
// edge lists and loops for drawing
|
|
9459
|
+
this.edgeLists = [];
|
|
9460
|
+
this.edgeLoops = [];
|
|
9333
9461
|
}
|
|
9334
9462
|
|
|
9335
9463
|
/** Destroy this object and its physics body */
|
|
9336
9464
|
destroy()
|
|
9337
9465
|
{
|
|
9466
|
+
if (this.destroyed)
|
|
9467
|
+
return;
|
|
9468
|
+
|
|
9338
9469
|
// destroy physics body, fixtures, and joints
|
|
9339
|
-
this.body
|
|
9340
|
-
this.body
|
|
9470
|
+
ASSERT(this.body, 'Box2dObject has no body to destroy');
|
|
9471
|
+
box2d.world.DestroyBody(this.body);
|
|
9341
9472
|
super.destroy();
|
|
9342
9473
|
}
|
|
9343
9474
|
|
|
9344
|
-
/**
|
|
9345
|
-
|
|
9346
|
-
{
|
|
9347
|
-
// use box2d physics update instead of normal engine update
|
|
9348
|
-
this.pos = box2d.vec2From(this.body.GetPosition());
|
|
9349
|
-
this.angle = -this.body.GetAngle();
|
|
9350
|
-
}
|
|
9475
|
+
/** Box2d objects updated with Box2d world step */
|
|
9476
|
+
updatePhysics() {}
|
|
9351
9477
|
|
|
9352
9478
|
/** Render the object, uses box2d drawing if no tile info exists */
|
|
9353
9479
|
render()
|
|
@@ -9373,10 +9499,23 @@ class Box2dObject extends EngineObject
|
|
|
9373
9499
|
* @param {Color} [lineColor]
|
|
9374
9500
|
* @param {number} [lineWidth]
|
|
9375
9501
|
* @param {CanvasRenderingContext2D} [context] */
|
|
9376
|
-
drawFixtures(color=WHITE, lineColor, lineWidth=.1, context)
|
|
9502
|
+
drawFixtures(color=WHITE, lineColor=BLACK, lineWidth=.1, context)
|
|
9377
9503
|
{
|
|
9378
|
-
|
|
9379
|
-
|
|
9504
|
+
// draw non-edge fixtures
|
|
9505
|
+
this.getFixtureList().forEach((fixture)=>
|
|
9506
|
+
{
|
|
9507
|
+
const shape = box2d.castObjectType(fixture.GetShape());
|
|
9508
|
+
if (shape.GetType() !== box2d.instance.b2Shape.e_edge)
|
|
9509
|
+
{
|
|
9510
|
+
box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, context);
|
|
9511
|
+
}
|
|
9512
|
+
});
|
|
9513
|
+
|
|
9514
|
+
// draw edges using a single draw line for better connections
|
|
9515
|
+
this.edgeLists.forEach(points=>
|
|
9516
|
+
drawLineList(points, lineWidth, lineColor, false, this.pos, this.angle));
|
|
9517
|
+
this.edgeLoops.forEach(points=>
|
|
9518
|
+
drawLineList(points, lineWidth, lineColor, true, this.pos, this.angle));
|
|
9380
9519
|
}
|
|
9381
9520
|
|
|
9382
9521
|
///////////////////////////////////////////////////////////////////////////////
|
|
@@ -9401,6 +9540,10 @@ class Box2dObject extends EngineObject
|
|
|
9401
9540
|
* @param {boolean} [isSensor] */
|
|
9402
9541
|
addShape(shape, density=1, friction=.2, restitution=0, isSensor=false)
|
|
9403
9542
|
{
|
|
9543
|
+
ASSERT(isNumber(density), 'density must be a number');
|
|
9544
|
+
ASSERT(isNumber(friction), 'friction must be a number');
|
|
9545
|
+
ASSERT(isNumber(restitution), 'restitution must be a number');
|
|
9546
|
+
|
|
9404
9547
|
const fd = new box2d.instance.b2FixtureDef();
|
|
9405
9548
|
fd.set_shape(shape);
|
|
9406
9549
|
fd.set_density(density);
|
|
@@ -9420,6 +9563,11 @@ class Box2dObject extends EngineObject
|
|
|
9420
9563
|
* @param {boolean} [isSensor] */
|
|
9421
9564
|
addBox(size=vec2(1), offset=vec2(), angle=0, density, friction, restitution, isSensor)
|
|
9422
9565
|
{
|
|
9566
|
+
ASSERT(isVector2(size), 'size must be a Vector2');
|
|
9567
|
+
ASSERT(size.x > 0 && size.y > 0, 'size must be positive');
|
|
9568
|
+
ASSERT(isVector2(offset), 'offset must be a Vector2');
|
|
9569
|
+
ASSERT(isNumber(angle), 'angle must be a number');
|
|
9570
|
+
|
|
9423
9571
|
const shape = new box2d.instance.b2PolygonShape();
|
|
9424
9572
|
shape.SetAsBox(size.x/2, size.y/2, box2d.vec2dTo(offset), angle);
|
|
9425
9573
|
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
@@ -9433,6 +9581,8 @@ class Box2dObject extends EngineObject
|
|
|
9433
9581
|
* @param {boolean} [isSensor] */
|
|
9434
9582
|
addPoly(points, density, friction, restitution, isSensor)
|
|
9435
9583
|
{
|
|
9584
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
9585
|
+
|
|
9436
9586
|
function box2dCreatePolygonShape(points)
|
|
9437
9587
|
{
|
|
9438
9588
|
function box2dCreatePointList(points)
|
|
@@ -9468,6 +9618,9 @@ class Box2dObject extends EngineObject
|
|
|
9468
9618
|
* @param {boolean} [isSensor] */
|
|
9469
9619
|
addRegularPoly(diameter=1, sides=8, density, friction, restitution, isSensor)
|
|
9470
9620
|
{
|
|
9621
|
+
ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
|
|
9622
|
+
ASSERT(isNumber(sides) && sides>2, 'sides must be a positive number greater than 2');
|
|
9623
|
+
|
|
9471
9624
|
const points = [];
|
|
9472
9625
|
const radius = diameter/2;
|
|
9473
9626
|
for (let i=sides; i--;)
|
|
@@ -9483,6 +9636,8 @@ class Box2dObject extends EngineObject
|
|
|
9483
9636
|
* @param {boolean} [isSensor] */
|
|
9484
9637
|
addRandomPoly(diameter=1, density, friction, restitution, isSensor)
|
|
9485
9638
|
{
|
|
9639
|
+
ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
|
|
9640
|
+
|
|
9486
9641
|
const sides = randInt(3, 9);
|
|
9487
9642
|
const points = [];
|
|
9488
9643
|
const radius = diameter/2;
|
|
@@ -9500,6 +9655,9 @@ class Box2dObject extends EngineObject
|
|
|
9500
9655
|
* @param {boolean} [isSensor] */
|
|
9501
9656
|
addCircle(diameter=1, offset=vec2(), density, friction, restitution, isSensor)
|
|
9502
9657
|
{
|
|
9658
|
+
ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
|
|
9659
|
+
ASSERT(isVector2(offset), 'offset must be a Vector2');
|
|
9660
|
+
|
|
9503
9661
|
const shape = new box2d.instance.b2CircleShape();
|
|
9504
9662
|
shape.set_m_p(box2d.vec2dTo(offset));
|
|
9505
9663
|
shape.set_m_radius(diameter/2);
|
|
@@ -9515,53 +9673,63 @@ class Box2dObject extends EngineObject
|
|
|
9515
9673
|
* @param {boolean} [isSensor] */
|
|
9516
9674
|
addEdge(point1, point2, density, friction, restitution, isSensor)
|
|
9517
9675
|
{
|
|
9676
|
+
ASSERT(isVector2(point1), 'point1 must be a Vector2');
|
|
9677
|
+
ASSERT(isVector2(point2), 'point2 must be a Vector2');
|
|
9678
|
+
|
|
9518
9679
|
const shape = new box2d.instance.b2EdgeShape();
|
|
9519
9680
|
shape.Set(box2d.vec2dTo(point1), box2d.vec2dTo(point2));
|
|
9520
9681
|
return this.addShape(shape, density, friction, restitution, isSensor);
|
|
9521
9682
|
}
|
|
9522
9683
|
|
|
9523
|
-
/** Add an edge
|
|
9684
|
+
/** Add an edge list to the body
|
|
9524
9685
|
* @param {Array<Vector2>} points
|
|
9525
9686
|
* @param {number} [density]
|
|
9526
9687
|
* @param {number} [friction]
|
|
9527
9688
|
* @param {number} [restitution]
|
|
9528
9689
|
* @param {boolean} [isSensor] */
|
|
9529
|
-
|
|
9690
|
+
addEdgeList(points, density, friction, restitution, isSensor)
|
|
9530
9691
|
{
|
|
9531
|
-
|
|
9532
|
-
const
|
|
9533
|
-
for (let i=0; i<points.length; ++i)
|
|
9692
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
9693
|
+
const fixtures = [], edgePoints = [];
|
|
9694
|
+
for (let i=0; i<points.length-1; ++i)
|
|
9534
9695
|
{
|
|
9535
9696
|
const shape = new box2d.instance.b2EdgeShape();
|
|
9536
|
-
shape.set_m_vertex0(box2d.vec2dTo(
|
|
9537
|
-
shape.set_m_vertex1(box2d.vec2dTo(
|
|
9538
|
-
shape.set_m_vertex2(box2d.vec2dTo(
|
|
9539
|
-
shape.set_m_vertex3(box2d.vec2dTo(
|
|
9697
|
+
points[i-1] && shape.set_m_vertex0(box2d.vec2dTo(points[i-1]));
|
|
9698
|
+
points[i+0] && shape.set_m_vertex1(box2d.vec2dTo(points[i+0]));
|
|
9699
|
+
points[i+1] && shape.set_m_vertex2(box2d.vec2dTo(points[i+1]));
|
|
9700
|
+
points[i+2] && shape.set_m_vertex3(box2d.vec2dTo(points[i+2]));
|
|
9540
9701
|
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
9541
9702
|
fixtures.push(f);
|
|
9703
|
+
edgePoints.push(points[i].copy());
|
|
9542
9704
|
}
|
|
9705
|
+
edgePoints.push(points[points.length-1].copy());
|
|
9706
|
+
this.edgeLists.push(edgePoints);
|
|
9543
9707
|
return fixtures;
|
|
9544
9708
|
}
|
|
9545
9709
|
|
|
9546
|
-
/** Add an edge
|
|
9710
|
+
/** Add an edge loop to the body, an edge loop connects the end points
|
|
9547
9711
|
* @param {Array<Vector2>} points
|
|
9548
9712
|
* @param {number} [density]
|
|
9549
9713
|
* @param {number} [friction]
|
|
9550
9714
|
* @param {number} [restitution]
|
|
9551
9715
|
* @param {boolean} [isSensor] */
|
|
9552
|
-
|
|
9716
|
+
addEdgeLoop(points, density, friction, restitution, isSensor)
|
|
9553
9717
|
{
|
|
9554
|
-
|
|
9555
|
-
|
|
9718
|
+
ASSERT(isArray(points), 'points must be an array');
|
|
9719
|
+
const fixtures = [], edgePoints = [];
|
|
9720
|
+
const getPoint = i=> points[mod(i,points.length)];
|
|
9721
|
+
for (let i=0; i<points.length; ++i)
|
|
9556
9722
|
{
|
|
9557
9723
|
const shape = new box2d.instance.b2EdgeShape();
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9724
|
+
shape.set_m_vertex0(box2d.vec2dTo(getPoint(i-1)));
|
|
9725
|
+
shape.set_m_vertex1(box2d.vec2dTo(getPoint(i+0)));
|
|
9726
|
+
shape.set_m_vertex2(box2d.vec2dTo(getPoint(i+1)));
|
|
9727
|
+
shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
|
|
9562
9728
|
const f = this.addShape(shape, density, friction, restitution, isSensor);
|
|
9563
9729
|
fixtures.push(f);
|
|
9730
|
+
i < points.length && edgePoints.push(points[i].copy());
|
|
9564
9731
|
}
|
|
9732
|
+
this.edgeLoops.push(edgePoints);
|
|
9565
9733
|
return fixtures;
|
|
9566
9734
|
}
|
|
9567
9735
|
|
|
@@ -9596,6 +9764,10 @@ class Box2dObject extends EngineObject
|
|
|
9596
9764
|
* @return {number} */
|
|
9597
9765
|
getBodyType() { return this.body.GetType(); }
|
|
9598
9766
|
|
|
9767
|
+
/** Get the speed of this object
|
|
9768
|
+
* @return {number} */
|
|
9769
|
+
getSpeed() { return this.getLinearVelocity().length(); }
|
|
9770
|
+
|
|
9599
9771
|
///////////////////////////////////////////////////////////////////////////////
|
|
9600
9772
|
// physics set functions
|
|
9601
9773
|
|
|
@@ -9611,33 +9783,40 @@ class Box2dObject extends EngineObject
|
|
|
9611
9783
|
|
|
9612
9784
|
/** Sets the position
|
|
9613
9785
|
* @param {Vector2} pos */
|
|
9614
|
-
setPosition(pos)
|
|
9786
|
+
setPosition(pos)
|
|
9787
|
+
{ this.setTransform(pos, this.body.GetAngle()); }
|
|
9615
9788
|
|
|
9616
9789
|
/** Sets the angle
|
|
9617
9790
|
* @param {number} angle */
|
|
9618
|
-
setAngle(angle)
|
|
9791
|
+
setAngle(angle)
|
|
9792
|
+
{ this.setTransform(box2d.vec2From(this.body.GetPosition()), -angle); }
|
|
9619
9793
|
|
|
9620
9794
|
/** Sets the linear velocity
|
|
9621
9795
|
* @param {Vector2} velocity */
|
|
9622
|
-
setLinearVelocity(velocity)
|
|
9796
|
+
setLinearVelocity(velocity)
|
|
9797
|
+
{ this.body.SetLinearVelocity(box2d.vec2dTo(velocity)); }
|
|
9623
9798
|
|
|
9624
9799
|
/** Sets the angular velocity
|
|
9625
9800
|
* @param {number} angularVelocity */
|
|
9626
|
-
setAngularVelocity(angularVelocity)
|
|
9801
|
+
setAngularVelocity(angularVelocity)
|
|
9802
|
+
{ this.body.SetAngularVelocity(angularVelocity); }
|
|
9627
9803
|
|
|
9628
9804
|
/** Sets the linear damping
|
|
9629
9805
|
* @param {number} damping */
|
|
9630
|
-
setLinearDamping(damping)
|
|
9806
|
+
setLinearDamping(damping)
|
|
9807
|
+
{ this.body.SetLinearDamping(damping); }
|
|
9631
9808
|
|
|
9632
9809
|
/** Sets the angular damping
|
|
9633
9810
|
* @param {number} damping */
|
|
9634
|
-
setAngularDamping(damping)
|
|
9811
|
+
setAngularDamping(damping)
|
|
9812
|
+
{ this.body.SetAngularDamping(damping); }
|
|
9635
9813
|
|
|
9636
9814
|
/** Sets the gravity scale
|
|
9637
9815
|
* @param {number} [scale] */
|
|
9638
|
-
setGravityScale(scale=1)
|
|
9816
|
+
setGravityScale(scale=1)
|
|
9817
|
+
{ this.body.SetGravityScale(this.gravityScale = scale); }
|
|
9639
9818
|
|
|
9640
|
-
/** Should
|
|
9819
|
+
/** Should be like a bullet for continuous collision detection?
|
|
9641
9820
|
* @param {boolean} [isBullet] */
|
|
9642
9821
|
setBullet(isBullet=true) { this.body.SetBullet(isBullet); }
|
|
9643
9822
|
|
|
@@ -9651,11 +9830,13 @@ class Box2dObject extends EngineObject
|
|
|
9651
9830
|
|
|
9652
9831
|
/** Set whether the body is allowed to sleep
|
|
9653
9832
|
* @param {boolean} [isAllowed] */
|
|
9654
|
-
setSleepingAllowed(isAllowed=true)
|
|
9833
|
+
setSleepingAllowed(isAllowed=true)
|
|
9834
|
+
{ this.body.SetSleepingAllowed(isAllowed); }
|
|
9655
9835
|
|
|
9656
9836
|
/** Set whether the body can rotate
|
|
9657
9837
|
* @param {boolean} [isFixed] */
|
|
9658
|
-
setFixedRotation(isFixed=true)
|
|
9838
|
+
setFixedRotation(isFixed=true)
|
|
9839
|
+
{ this.body.SetFixedRotation(isFixed); }
|
|
9659
9840
|
|
|
9660
9841
|
/** Set the center of mass of the body
|
|
9661
9842
|
* @param {Vector2} center */
|
|
@@ -9667,10 +9848,11 @@ class Box2dObject extends EngineObject
|
|
|
9667
9848
|
|
|
9668
9849
|
/** Set the moment of inertia of the body
|
|
9669
9850
|
* @param {number} momentOfInertia */
|
|
9670
|
-
setMomentOfInertia(momentOfInertia)
|
|
9851
|
+
setMomentOfInertia(momentOfInertia)
|
|
9852
|
+
{ this.setMassData(undefined, undefined, momentOfInertia) }
|
|
9671
9853
|
|
|
9672
9854
|
/** Reset the mass, center of mass, and moment */
|
|
9673
|
-
resetMassData()
|
|
9855
|
+
resetMassData() { this.body.ResetMassData(); }
|
|
9674
9856
|
|
|
9675
9857
|
/** Set the mass data of the body
|
|
9676
9858
|
* @param {Vector2} [localCenter]
|
|
@@ -10312,6 +10494,50 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
10312
10494
|
getMotorForce(time) { return this.box2dJoint.GetMotorForce(1/time); }
|
|
10313
10495
|
}
|
|
10314
10496
|
|
|
10497
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10498
|
+
/**
|
|
10499
|
+
* Box2D Static Object - Box2d with a static physics body
|
|
10500
|
+
* @extends Box2dObject
|
|
10501
|
+
* @memberof Box2D
|
|
10502
|
+
*/
|
|
10503
|
+
class Box2dStaticObject extends Box2dObject
|
|
10504
|
+
{
|
|
10505
|
+
/** Create a LittleJS object with Box2d physics
|
|
10506
|
+
* @param {Vector2} [pos]
|
|
10507
|
+
* @param {Vector2} [size]
|
|
10508
|
+
* @param {TileInfo} [tileInfo]
|
|
10509
|
+
* @param {number} [angle]
|
|
10510
|
+
* @param {Color} [color]
|
|
10511
|
+
* @param {number} [renderOrder] */
|
|
10512
|
+
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10513
|
+
{
|
|
10514
|
+
const bodyType = box2d.bodyTypeStatic;
|
|
10515
|
+
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10516
|
+
}
|
|
10517
|
+
}
|
|
10518
|
+
|
|
10519
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
10520
|
+
/**
|
|
10521
|
+
* Box2D Kiematic Object - Box2d with a kinematic physics body
|
|
10522
|
+
* @extends Box2dObject
|
|
10523
|
+
* @memberof Box2D
|
|
10524
|
+
*/
|
|
10525
|
+
class Box2dKiematicObject extends Box2dObject
|
|
10526
|
+
{
|
|
10527
|
+
/** Create a LittleJS object with Box2d physics
|
|
10528
|
+
* @param {Vector2} [pos]
|
|
10529
|
+
* @param {Vector2} [size]
|
|
10530
|
+
* @param {TileInfo} [tileInfo]
|
|
10531
|
+
* @param {number} [angle]
|
|
10532
|
+
* @param {Color} [color]
|
|
10533
|
+
* @param {number} [renderOrder] */
|
|
10534
|
+
constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
|
|
10535
|
+
{
|
|
10536
|
+
const bodyType = box2d.bodyTypeKinematic;
|
|
10537
|
+
super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
|
|
10538
|
+
}
|
|
10539
|
+
}
|
|
10540
|
+
|
|
10315
10541
|
///////////////////////////////////////////////////////////////////////////////
|
|
10316
10542
|
/**
|
|
10317
10543
|
* Box2D Wheel Joint
|
|
@@ -10674,6 +10900,7 @@ class Box2dPlugin
|
|
|
10674
10900
|
box2d = this;
|
|
10675
10901
|
this.instance = instance;
|
|
10676
10902
|
this.world = new box2d.instance.b2World();
|
|
10903
|
+
this.objects = [];
|
|
10677
10904
|
|
|
10678
10905
|
/** @property {number} - Velocity iterations per update*/
|
|
10679
10906
|
this.velocityIterations = 8;
|
|
@@ -10916,9 +11143,10 @@ class Box2dPlugin
|
|
|
10916
11143
|
|
|
10917
11144
|
/** converts a box2d vec2 pointer to a Vector2
|
|
10918
11145
|
* @param {Object} v */
|
|
10919
|
-
vec2FromPointer(
|
|
11146
|
+
vec2FromPointer(vp)
|
|
10920
11147
|
{
|
|
10921
|
-
|
|
11148
|
+
const v = box2d.instance.wrapPointer(vp, box2d.instance.b2Vec2);
|
|
11149
|
+
return box2d.vec2From(v);
|
|
10922
11150
|
}
|
|
10923
11151
|
|
|
10924
11152
|
/** converts a Vector2 to a box2 vec2
|
|
@@ -10992,8 +11220,23 @@ async function box2dInit()
|
|
|
10992
11220
|
// add the box2d plugin to the engine
|
|
10993
11221
|
function box2dUpdate()
|
|
10994
11222
|
{
|
|
10995
|
-
if (
|
|
10996
|
-
|
|
11223
|
+
if (paused)
|
|
11224
|
+
return;
|
|
11225
|
+
|
|
11226
|
+
box2d.step();
|
|
11227
|
+
|
|
11228
|
+
// remove destroyed objects
|
|
11229
|
+
box2d.objects = box2d.objects.filter(o=>!o.destroyed);
|
|
11230
|
+
|
|
11231
|
+
// copy box2d physics results to engine objects
|
|
11232
|
+
for (const o of box2d.objects)
|
|
11233
|
+
{
|
|
11234
|
+
if (o.body)
|
|
11235
|
+
{
|
|
11236
|
+
o.pos = box2d.vec2From(o.body.GetPosition());
|
|
11237
|
+
o.angle = -o.body.GetAngle();
|
|
11238
|
+
}
|
|
11239
|
+
}
|
|
10997
11240
|
}
|
|
10998
11241
|
function box2dRender()
|
|
10999
11242
|
{
|
|
@@ -11376,6 +11619,7 @@ export
|
|
|
11376
11619
|
// Random
|
|
11377
11620
|
rand,
|
|
11378
11621
|
randInt,
|
|
11622
|
+
randBool,
|
|
11379
11623
|
randSign,
|
|
11380
11624
|
randInCircle,
|
|
11381
11625
|
randVec2,
|
|
@@ -11393,6 +11637,7 @@ export
|
|
|
11393
11637
|
isVector2,
|
|
11394
11638
|
isNumber,
|
|
11395
11639
|
isString,
|
|
11640
|
+
isArray,
|
|
11396
11641
|
|
|
11397
11642
|
// Default Colors
|
|
11398
11643
|
WHITE,
|
|
@@ -11493,6 +11738,7 @@ export
|
|
|
11493
11738
|
mouseDelta,
|
|
11494
11739
|
mouseDeltaScreen,
|
|
11495
11740
|
mouseWheel,
|
|
11741
|
+
mouseInWindow,
|
|
11496
11742
|
isUsingGamepad,
|
|
11497
11743
|
inputPreventDefault,
|
|
11498
11744
|
setInputPreventDefault,
|
|
@@ -11581,6 +11827,8 @@ export
|
|
|
11581
11827
|
box2dInit,
|
|
11582
11828
|
Box2dPlugin,
|
|
11583
11829
|
Box2dObject,
|
|
11830
|
+
Box2dStaticObject,
|
|
11831
|
+
Box2dKiematicObject,
|
|
11584
11832
|
Box2dRaycastResult,
|
|
11585
11833
|
Box2dJoint,
|
|
11586
11834
|
Box2dTargetJoint,
|