littlejsengine 1.14.23 → 1.15.2

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.
Files changed (52) hide show
  1. package/dist/littlejs.d.ts +184 -30
  2. package/dist/littlejs.esm.js +589 -147
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +583 -147
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +546 -147
  7. package/examples/box2d/gameObjects.js +6 -10
  8. package/examples/box2d/scenes.js +2 -2
  9. package/examples/breakout/gameObjects.js +1 -1
  10. package/examples/electron/index.html +2 -2
  11. package/examples/index.html +11 -7
  12. package/examples/platformer/gameCharacter.js +3 -2
  13. package/examples/platformer/gameObjects.js +3 -8
  14. package/examples/shorts/base.html +2 -2
  15. package/examples/shorts/box2d.js +2 -2
  16. package/examples/shorts/box2dCar.js +18 -7
  17. package/examples/shorts/box2dPool.js +108 -0
  18. package/examples/shorts/debugDraw.js +2 -3
  19. package/examples/shorts/flappyGame.js +1 -0
  20. package/examples/shorts/fps.js +1 -1
  21. package/examples/shorts/hillGlideGame.js +0 -2
  22. package/examples/shorts/input.js +59 -0
  23. package/examples/shorts/landerGame.js +1 -3
  24. package/examples/shorts/medals.js +15 -9
  25. package/examples/shorts/music.js +15 -17
  26. package/examples/shorts/musicPlayer.js +24 -24
  27. package/examples/shorts/platformer.js +0 -2
  28. package/examples/shorts/slidingPuzzle.js +1 -1
  29. package/examples/shorts/song.mp3 +0 -0
  30. package/examples/shorts/spaceGame.js +0 -2
  31. package/examples/shorts/spriteAtlas.js +0 -2
  32. package/examples/shorts/tiltedView.js +0 -3
  33. package/examples/shorts/timers.js +1 -2
  34. package/examples/shorts/topDown.js +0 -2
  35. package/examples/shorts/video.webm +0 -0
  36. package/examples/shorts/videoPlayer.js +33 -0
  37. package/examples/starter/index.html +3 -3
  38. package/package.json +1 -1
  39. package/plugins/box2d.js +170 -50
  40. package/plugins/pluginExport.js +3 -0
  41. package/plugins/uiSystem.js +229 -27
  42. package/src/engine.js +21 -18
  43. package/src/engineAudio.js +14 -2
  44. package/src/engineDebug.js +37 -0
  45. package/src/engineDraw.js +21 -13
  46. package/src/engineExport.js +3 -0
  47. package/src/engineInput.js +24 -12
  48. package/src/engineMedals.js +2 -2
  49. package/src/engineObject.js +24 -4
  50. package/src/engineParticles.js +4 -6
  51. package/src/engineTileLayer.js +2 -0
  52. package/src/engineUtilities.js +35 -13
package/plugins/box2d.js CHANGED
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * LittleJS Box2D Physics Plugin
3
3
  * - Box2dObject extends EngineObject with Box2D physics
4
- * - Call box2dInit() before engineInit() to enable
4
+ * - Call box2dInit() to enable
5
5
  * - You will also need to include box2d.wasm.js
6
- * - Uses a super fast web assembly port of Box2D
6
+ * - Uses a super fast web assembly port of Box2D v2.3.1
7
7
  * - More info: https://github.com/kripken/box2d.js
8
8
  * - Functions to create polygon, circle, and edge shapes
9
9
  * - Contact begin and end callbacks
@@ -35,9 +35,9 @@ function box2dSetDebug(enable) { box2dDebug = enable; }
35
35
  ///////////////////////////////////////////////////////////////////////////////
36
36
  /**
37
37
  * Box2D Object - extend with your own custom physics objects
38
- * - A LittleJS object with Box2D physics
39
- * - Each object has a Box2D body which can have multiple fixtures and joints
38
+ * - A LittleJS object with Box2D physics, dynamic by default
40
39
  * - Provides interface for Box2D body and fixture functions
40
+ * - Each object can have multiple fixtures and joints
41
41
  * @extends EngineObject
42
42
  * @memberof Box2D
43
43
  */
@@ -51,7 +51,7 @@ class Box2dObject extends EngineObject
51
51
  * @param {Color} [color]
52
52
  * @param {number} [bodyType]
53
53
  * @param {number} [renderOrder] */
54
- constructor(pos, size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
54
+ constructor(pos=vec2(), size=vec2(), tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
55
55
  {
56
56
  super(pos, size, tileInfo, angle, color, renderOrder);
57
57
 
@@ -63,24 +63,27 @@ class Box2dObject extends EngineObject
63
63
  this.body = box2d.world.CreateBody(bodyDef);
64
64
  this.body.object = this;
65
65
  this.lineColor = BLACK;
66
+ box2d.objects.push(this);
67
+
68
+ // edge lists and loops for drawing
69
+ this.edgeLists = [];
70
+ this.edgeLoops = [];
66
71
  }
67
72
 
68
73
  /** Destroy this object and its physics body */
69
74
  destroy()
70
75
  {
76
+ if (this.destroyed)
77
+ return;
78
+
71
79
  // destroy physics body, fixtures, and joints
72
- this.body && box2d.world.DestroyBody(this.body);
73
- this.body = 0;
80
+ ASSERT(this.body, 'Box2dObject has no body to destroy');
81
+ box2d.world.DestroyBody(this.body);
74
82
  super.destroy();
75
83
  }
76
84
 
77
- /** Copy box2d update sim data */
78
- update()
79
- {
80
- // use box2d physics update instead of normal engine update
81
- this.pos = box2d.vec2From(this.body.GetPosition());
82
- this.angle = -this.body.GetAngle();
83
- }
85
+ /** Box2d objects updated with Box2d world step */
86
+ updatePhysics() {}
84
87
 
85
88
  /** Render the object, uses box2d drawing if no tile info exists */
86
89
  render()
@@ -106,10 +109,23 @@ class Box2dObject extends EngineObject
106
109
  * @param {Color} [lineColor]
107
110
  * @param {number} [lineWidth]
108
111
  * @param {CanvasRenderingContext2D} [context] */
109
- drawFixtures(color=WHITE, lineColor, lineWidth=.1, context)
112
+ drawFixtures(color=WHITE, lineColor=BLACK, lineWidth=.1, context)
110
113
  {
111
- this.getFixtureList().forEach(fixture=>
112
- box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, context));
114
+ // draw non-edge fixtures
115
+ this.getFixtureList().forEach((fixture)=>
116
+ {
117
+ const shape = box2d.castObjectType(fixture.GetShape());
118
+ if (shape.GetType() !== box2d.instance.b2Shape.e_edge)
119
+ {
120
+ box2d.drawFixture(fixture, this.pos, this.angle, color, lineColor, lineWidth, context);
121
+ }
122
+ });
123
+
124
+ // draw edges using a single draw line for better connections
125
+ this.edgeLists.forEach(points=>
126
+ drawLineList(points, lineWidth, lineColor, false, this.pos, this.angle));
127
+ this.edgeLoops.forEach(points=>
128
+ drawLineList(points, lineWidth, lineColor, true, this.pos, this.angle));
113
129
  }
114
130
 
115
131
  ///////////////////////////////////////////////////////////////////////////////
@@ -134,6 +150,10 @@ class Box2dObject extends EngineObject
134
150
  * @param {boolean} [isSensor] */
135
151
  addShape(shape, density=1, friction=.2, restitution=0, isSensor=false)
136
152
  {
153
+ ASSERT(isNumber(density), 'density must be a number');
154
+ ASSERT(isNumber(friction), 'friction must be a number');
155
+ ASSERT(isNumber(restitution), 'restitution must be a number');
156
+
137
157
  const fd = new box2d.instance.b2FixtureDef();
138
158
  fd.set_shape(shape);
139
159
  fd.set_density(density);
@@ -153,6 +173,11 @@ class Box2dObject extends EngineObject
153
173
  * @param {boolean} [isSensor] */
154
174
  addBox(size=vec2(1), offset=vec2(), angle=0, density, friction, restitution, isSensor)
155
175
  {
176
+ ASSERT(isVector2(size), 'size must be a Vector2');
177
+ ASSERT(size.x > 0 && size.y > 0, 'size must be positive');
178
+ ASSERT(isVector2(offset), 'offset must be a Vector2');
179
+ ASSERT(isNumber(angle), 'angle must be a number');
180
+
156
181
  const shape = new box2d.instance.b2PolygonShape();
157
182
  shape.SetAsBox(size.x/2, size.y/2, box2d.vec2dTo(offset), angle);
158
183
  return this.addShape(shape, density, friction, restitution, isSensor);
@@ -166,6 +191,8 @@ class Box2dObject extends EngineObject
166
191
  * @param {boolean} [isSensor] */
167
192
  addPoly(points, density, friction, restitution, isSensor)
168
193
  {
194
+ ASSERT(isArray(points), 'points must be an array');
195
+
169
196
  function box2dCreatePolygonShape(points)
170
197
  {
171
198
  function box2dCreatePointList(points)
@@ -201,6 +228,9 @@ class Box2dObject extends EngineObject
201
228
  * @param {boolean} [isSensor] */
202
229
  addRegularPoly(diameter=1, sides=8, density, friction, restitution, isSensor)
203
230
  {
231
+ ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
232
+ ASSERT(isNumber(sides) && sides>2, 'sides must be a positive number greater than 2');
233
+
204
234
  const points = [];
205
235
  const radius = diameter/2;
206
236
  for (let i=sides; i--;)
@@ -216,6 +246,8 @@ class Box2dObject extends EngineObject
216
246
  * @param {boolean} [isSensor] */
217
247
  addRandomPoly(diameter=1, density, friction, restitution, isSensor)
218
248
  {
249
+ ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
250
+
219
251
  const sides = randInt(3, 9);
220
252
  const points = [];
221
253
  const radius = diameter/2;
@@ -233,6 +265,9 @@ class Box2dObject extends EngineObject
233
265
  * @param {boolean} [isSensor] */
234
266
  addCircle(diameter=1, offset=vec2(), density, friction, restitution, isSensor)
235
267
  {
268
+ ASSERT(isNumber(diameter) && diameter>0, 'diameter must be a positive number');
269
+ ASSERT(isVector2(offset), 'offset must be a Vector2');
270
+
236
271
  const shape = new box2d.instance.b2CircleShape();
237
272
  shape.set_m_p(box2d.vec2dTo(offset));
238
273
  shape.set_m_radius(diameter/2);
@@ -248,53 +283,63 @@ class Box2dObject extends EngineObject
248
283
  * @param {boolean} [isSensor] */
249
284
  addEdge(point1, point2, density, friction, restitution, isSensor)
250
285
  {
286
+ ASSERT(isVector2(point1), 'point1 must be a Vector2');
287
+ ASSERT(isVector2(point2), 'point2 must be a Vector2');
288
+
251
289
  const shape = new box2d.instance.b2EdgeShape();
252
290
  shape.Set(box2d.vec2dTo(point1), box2d.vec2dTo(point2));
253
291
  return this.addShape(shape, density, friction, restitution, isSensor);
254
292
  }
255
293
 
256
- /** Add an edge loop to the body, an edge loop connects the end points
294
+ /** Add an edge list to the body
257
295
  * @param {Array<Vector2>} points
258
296
  * @param {number} [density]
259
297
  * @param {number} [friction]
260
298
  * @param {number} [restitution]
261
299
  * @param {boolean} [isSensor] */
262
- addEdgeLoop(points, density, friction, restitution, isSensor)
300
+ addEdgeList(points, density, friction, restitution, isSensor)
263
301
  {
264
- const fixtures = [];
265
- const getPoint = i=> points[mod(i,points.length)];
266
- for (let i=0; i<points.length; ++i)
302
+ ASSERT(isArray(points), 'points must be an array');
303
+ const fixtures = [], edgePoints = [];
304
+ for (let i=0; i<points.length-1; ++i)
267
305
  {
268
306
  const shape = new box2d.instance.b2EdgeShape();
269
- shape.set_m_vertex0(box2d.vec2dTo(getPoint(i-1)));
270
- shape.set_m_vertex1(box2d.vec2dTo(getPoint(i+0)));
271
- shape.set_m_vertex2(box2d.vec2dTo(getPoint(i+1)));
272
- shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
307
+ points[i-1] && shape.set_m_vertex0(box2d.vec2dTo(points[i-1]));
308
+ points[i+0] && shape.set_m_vertex1(box2d.vec2dTo(points[i+0]));
309
+ points[i+1] && shape.set_m_vertex2(box2d.vec2dTo(points[i+1]));
310
+ points[i+2] && shape.set_m_vertex3(box2d.vec2dTo(points[i+2]));
273
311
  const f = this.addShape(shape, density, friction, restitution, isSensor);
274
312
  fixtures.push(f);
313
+ edgePoints.push(points[i].copy());
275
314
  }
315
+ edgePoints.push(points[points.length-1].copy());
316
+ this.edgeLists.push(edgePoints);
276
317
  return fixtures;
277
318
  }
278
319
 
279
- /** Add an edge list to the body
320
+ /** Add an edge loop to the body, an edge loop connects the end points
280
321
  * @param {Array<Vector2>} points
281
322
  * @param {number} [density]
282
323
  * @param {number} [friction]
283
324
  * @param {number} [restitution]
284
325
  * @param {boolean} [isSensor] */
285
- addEdgeList(points, density, friction, restitution, isSensor)
326
+ addEdgeLoop(points, density, friction, restitution, isSensor)
286
327
  {
287
- const fixtures = [];
288
- for (let i=0; i<points.length-1; ++i)
328
+ ASSERT(isArray(points), 'points must be an array');
329
+ const fixtures = [], edgePoints = [];
330
+ const getPoint = i=> points[mod(i,points.length)];
331
+ for (let i=0; i<points.length; ++i)
289
332
  {
290
333
  const shape = new box2d.instance.b2EdgeShape();
291
- points[i-1] && shape.set_m_vertex0(box2d.vec2dTo(points[i-1]));
292
- points[i+0] && shape.set_m_vertex1(box2d.vec2dTo(points[i+0]));
293
- points[i+1] && shape.set_m_vertex2(box2d.vec2dTo(points[i+1]));
294
- points[i+2] && shape.set_m_vertex3(box2d.vec2dTo(points[i+2]));
334
+ shape.set_m_vertex0(box2d.vec2dTo(getPoint(i-1)));
335
+ shape.set_m_vertex1(box2d.vec2dTo(getPoint(i+0)));
336
+ shape.set_m_vertex2(box2d.vec2dTo(getPoint(i+1)));
337
+ shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
295
338
  const f = this.addShape(shape, density, friction, restitution, isSensor);
296
339
  fixtures.push(f);
340
+ i < points.length && edgePoints.push(points[i].copy());
297
341
  }
342
+ this.edgeLoops.push(edgePoints);
298
343
  return fixtures;
299
344
  }
300
345
 
@@ -329,6 +374,10 @@ class Box2dObject extends EngineObject
329
374
  * @return {number} */
330
375
  getBodyType() { return this.body.GetType(); }
331
376
 
377
+ /** Get the speed of this object
378
+ * @return {number} */
379
+ getSpeed() { return this.getLinearVelocity().length(); }
380
+
332
381
  ///////////////////////////////////////////////////////////////////////////////
333
382
  // physics set functions
334
383
 
@@ -344,33 +393,40 @@ class Box2dObject extends EngineObject
344
393
 
345
394
  /** Sets the position
346
395
  * @param {Vector2} pos */
347
- setPosition(pos) { this.setTransform(pos, this.body.GetAngle()); }
396
+ setPosition(pos)
397
+ { this.setTransform(pos, this.body.GetAngle()); }
348
398
 
349
399
  /** Sets the angle
350
400
  * @param {number} angle */
351
- setAngle(angle) { this.setTransform(box2d.vec2From(this.body.GetPosition()), -angle); }
401
+ setAngle(angle)
402
+ { this.setTransform(box2d.vec2From(this.body.GetPosition()), -angle); }
352
403
 
353
404
  /** Sets the linear velocity
354
405
  * @param {Vector2} velocity */
355
- setLinearVelocity(velocity) { this.body.SetLinearVelocity(box2d.vec2dTo(velocity)); }
406
+ setLinearVelocity(velocity)
407
+ { this.body.SetLinearVelocity(box2d.vec2dTo(velocity)); }
356
408
 
357
409
  /** Sets the angular velocity
358
410
  * @param {number} angularVelocity */
359
- setAngularVelocity(angularVelocity) { this.body.SetAngularVelocity(angularVelocity); }
411
+ setAngularVelocity(angularVelocity)
412
+ { this.body.SetAngularVelocity(angularVelocity); }
360
413
 
361
414
  /** Sets the linear damping
362
415
  * @param {number} damping */
363
- setLinearDamping(damping) { this.body.SetLinearDamping(damping); }
416
+ setLinearDamping(damping)
417
+ { this.body.SetLinearDamping(damping); }
364
418
 
365
419
  /** Sets the angular damping
366
420
  * @param {number} damping */
367
- setAngularDamping(damping) { this.body.SetAngularDamping(damping); }
421
+ setAngularDamping(damping)
422
+ { this.body.SetAngularDamping(damping); }
368
423
 
369
424
  /** Sets the gravity scale
370
425
  * @param {number} [scale] */
371
- setGravityScale(scale=1) { this.body.SetGravityScale(this.gravityScale = scale); }
426
+ setGravityScale(scale=1)
427
+ { this.body.SetGravityScale(this.gravityScale = scale); }
372
428
 
373
- /** Should this body be treated like a bullet for continuous collision detection?
429
+ /** Should be like a bullet for continuous collision detection?
374
430
  * @param {boolean} [isBullet] */
375
431
  setBullet(isBullet=true) { this.body.SetBullet(isBullet); }
376
432
 
@@ -384,11 +440,13 @@ class Box2dObject extends EngineObject
384
440
 
385
441
  /** Set whether the body is allowed to sleep
386
442
  * @param {boolean} [isAllowed] */
387
- setSleepingAllowed(isAllowed=true) { this.body.SetSleepingAllowed(isAllowed); }
443
+ setSleepingAllowed(isAllowed=true)
444
+ { this.body.SetSleepingAllowed(isAllowed); }
388
445
 
389
446
  /** Set whether the body can rotate
390
447
  * @param {boolean} [isFixed] */
391
- setFixedRotation(isFixed=true) { this.body.SetFixedRotation(isFixed); }
448
+ setFixedRotation(isFixed=true)
449
+ { this.body.SetFixedRotation(isFixed); }
392
450
 
393
451
  /** Set the center of mass of the body
394
452
  * @param {Vector2} center */
@@ -400,10 +458,11 @@ class Box2dObject extends EngineObject
400
458
 
401
459
  /** Set the moment of inertia of the body
402
460
  * @param {number} momentOfInertia */
403
- setMomentOfInertia(momentOfInertia) { this.setMassData(undefined, undefined, momentOfInertia) }
461
+ setMomentOfInertia(momentOfInertia)
462
+ { this.setMassData(undefined, undefined, momentOfInertia) }
404
463
 
405
464
  /** Reset the mass, center of mass, and moment */
406
- resetMassData() { this.body.ResetMassData(); }
465
+ resetMassData() { this.body.ResetMassData(); }
407
466
 
408
467
  /** Set the mass data of the body
409
468
  * @param {Vector2} [localCenter]
@@ -1045,6 +1104,50 @@ class Box2dPrismaticJoint extends Box2dJoint
1045
1104
  getMotorForce(time) { return this.box2dJoint.GetMotorForce(1/time); }
1046
1105
  }
1047
1106
 
1107
+ ///////////////////////////////////////////////////////////////////////////////
1108
+ /**
1109
+ * Box2D Static Object - Box2d with a static physics body
1110
+ * @extends Box2dObject
1111
+ * @memberof Box2D
1112
+ */
1113
+ class Box2dStaticObject extends Box2dObject
1114
+ {
1115
+ /** Create a LittleJS object with Box2d physics
1116
+ * @param {Vector2} [pos]
1117
+ * @param {Vector2} [size]
1118
+ * @param {TileInfo} [tileInfo]
1119
+ * @param {number} [angle]
1120
+ * @param {Color} [color]
1121
+ * @param {number} [renderOrder] */
1122
+ constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
1123
+ {
1124
+ const bodyType = box2d.bodyTypeStatic;
1125
+ super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
1126
+ }
1127
+ }
1128
+
1129
+ ///////////////////////////////////////////////////////////////////////////////
1130
+ /**
1131
+ * Box2D Kiematic Object - Box2d with a kinematic physics body
1132
+ * @extends Box2dObject
1133
+ * @memberof Box2D
1134
+ */
1135
+ class Box2dKiematicObject extends Box2dObject
1136
+ {
1137
+ /** Create a LittleJS object with Box2d physics
1138
+ * @param {Vector2} [pos]
1139
+ * @param {Vector2} [size]
1140
+ * @param {TileInfo} [tileInfo]
1141
+ * @param {number} [angle]
1142
+ * @param {Color} [color]
1143
+ * @param {number} [renderOrder] */
1144
+ constructor(pos, size, tileInfo, angle=0, color, renderOrder=0)
1145
+ {
1146
+ const bodyType = box2d.bodyTypeKinematic;
1147
+ super(pos, size, tileInfo, angle, color, bodyType, renderOrder);
1148
+ }
1149
+ }
1150
+
1048
1151
  ///////////////////////////////////////////////////////////////////////////////
1049
1152
  /**
1050
1153
  * Box2D Wheel Joint
@@ -1407,6 +1510,7 @@ class Box2dPlugin
1407
1510
  box2d = this;
1408
1511
  this.instance = instance;
1409
1512
  this.world = new box2d.instance.b2World();
1513
+ this.objects = [];
1410
1514
 
1411
1515
  /** @property {number} - Velocity iterations per update*/
1412
1516
  this.velocityIterations = 8;
@@ -1649,9 +1753,10 @@ class Box2dPlugin
1649
1753
 
1650
1754
  /** converts a box2d vec2 pointer to a Vector2
1651
1755
  * @param {Object} v */
1652
- vec2FromPointer(v)
1756
+ vec2FromPointer(vp)
1653
1757
  {
1654
- return box2d.vec2From(box2d.instance.wrapPointer(v, box2d.instance.b2Vec2));
1758
+ const v = box2d.instance.wrapPointer(vp, box2d.instance.b2Vec2);
1759
+ return box2d.vec2From(v);
1655
1760
  }
1656
1761
 
1657
1762
  /** converts a Vector2 to a box2 vec2
@@ -1725,8 +1830,23 @@ async function box2dInit()
1725
1830
  // add the box2d plugin to the engine
1726
1831
  function box2dUpdate()
1727
1832
  {
1728
- if (!paused)
1729
- box2d.step();
1833
+ if (paused)
1834
+ return;
1835
+
1836
+ box2d.step();
1837
+
1838
+ // remove destroyed objects
1839
+ box2d.objects = box2d.objects.filter(o=>!o.destroyed);
1840
+
1841
+ // copy box2d physics results to engine objects
1842
+ for (const o of box2d.objects)
1843
+ {
1844
+ if (o.body)
1845
+ {
1846
+ o.pos = box2d.vec2From(o.body.GetPosition());
1847
+ o.angle = -o.body.GetAngle();
1848
+ }
1849
+ }
1730
1850
  }
1731
1851
  function box2dRender()
1732
1852
  {
@@ -25,6 +25,7 @@ export
25
25
  UIButton,
26
26
  UICheckbox,
27
27
  UIScrollbar,
28
+ UIVideo,
28
29
 
29
30
  // Box2D Physics
30
31
  box2d,
@@ -33,6 +34,8 @@ export
33
34
  box2dInit,
34
35
  Box2dPlugin,
35
36
  Box2dObject,
37
+ Box2dStaticObject,
38
+ Box2dKiematicObject,
36
39
  Box2dRaycastResult,
37
40
  Box2dJoint,
38
41
  Box2dTargetJoint,