littlejsengine 1.17.15 → 1.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +1 -0
- package/README.md +13 -4
- package/dist/littlejs.d.ts +28 -9
- package/dist/littlejs.esm.js +111 -44
- package/dist/littlejs.esm.min.js +1 -1
- package/dist/littlejs.js +107 -43
- package/dist/littlejs.min.js +1 -1
- package/dist/littlejs.release.js +104 -40
- package/examples/box2d/game.js +2 -2
- package/examples/box2d/gameObjects.js +74 -0
- package/examples/box2d/scenes.js +7 -0
- package/examples/shorts/base.html +1 -1
- package/examples/shorts/music.js +1 -1
- package/examples/shorts/musicPlayer.js +3 -3
- package/examples/shorts/sequencer.js +1 -1
- package/examples/shorts/timers.js +1 -1
- package/examples/shorts/uiSystem.js +5 -5
- package/examples/starter/index.html +2 -2
- package/examples/stress/index.html +1 -1
- package/examples/uiSystem/game.js +6 -6
- package/package.json +1 -1
- package/plugins/box2d.js +18 -7
- package/plugins/pluginExport.js +1 -1
- package/plugins/uiSystem.js +15 -15
- package/src/engine.js +23 -6
- package/src/engineBuild.mjs +1 -1
- package/src/engineDebug.js +3 -3
- package/src/engineDraw.js +1 -0
- package/src/engineExport.js +3 -0
- package/src/engineInput.js +19 -8
- package/src/engineMath.js +1 -1
- package/src/engineObject.js +3 -3
- package/src/engineSettings.js +24 -0
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.
|
|
38
|
+
const engineVersion = '1.18.1';
|
|
39
39
|
|
|
40
40
|
/** Frames per second to update
|
|
41
41
|
* @type {number}
|
|
@@ -318,12 +318,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
318
318
|
}
|
|
319
319
|
else
|
|
320
320
|
{
|
|
321
|
+
// apply device pixel ratio for crisp rendering
|
|
322
|
+
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
323
|
+
const viewWidth = innerWidth * dpr | 0;
|
|
324
|
+
const viewHeight = innerHeight * dpr | 0;
|
|
325
|
+
|
|
321
326
|
// get main canvas size based on window size
|
|
322
|
-
mainCanvasSize.x = min(
|
|
323
|
-
mainCanvasSize.y = min(
|
|
327
|
+
mainCanvasSize.x = min(viewWidth, canvasMaxSize.x);
|
|
328
|
+
mainCanvasSize.y = min(viewHeight, canvasMaxSize.y);
|
|
324
329
|
|
|
325
330
|
// responsive aspect ratio with native resolution
|
|
326
|
-
const innerAspect =
|
|
331
|
+
const innerAspect = viewWidth / viewHeight;
|
|
327
332
|
ASSERT(canvasMinAspect <= canvasMaxAspect);
|
|
328
333
|
if (canvasMaxAspect && innerAspect > canvasMaxAspect)
|
|
329
334
|
{
|
|
@@ -337,6 +342,17 @@ async function engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, game
|
|
|
337
342
|
const h = mainCanvasSize.x / canvasMinAspect | 0;
|
|
338
343
|
mainCanvasSize.y = min(h, canvasMaxSize.y);
|
|
339
344
|
}
|
|
345
|
+
|
|
346
|
+
// set CSS display size so backing store renders at viewport size
|
|
347
|
+
const cssW = (mainCanvasSize.x / dpr | 0) + 'px';
|
|
348
|
+
const cssH = (mainCanvasSize.y / dpr | 0) + 'px';
|
|
349
|
+
mainCanvas.style.width = cssW;
|
|
350
|
+
mainCanvas.style.height = cssH;
|
|
351
|
+
if (glCanvas)
|
|
352
|
+
{
|
|
353
|
+
glCanvas.style.width = cssW;
|
|
354
|
+
glCanvas.style.height = cssH;
|
|
355
|
+
}
|
|
340
356
|
}
|
|
341
357
|
|
|
342
358
|
// clear main canvas and set size
|
|
@@ -556,8 +572,9 @@ function drawEngineLogo(t)
|
|
|
556
572
|
|
|
557
573
|
// LittleJS Logo and Splash Screen
|
|
558
574
|
const x = mainContext;
|
|
559
|
-
const
|
|
560
|
-
const
|
|
575
|
+
const dpr = canvasPixelRatio ?? (devicePixelRatio || 1);
|
|
576
|
+
const w = mainCanvas.width = innerWidth * dpr;
|
|
577
|
+
const h = mainCanvas.height = innerHeight * dpr;
|
|
561
578
|
{
|
|
562
579
|
// background
|
|
563
580
|
const p3 = percent(t, 1, .8);
|
|
@@ -1054,7 +1071,7 @@ function lineTest(posStart, posEnd, testFunction, normal)
|
|
|
1054
1071
|
// get ray direction and length
|
|
1055
1072
|
const dx = posEnd.x - posStart.x;
|
|
1056
1073
|
const dy = posEnd.y - posStart.y;
|
|
1057
|
-
const totalLength =
|
|
1074
|
+
const totalLength = (dx*dx + dy*dy)**.5;
|
|
1058
1075
|
if (!totalLength) return;
|
|
1059
1076
|
|
|
1060
1077
|
// current integer cell we are in
|
|
@@ -2158,6 +2175,13 @@ let canvasPixelated = false;
|
|
|
2158
2175
|
* @memberof Settings */
|
|
2159
2176
|
let tilesPixelated = true;
|
|
2160
2177
|
|
|
2178
|
+
/** Scale factor applied to the canvas backing store for native-resolution rendering.
|
|
2179
|
+
* Pass 1 for no scaling, a number for an explicit ratio, or undefined to track devicePixelRatio each frame.
|
|
2180
|
+
* @type {number|undefined}
|
|
2181
|
+
* @default
|
|
2182
|
+
* @memberof Settings */
|
|
2183
|
+
let canvasPixelRatio = 1;
|
|
2184
|
+
|
|
2161
2185
|
/** Default font used for text rendering
|
|
2162
2186
|
* @type {string}
|
|
2163
2187
|
* @default
|
|
@@ -2336,6 +2360,12 @@ let touchGamepadSize = 99;
|
|
|
2336
2360
|
* @memberof Settings */
|
|
2337
2361
|
let touchGamepadAlpha = .3;
|
|
2338
2362
|
|
|
2363
|
+
/** How long to display the touch gamepad on screen in seconds, set to 0 to always display
|
|
2364
|
+
* @type {number}
|
|
2365
|
+
* @default
|
|
2366
|
+
* @memberof Settings */
|
|
2367
|
+
let touchGamepadDisplayTime = 3;
|
|
2368
|
+
|
|
2339
2369
|
/** Allow vibration hardware if it exists
|
|
2340
2370
|
* @type {boolean}
|
|
2341
2371
|
* @default
|
|
@@ -2464,6 +2494,12 @@ function setCanvasPixelated(pixelated)
|
|
|
2464
2494
|
* @memberof Settings */
|
|
2465
2495
|
function setTilesPixelated(pixelated) { tilesPixelated = pixelated; }
|
|
2466
2496
|
|
|
2497
|
+
/** Set the canvas pixel ratio.
|
|
2498
|
+
* Pass a number for an explicit ratio, or call with no argument to track devicePixelRatio each frame.
|
|
2499
|
+
* @param {number} [pixelRatio]
|
|
2500
|
+
* @memberof Settings */
|
|
2501
|
+
function setCanvasPixelRatio(pixelRatio) { canvasPixelRatio = pixelRatio; }
|
|
2502
|
+
|
|
2467
2503
|
/** Set default font used for text rendering
|
|
2468
2504
|
* @param {string} font
|
|
2469
2505
|
* @memberof Settings */
|
|
@@ -2610,6 +2646,11 @@ function setTouchGamepadSize(size) { touchGamepadSize = size; }
|
|
|
2610
2646
|
* @memberof Settings */
|
|
2611
2647
|
function setTouchGamepadAlpha(alpha) { touchGamepadAlpha = alpha; }
|
|
2612
2648
|
|
|
2649
|
+
/** Set how long to display the touch gamepad on screen in seconds, set to 0 to always display
|
|
2650
|
+
* @param {number} time
|
|
2651
|
+
* @memberof Settings */
|
|
2652
|
+
function setTouchGamepadDisplayTime(time) { touchGamepadDisplayTime = time; }
|
|
2653
|
+
|
|
2613
2654
|
/** Set to allow vibration hardware if it exists
|
|
2614
2655
|
* @param {boolean} enable
|
|
2615
2656
|
* @memberof Settings */
|
|
@@ -2881,10 +2922,10 @@ class EngineObject
|
|
|
2881
2922
|
// if already was touching, try to push away
|
|
2882
2923
|
const deltaPos = oldPos.subtract(o.pos);
|
|
2883
2924
|
const length = deltaPos.length();
|
|
2884
|
-
const pushAwayAccel = .001;
|
|
2885
|
-
const velocity = length < .
|
|
2925
|
+
const pushAwayAccel = .001;
|
|
2926
|
+
const velocity = length < .001 ? vec2(0,1) : deltaPos.scale(pushAwayAccel/length);
|
|
2886
2927
|
this.velocity = this.velocity.add(velocity);
|
|
2887
|
-
if (o.mass) // push away if not fixed
|
|
2928
|
+
if (o.mass) // push away other object if not fixed
|
|
2888
2929
|
o.velocity = o.velocity.subtract(velocity);
|
|
2889
2930
|
|
|
2890
2931
|
debugPhysics && debugOverlap(this.pos, this.size, o.pos, o.size, '#f00');
|
|
@@ -3313,6 +3354,7 @@ function tile(index=new Vector2, size=tileDefaultSize, texture=0, padding=tileDe
|
|
|
3313
3354
|
const textureInfo = typeof texture === 'number' ?
|
|
3314
3355
|
textureInfos[texture] : texture;
|
|
3315
3356
|
ASSERT(textureInfo instanceof TextureInfo, 'tile texture is not loaded');
|
|
3357
|
+
ASSERT(textureInfo.size.x > 0, 'tile texture is not loaded');
|
|
3316
3358
|
|
|
3317
3359
|
// get the position of the tile
|
|
3318
3360
|
const sizePaddedX = size.x + padding*2;
|
|
@@ -4675,7 +4717,7 @@ function inputInit()
|
|
|
4675
4717
|
document.addEventListener('mouseup', onMouseUp);
|
|
4676
4718
|
document.addEventListener('mousemove', onMouseMove);
|
|
4677
4719
|
document.addEventListener('mouseleave', onMouseLeave);
|
|
4678
|
-
document.addEventListener('wheel', onMouseWheel);
|
|
4720
|
+
document.addEventListener('wheel', onMouseWheel, { passive: false });
|
|
4679
4721
|
document.addEventListener('contextmenu', onContextMenu);
|
|
4680
4722
|
document.addEventListener('blur', onBlur);
|
|
4681
4723
|
|
|
@@ -4694,7 +4736,7 @@ function inputInit()
|
|
|
4694
4736
|
}
|
|
4695
4737
|
|
|
4696
4738
|
// try to prevent default browser handling of input
|
|
4697
|
-
if (!inputPreventDefault || !
|
|
4739
|
+
if (!inputPreventDefault || !e.cancelable || !document.hasFocus()) return;
|
|
4698
4740
|
|
|
4699
4741
|
// don't break browser shortcuts
|
|
4700
4742
|
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
@@ -4753,7 +4795,7 @@ function inputInit()
|
|
|
4753
4795
|
mousePosScreen = mouseEventToScreen(vec2(e.x,e.y));
|
|
4754
4796
|
mouseDeltaScreen = mouseDeltaScreen.add(mousePosScreen.subtract(mousePosScreenLast));
|
|
4755
4797
|
|
|
4756
|
-
if (inputPreventDefault &&
|
|
4798
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
4757
4799
|
e.preventDefault();
|
|
4758
4800
|
}
|
|
4759
4801
|
function onMouseUp(e)
|
|
@@ -4775,7 +4817,12 @@ function inputInit()
|
|
|
4775
4817
|
mouseDeltaScreen = mouseDeltaScreen.add(movement);
|
|
4776
4818
|
}
|
|
4777
4819
|
function onMouseLeave() { mouseInWindow = false; } // mouse moved off window
|
|
4778
|
-
function onMouseWheel(e)
|
|
4820
|
+
function onMouseWheel(e)
|
|
4821
|
+
{
|
|
4822
|
+
mouseWheel = e.ctrlKey ? 0 : sign(e.deltaY);
|
|
4823
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
4824
|
+
e.preventDefault(); // prevent page scrolling
|
|
4825
|
+
}
|
|
4779
4826
|
function onContextMenu(e) { e.preventDefault(); } // prevent right click menu
|
|
4780
4827
|
function onBlur() { inputClear(); } // reset input when focus is lost
|
|
4781
4828
|
|
|
@@ -4825,7 +4872,7 @@ function inputInit()
|
|
|
4825
4872
|
wasTouching = touching;
|
|
4826
4873
|
|
|
4827
4874
|
// prevent default handling like copy, magnifier lens, and scrolling
|
|
4828
|
-
if (inputPreventDefault &&
|
|
4875
|
+
if (inputPreventDefault && e.cancelable && document.hasFocus())
|
|
4829
4876
|
e.preventDefault();
|
|
4830
4877
|
|
|
4831
4878
|
// must return true so the document will get focus
|
|
@@ -4983,7 +5030,13 @@ function inputUpdate()
|
|
|
4983
5030
|
}
|
|
4984
5031
|
|
|
4985
5032
|
// return if gamepads are disabled or not supported
|
|
4986
|
-
|
|
5033
|
+
try {
|
|
5034
|
+
// protect against getGamepads disallowed security error
|
|
5035
|
+
if (!gamepadsEnable || !navigator?.getGamepads)
|
|
5036
|
+
return;
|
|
5037
|
+
} catch(e) {
|
|
5038
|
+
return;
|
|
5039
|
+
}
|
|
4987
5040
|
|
|
4988
5041
|
// only poll gamepads when focused or in debug mode
|
|
4989
5042
|
if (!debug && !document.hasFocus()) return;
|
|
@@ -5081,10 +5134,10 @@ function inputRender()
|
|
|
5081
5134
|
function touchGamepadRender()
|
|
5082
5135
|
{
|
|
5083
5136
|
if (!touchInputEnable || !isTouchDevice || headlessMode) return;
|
|
5084
|
-
if (!touchGamepadEnable || !touchGamepadTimer.isSet()) return;
|
|
5137
|
+
if (!touchGamepadEnable || !touchGamepadTimer.isSet() && touchGamepadDisplayTime) return;
|
|
5085
5138
|
|
|
5086
5139
|
// fade off when not touching or paused
|
|
5087
|
-
const alpha = percent(touchGamepadTimer.get(),
|
|
5140
|
+
const alpha = touchGamepadDisplayTime ? percent(touchGamepadTimer.get(), touchGamepadDisplayTime+1, touchGamepadDisplayTime) : 1;
|
|
5088
5141
|
if (!alpha || paused) return;
|
|
5089
5142
|
|
|
5090
5143
|
// setup the canvas
|
|
@@ -8655,7 +8708,7 @@ function zzfxM(instruments, patterns, sequence, BPM = 125)
|
|
|
8655
8708
|
* - Buttons
|
|
8656
8709
|
* - Checkboxes
|
|
8657
8710
|
* - Images
|
|
8658
|
-
* -
|
|
8711
|
+
* - Sliders
|
|
8659
8712
|
* - Video
|
|
8660
8713
|
* @namespace UISystem
|
|
8661
8714
|
*/
|
|
@@ -9852,7 +9905,7 @@ class UICheckbox extends UIObject
|
|
|
9852
9905
|
ASSERT(isString(text), 'ui checkbox must be a string');
|
|
9853
9906
|
ASSERT(isColor(color), 'ui checkbox color must be a color');
|
|
9854
9907
|
|
|
9855
|
-
/** @property {boolean} - Current percentage value of this
|
|
9908
|
+
/** @property {boolean} - Current percentage value of this slider 0-1 */
|
|
9856
9909
|
this.checked = checked;
|
|
9857
9910
|
// set properties
|
|
9858
9911
|
this.text = text;
|
|
@@ -9887,13 +9940,13 @@ class UICheckbox extends UIObject
|
|
|
9887
9940
|
|
|
9888
9941
|
///////////////////////////////////////////////////////////////////////////////
|
|
9889
9942
|
/**
|
|
9890
|
-
*
|
|
9943
|
+
* UISlider - A UI object that acts as a slider or scrollbar
|
|
9891
9944
|
* @extends UIObject
|
|
9892
9945
|
* @memberof UISystem
|
|
9893
9946
|
*/
|
|
9894
|
-
class
|
|
9947
|
+
class UISlider extends UIObject
|
|
9895
9948
|
{
|
|
9896
|
-
/** Create a
|
|
9949
|
+
/** Create a UISlider object
|
|
9897
9950
|
* @param {Vector2} [pos]
|
|
9898
9951
|
* @param {Vector2} [size]
|
|
9899
9952
|
* @param {number} [value]
|
|
@@ -9905,14 +9958,14 @@ class UIScrollbar extends UIObject
|
|
|
9905
9958
|
{
|
|
9906
9959
|
super(pos, size);
|
|
9907
9960
|
|
|
9908
|
-
ASSERT(isNumber(value), 'ui
|
|
9909
|
-
ASSERT(isString(text), 'ui
|
|
9910
|
-
ASSERT(isColor(color), 'ui
|
|
9911
|
-
ASSERT(isColor(handleColor), 'ui
|
|
9961
|
+
ASSERT(isNumber(value), 'ui slider value must be a number');
|
|
9962
|
+
ASSERT(isString(text), 'ui slider must be a string');
|
|
9963
|
+
ASSERT(isColor(color), 'ui slider color must be a color');
|
|
9964
|
+
ASSERT(isColor(handleColor), 'ui slider handleColor must be a color');
|
|
9912
9965
|
|
|
9913
|
-
/** @property {number} - Current percentage value of this
|
|
9966
|
+
/** @property {number} - Current percentage value of this slider 0-1 */
|
|
9914
9967
|
this.value = value;
|
|
9915
|
-
/** @property {Color} - Color for the handle part of the
|
|
9968
|
+
/** @property {Color} - Color for the handle part of the slider */
|
|
9916
9969
|
this.handleColor = handleColor.copy();
|
|
9917
9970
|
/** @property {boolean} - Should it fill up like a progress bar? */
|
|
9918
9971
|
this.fillMode = false;
|
|
@@ -9931,7 +9984,7 @@ class UIScrollbar extends UIObject
|
|
|
9931
9984
|
const oldValue = this.value;
|
|
9932
9985
|
if (this.isActiveObject())
|
|
9933
9986
|
{
|
|
9934
|
-
// handle horizontal or vertical
|
|
9987
|
+
// handle horizontal or vertical slider
|
|
9935
9988
|
const isHorizontal = this.size.x > this.size.y;
|
|
9936
9989
|
const handleSize = isHorizontal ? this.size.y : this.size.x;
|
|
9937
9990
|
const barSize = isHorizontal ? this.size.x : this.size.y;
|
|
@@ -9959,7 +10012,7 @@ class UIScrollbar extends UIObject
|
|
|
9959
10012
|
{
|
|
9960
10013
|
super.render();
|
|
9961
10014
|
|
|
9962
|
-
// handle horizontal or vertical
|
|
10015
|
+
// handle horizontal or vertical slider
|
|
9963
10016
|
const isHorizontal = this.size.x > this.size.y;
|
|
9964
10017
|
const barWidth = isHorizontal ? this.size.x : this.size.y;
|
|
9965
10018
|
const handleWidth = isHorizontal ? this.size.y : this.size.x;
|
|
@@ -9977,7 +10030,7 @@ class UIScrollbar extends UIObject
|
|
|
9977
10030
|
}
|
|
9978
10031
|
else
|
|
9979
10032
|
{
|
|
9980
|
-
// draw the
|
|
10033
|
+
// draw the slider handle
|
|
9981
10034
|
const value = clamp(isHorizontal ? this.value : 1 - this.value);
|
|
9982
10035
|
const p = (barWidth - handleWidth) * (value - .5);
|
|
9983
10036
|
const pos = this.pos.add(isHorizontal ? vec2(p, 0) : vec2(0, p));
|
|
@@ -9986,7 +10039,7 @@ class UIScrollbar extends UIObject
|
|
|
9986
10039
|
uiSystem.drawRect(pos, drawSize, color, this.lineWidth, this.lineColor, this.cornerRadius, this.gradientColor);
|
|
9987
10040
|
}
|
|
9988
10041
|
|
|
9989
|
-
// draw the text scaled to fit on the
|
|
10042
|
+
// draw the text scaled to fit on the slider
|
|
9990
10043
|
const textSize = this.getTextSize();
|
|
9991
10044
|
uiSystem.drawText(this.text, this.pos, textSize,
|
|
9992
10045
|
this.textColor, this.textLineWidth, this.textLineColor, this.align, this.font, this.fontStyle, true, this.textShadow);
|
|
@@ -10261,7 +10314,7 @@ class Box2dObject extends EngineObject
|
|
|
10261
10314
|
// draw non-edge fixtures
|
|
10262
10315
|
this.getFixtureList().forEach((fixture)=>
|
|
10263
10316
|
{
|
|
10264
|
-
const shape = box2d.
|
|
10317
|
+
const shape = box2d.castShapeObject(fixture.GetShape());
|
|
10265
10318
|
if (shape.GetType() !== box2d.instance.b2Shape.e_edge)
|
|
10266
10319
|
{
|
|
10267
10320
|
box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, useWebGL, context);
|
|
@@ -10901,7 +10954,7 @@ class Box2dJoint
|
|
|
10901
10954
|
constructor(jointDef)
|
|
10902
10955
|
{
|
|
10903
10956
|
/** @property {Object} - The Box2d joint */
|
|
10904
|
-
this.box2dJoint = box2d.
|
|
10957
|
+
this.box2dJoint = box2d.castJointObject(box2d.world.CreateJoint(jointDef));
|
|
10905
10958
|
}
|
|
10906
10959
|
|
|
10907
10960
|
/** Destroy this joint */
|
|
@@ -11186,7 +11239,7 @@ class Box2dRevoluteJoint extends Box2dJoint
|
|
|
11186
11239
|
|
|
11187
11240
|
/** Enable/disable the joint limit
|
|
11188
11241
|
* @param {boolean} [enable] */
|
|
11189
|
-
enableLimit(enable=true) { return this.box2dJoint.
|
|
11242
|
+
enableLimit(enable=true) { return this.box2dJoint.EnableLimit(enable); }
|
|
11190
11243
|
|
|
11191
11244
|
/** Get the lower joint limit
|
|
11192
11245
|
* @return {number} */
|
|
@@ -11344,7 +11397,7 @@ class Box2dPrismaticJoint extends Box2dJoint
|
|
|
11344
11397
|
|
|
11345
11398
|
/** Enable/disable the joint limit
|
|
11346
11399
|
* @param {boolean} [enable] */
|
|
11347
|
-
enableLimit(enable=true) { return this.box2dJoint.
|
|
11400
|
+
enableLimit(enable=true) { return this.box2dJoint.EnableLimit(enable); }
|
|
11348
11401
|
|
|
11349
11402
|
/** Get the lower joint limit
|
|
11350
11403
|
* @return {number} */
|
|
@@ -11958,7 +12011,7 @@ class Box2dPlugin
|
|
|
11958
12011
|
* @param {CanvasRenderingContext2D} [context] */
|
|
11959
12012
|
drawFixture(fixture, pos, angle, color=WHITE, lineColor=BLACK, lineWidth=.1, useWebgl, context)
|
|
11960
12013
|
{
|
|
11961
|
-
const shape = box2d.
|
|
12014
|
+
const shape = box2d.castShapeObject(fixture.GetShape());
|
|
11962
12015
|
switch (shape.GetType())
|
|
11963
12016
|
{
|
|
11964
12017
|
case box2d.instance.b2Shape.e_polygon:
|
|
@@ -12016,9 +12069,9 @@ class Box2dPlugin
|
|
|
12016
12069
|
* @param {Object} o */
|
|
12017
12070
|
isNull(o) { return !box2d.instance.getPointer(o); }
|
|
12018
12071
|
|
|
12019
|
-
/** casts a box2d object to
|
|
12072
|
+
/** casts a box2d object to a shape type
|
|
12020
12073
|
* @param {Object} o */
|
|
12021
|
-
|
|
12074
|
+
castShapeObject(o)
|
|
12022
12075
|
{
|
|
12023
12076
|
switch (o.GetType())
|
|
12024
12077
|
{
|
|
@@ -12030,6 +12083,17 @@ class Box2dPlugin
|
|
|
12030
12083
|
return box2d.instance.castObject(o, box2d.instance.b2PolygonShape);
|
|
12031
12084
|
case box2d.instance.b2Shape.e_chain:
|
|
12032
12085
|
return box2d.instance.castObject(o, box2d.instance.b2ChainShape);
|
|
12086
|
+
}
|
|
12087
|
+
|
|
12088
|
+
ASSERT(false, 'Unknown box2d object type');
|
|
12089
|
+
}
|
|
12090
|
+
|
|
12091
|
+
/** casts a box2d object to a joint type
|
|
12092
|
+
* @param {Object} o */
|
|
12093
|
+
castJointObject(o)
|
|
12094
|
+
{
|
|
12095
|
+
switch (o.GetType())
|
|
12096
|
+
{
|
|
12033
12097
|
case box2d.instance.e_revoluteJoint:
|
|
12034
12098
|
return box2d.instance.castObject(o, box2d.instance.b2RevoluteJoint);
|
|
12035
12099
|
case box2d.instance.e_prismaticJoint:
|
package/examples/box2d/game.js
CHANGED
|
@@ -24,7 +24,7 @@ LJS.setTilesPixelated(false);
|
|
|
24
24
|
///////////////////////////////////////////////////////////////////////////////
|
|
25
25
|
// game variables
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const sceneCount = 12;
|
|
28
28
|
const startScene = 0;
|
|
29
29
|
export let spriteAtlas, groundObject, mouseJoint, repeatSpawnTimer = new LJS.Timer;
|
|
30
30
|
const sound_click = new LJS.Sound([.2,.1,,,,.01,,,,,,,,,,,,,,,-500]);
|
|
@@ -125,7 +125,7 @@ function gameUpdate()
|
|
|
125
125
|
{
|
|
126
126
|
// change scene
|
|
127
127
|
const upPressed = LJS.keyWasPressed('ArrowUp');
|
|
128
|
-
setScene(LJS.mod(Scenes.scene + (upPressed?1:-1),
|
|
128
|
+
setScene(LJS.mod(Scenes.scene + (upPressed?1:-1), sceneCount));
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -446,6 +446,80 @@ export class ClothObject extends LJS.Box2dStaticObject
|
|
|
446
446
|
}
|
|
447
447
|
}
|
|
448
448
|
|
|
449
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
450
|
+
|
|
451
|
+
// ragdoll with head, arms, and legs connected to torso by revolute joints
|
|
452
|
+
export class RagdollObject extends LJS.Box2dObject
|
|
453
|
+
{
|
|
454
|
+
constructor(pos, scale=1, color=LJS.randColor())
|
|
455
|
+
{
|
|
456
|
+
const headSize = .8 * scale;
|
|
457
|
+
const torsoWidth = 1.2 * scale;
|
|
458
|
+
const torsoHeight = 2 * scale;
|
|
459
|
+
const limbWidth = .4 * scale;
|
|
460
|
+
const upperArmLen = 1 * scale;
|
|
461
|
+
const lowerArmLen = 1 * scale;
|
|
462
|
+
const upperLegLen = 1.1 * scale;
|
|
463
|
+
const lowerLegLen = 1.1 * scale;
|
|
464
|
+
|
|
465
|
+
// torso is the main body
|
|
466
|
+
super(pos, vec2(torsoWidth, torsoHeight), 0, 0, color);
|
|
467
|
+
this.addBox(vec2(torsoWidth, torsoHeight), vec2(), 0, density, friction, restitution);
|
|
468
|
+
this.limbs = [];
|
|
469
|
+
|
|
470
|
+
const addLimb = (offset, size, isCircle=false) =>
|
|
471
|
+
{
|
|
472
|
+
const o = new LJS.Box2dObject(pos.add(offset), size, 0, 0, color);
|
|
473
|
+
if (isCircle)
|
|
474
|
+
o.addCircle(size.x, vec2(), density, friction, restitution);
|
|
475
|
+
else
|
|
476
|
+
o.addBox(size, vec2(), 0, density, friction, restitution);
|
|
477
|
+
this.limbs.push(o);
|
|
478
|
+
return o;
|
|
479
|
+
};
|
|
480
|
+
const addJoint = (a, b, anchor, lower, upper) =>
|
|
481
|
+
{
|
|
482
|
+
const joint = new LJS.Box2dRevoluteJoint(a, b, anchor);
|
|
483
|
+
joint.enableLimit();
|
|
484
|
+
joint.setLimits(lower, upper);
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
// head
|
|
488
|
+
const head = addLimb(vec2(0, torsoHeight/2 + headSize/2 + .1*scale), vec2(headSize), true);
|
|
489
|
+
addJoint(this, head, pos.add(vec2(0, torsoHeight/2)), -.5, .5);
|
|
490
|
+
|
|
491
|
+
// arms
|
|
492
|
+
const shoulderY = torsoHeight/2 - .2*scale;
|
|
493
|
+
for (const side of [-1, 1])
|
|
494
|
+
{
|
|
495
|
+
const shoulder = pos.add(vec2(side*torsoWidth/2, shoulderY));
|
|
496
|
+
const upperArm = addLimb(vec2(side*(torsoWidth/2 + upperArmLen/2), shoulderY), vec2(upperArmLen, limbWidth));
|
|
497
|
+
addJoint(this, upperArm, shoulder, -2, 2);
|
|
498
|
+
|
|
499
|
+
const elbow = shoulder.add(vec2(side*upperArmLen, 0));
|
|
500
|
+
const lowerArm = addLimb(vec2(side*(torsoWidth/2 + upperArmLen + lowerArmLen/2), shoulderY), vec2(lowerArmLen, limbWidth));
|
|
501
|
+
addJoint(upperArm, lowerArm, elbow, -2.5, 2.5);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// legs
|
|
505
|
+
for (const side of [-1, 1])
|
|
506
|
+
{
|
|
507
|
+
const hip = pos.add(vec2(side*torsoWidth/4, -torsoHeight/2));
|
|
508
|
+
const upperLeg = addLimb(vec2(side*torsoWidth/4, -torsoHeight/2 - upperLegLen/2), vec2(limbWidth, upperLegLen));
|
|
509
|
+
addJoint(this, upperLeg, hip, -.5, 2.5);
|
|
510
|
+
|
|
511
|
+
const knee = hip.add(vec2(0, -upperLegLen));
|
|
512
|
+
const lowerLeg = addLimb(vec2(side*torsoWidth/4, -torsoHeight/2 - upperLegLen - lowerLegLen/2), vec2(limbWidth, lowerLegLen));
|
|
513
|
+
addJoint(upperLeg, lowerLeg, knee, -2.5, 2.5);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
destroy()
|
|
517
|
+
{
|
|
518
|
+
this.limbs.forEach(o => o.destroy());
|
|
519
|
+
super.destroy();
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
449
523
|
///////////////////////////////////////////////////////////////////////////////
|
|
450
524
|
// Effects
|
|
451
525
|
|
package/examples/box2d/scenes.js
CHANGED
|
@@ -184,4 +184,11 @@ export function loadScene(_scene)
|
|
|
184
184
|
for (let i=3;i--;)
|
|
185
185
|
new GameObjects.SoftBodyObject(vec2(20, 3+i*7), vec2(6-i), vec2(9-i), LJS.randColor());
|
|
186
186
|
}
|
|
187
|
+
if (scene == 11)
|
|
188
|
+
{
|
|
189
|
+
sceneName = 'Ragdolls';
|
|
190
|
+
new GameObjects.RagdollObject(vec2(20,5));
|
|
191
|
+
new GameObjects.RagdollObject(vec2(10,8), 2);
|
|
192
|
+
GameObjects.spawnPyramid(vec2(30,0), 6);
|
|
193
|
+
}
|
|
187
194
|
}
|
package/examples/shorts/music.js
CHANGED
|
@@ -20,7 +20,7 @@ function gameInit()
|
|
|
20
20
|
musicPlayer.addChild(infoText);
|
|
21
21
|
|
|
22
22
|
// volume slider
|
|
23
|
-
const volumeSlider = new
|
|
23
|
+
const volumeSlider = new UISlider(vec2(0, -20), vec2(400, 30),
|
|
24
24
|
musicVolume, 'Music Volume');
|
|
25
25
|
volumeSlider.fillMode = true;
|
|
26
26
|
musicPlayer.addChild(volumeSlider);
|
|
@@ -26,7 +26,7 @@ function gameInit()
|
|
|
26
26
|
musicPlayer.addChild(dropZoneText);
|
|
27
27
|
|
|
28
28
|
// volume slider
|
|
29
|
-
const volumeSlider = new
|
|
29
|
+
const volumeSlider = new UISlider(vec2(0, -20), vec2(400, 30),
|
|
30
30
|
musicVolume, 'Music Volume');
|
|
31
31
|
volumeSlider.fillMode = true;
|
|
32
32
|
musicPlayer.addChild(volumeSlider);
|
|
@@ -58,8 +58,8 @@ function gameInit()
|
|
|
58
58
|
stopButton.onClick = ()=> musicInstance?.stop();
|
|
59
59
|
musicPlayer.addChild(stopButton);
|
|
60
60
|
|
|
61
|
-
// progress bar and
|
|
62
|
-
progressBar = new
|
|
61
|
+
// progress bar and slider for seeking
|
|
62
|
+
progressBar = new UISlider(vec2(0, 120), vec2(400, 30), 0);
|
|
63
63
|
progressBar.disabledColor = RED;
|
|
64
64
|
progressBar.onChange = ()=>
|
|
65
65
|
{
|
|
@@ -91,7 +91,7 @@ function gameInit()
|
|
|
91
91
|
// create tempo slider
|
|
92
92
|
const minTempo = 120, maxTempo = 480;
|
|
93
93
|
const tempoPercent = percent(tempo, minTempo, maxTempo);
|
|
94
|
-
const tempoSlider = new
|
|
94
|
+
const tempoSlider = new UISlider(vec2(380,500), vec2(340,40), tempoPercent);
|
|
95
95
|
tempoSlider.onChange = ()=>
|
|
96
96
|
{
|
|
97
97
|
tempo = lerp(minTempo, maxTempo, tempoSlider.value);
|
|
@@ -30,7 +30,7 @@ function gameInit()
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// create non-interactive slider to display timer
|
|
33
|
-
timerSlider = new
|
|
33
|
+
timerSlider = new UISlider(vec2(0, 100), vec2(400, 50));
|
|
34
34
|
timerSlider.interactive = false;
|
|
35
35
|
timerSlider.update = ()=>
|
|
36
36
|
{
|
|
@@ -33,12 +33,12 @@ function gameInit()
|
|
|
33
33
|
uiMenu.addChild(textInput);
|
|
34
34
|
textInput.onChange = ()=> canvasClearColor = randColor();
|
|
35
35
|
|
|
36
|
-
// example
|
|
37
|
-
const
|
|
36
|
+
// example slider
|
|
37
|
+
const slider = new UISlider(vec2(0,90), vec2(400, 50),
|
|
38
38
|
soundVolume, 'Volume');
|
|
39
|
-
|
|
40
|
-
uiMenu.addChild(
|
|
41
|
-
|
|
39
|
+
slider.navigationIndex = ++navigationIndex;
|
|
40
|
+
uiMenu.addChild(slider);
|
|
41
|
+
slider.onChange = ()=> setSoundVolume(slider.value);
|
|
42
42
|
|
|
43
43
|
// exit button
|
|
44
44
|
const button1 = new UIButton(vec2(0,170), vec2(200, 50), 'Exit Menu');
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
</head><body>
|
|
8
8
|
|
|
9
9
|
<!-- LittleJS Engine -->
|
|
10
|
-
<script src=../../dist/littlejs.js?1.
|
|
10
|
+
<script src=../../dist/littlejs.js?1.18.1></script>
|
|
11
11
|
|
|
12
12
|
<!-- LittleJS Engine Source
|
|
13
13
|
<script src=../../src/engine.js></script>
|
|
@@ -32,4 +32,4 @@
|
|
|
32
32
|
-->
|
|
33
33
|
|
|
34
34
|
<!-- Add your game scripts here -->
|
|
35
|
-
<script src=game.js?1.
|
|
35
|
+
<script src=game.js?1.18.1></script>
|
|
@@ -67,12 +67,12 @@ function createUI()
|
|
|
67
67
|
checkbox.navigationIndex = ++navigationIndex;
|
|
68
68
|
checkbox.text = 'Test Checkbox';
|
|
69
69
|
|
|
70
|
-
// example
|
|
71
|
-
const
|
|
72
|
-
uiMenu.addChild(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
70
|
+
// example slider
|
|
71
|
+
const slider = new LJS.UISlider(vec2(0,60), vec2(350, 50));
|
|
72
|
+
uiMenu.addChild(slider);
|
|
73
|
+
slider.onChange = ()=> slider.text = slider.value.toFixed(2)
|
|
74
|
+
slider.onChange();
|
|
75
|
+
slider.navigationIndex = ++navigationIndex;
|
|
76
76
|
|
|
77
77
|
// example button
|
|
78
78
|
const button1 = new LJS.UIButton(vec2(0,140), vec2(350, 50), 'Test Button');
|