littlejsengine 1.11.13 → 1.12.4

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 (79) hide show
  1. package/dist/littlejs.d.ts +1418 -109
  2. package/dist/littlejs.esm.js +3495 -581
  3. package/dist/littlejs.esm.min.js +1 -1
  4. package/dist/littlejs.js +3258 -399
  5. package/dist/littlejs.min.js +1 -1
  6. package/dist/littlejs.release.js +3147 -379
  7. package/examples/box2d/game.js +73 -45
  8. package/examples/box2d/gameObjects.js +96 -92
  9. package/examples/box2d/index.html +2 -7
  10. package/examples/box2d/scenes.js +82 -92
  11. package/examples/breakout/game.js +62 -30
  12. package/examples/breakout/gameObjects.js +45 -47
  13. package/examples/breakout/index.html +1 -5
  14. package/examples/breakoutTutorial/game.js +36 -29
  15. package/examples/breakoutTutorial/index.html +1 -3
  16. package/examples/empty/game.js +5 -2
  17. package/examples/empty/index.html +1 -3
  18. package/examples/htmlMenu/game.js +25 -19
  19. package/examples/htmlMenu/index.html +5 -7
  20. package/examples/index.html +2 -3
  21. package/examples/module/game.js +32 -34
  22. package/examples/module/index.html +1 -1
  23. package/examples/particles/index.html +27 -22
  24. package/examples/platformer/game.js +71 -48
  25. package/examples/platformer/gameCharacter.js +42 -34
  26. package/examples/platformer/gameEffects.js +82 -75
  27. package/examples/platformer/gameLevel.js +67 -38
  28. package/examples/platformer/{gameLevelData.js → gameLevelData.json} +1 -11
  29. package/examples/platformer/gameObjects.js +70 -64
  30. package/examples/platformer/gamePlayer.js +12 -7
  31. package/examples/platformer/index.html +1 -9
  32. package/examples/puzzle/game.js +58 -42
  33. package/examples/puzzle/index.html +1 -3
  34. package/examples/shorts/base.html +2 -2
  35. package/examples/shorts/particles.js +1 -1
  36. package/examples/shorts/platformer.js +1 -1
  37. package/examples/shorts/tileLayer.js +5 -6
  38. package/examples/shorts/tiles.png +0 -0
  39. package/examples/starter/build.js +4 -4
  40. package/examples/starter/game.js +9 -12
  41. package/examples/starter/index.html +13 -13
  42. package/examples/stress/index.html +44 -43
  43. package/examples/typescript/build.js +17 -18
  44. package/examples/typescript/game.js +32 -34
  45. package/examples/typescript/game.ts +32 -34
  46. package/examples/typescript/index.html +1 -1
  47. package/examples/uiSystem/game.js +33 -36
  48. package/examples/uiSystem/index.html +1 -5
  49. package/package.json +3 -3
  50. package/plugins/box2d.js +1556 -640
  51. package/plugins/{Box2D_v2.3.1_min.wasm.js → box2d.wasm.js} +1 -1
  52. package/plugins/newgrounds.js +7 -8
  53. package/plugins/pluginExport.js +50 -0
  54. package/plugins/postProcess.js +94 -93
  55. package/plugins/uiSystem.js +145 -161
  56. package/plugins/zzfxm.js +163 -0
  57. package/reference.md +29 -27
  58. package/src/engine.js +15 -20
  59. package/src/engineAudio.js +74 -204
  60. package/src/engineBuild.js +29 -4
  61. package/src/engineDebug.js +105 -9
  62. package/src/engineDraw.js +27 -14
  63. package/src/engineExport.js +12 -8
  64. package/src/engineInput.js +3 -1
  65. package/src/engineObject.js +18 -14
  66. package/src/engineParticles.js +6 -1
  67. package/src/engineRelease.js +6 -1
  68. package/src/engineSettings.js +8 -8
  69. package/src/engineTileLayer.js +179 -96
  70. package/src/engineUtilities.js +5 -11
  71. package/src/engineWebGL.js +19 -7
  72. package/examples/starter/build/index.html +0 -2
  73. package/examples/starter/build/index.js +0 -1
  74. package/examples/starter/build/tiles.png +0 -0
  75. package/examples/starter/game.zip +0 -0
  76. package/examples/typescript/build/dist/littlejs.esm.js +0 -4834
  77. package/examples/typescript/build/examples/typescript/build.js +0 -24
  78. package/examples/typescript/build/examples/typescript/game.js +0 -102
  79. /package/plugins/{Box2D_v2.3.1_min.wasm.wasm → box2d.wasm.wasm} +0 -0
package/plugins/box2d.js CHANGED
@@ -1,113 +1,199 @@
1
1
  /**
2
- * LittleJS Box2D Plugin
2
+ * LittleJS Box2D Physics Plugin
3
3
  * - Box2dObject extends EngineObject with Box2D physics
4
+ * - Call box2dEngineInit() to start instead of normal engineInit()
5
+ * - You will also need to include box2d.wasm.js
4
6
  * - Uses box2d.js super fast web assembly port of Box2D
5
7
  * - More info: https://github.com/kripken/box2d.js
8
+ * - Fully wraps everything in Box2d
6
9
  * - Functions to create polygon, circle, and edge shapes
7
10
  * - Raycasting and querying
8
11
  * - Joint creation
9
12
  * - Contact begin and end callbacks
10
13
  * - Debug physics drawing
11
- * - Call box2dEngineInit to start
12
14
  */
13
15
 
14
16
  'use strict';
15
-
17
+
18
+ /** Global Box2d Plugin object
19
+ * @type {Box2dPlugin} */
16
20
  let box2d;
17
- let box2dWorld;
18
- let box2dDebugDraw;
21
+
22
+ /** Enable Box2D debug drawing
23
+ * @type {boolean}
24
+ * @default */
19
25
  let box2dDebug = false;
20
- let box2dStepIterations = 3;
21
- const box2dBodyTypeStatic = 0;
22
- const box2dBodyTypeKinematic = 1;
23
- const box2dBodyTypeDynamic = 2;
24
26
 
25
- ///////////////////////////////////////////////////////////////////////////////
26
- // Box2D Object - extend with your own custom physics objects
27
+ /** Enable Box2D debug drawing
28
+ * @param {boolean} enable */
29
+ function box2dSetDebug(enable) { box2dDebug = enable; }
27
30
 
31
+ ///////////////////////////////////////////////////////////////////////////////
32
+ /**
33
+ * Box2D Object - extend with your own custom physics objects
34
+ * - A LittleJS object with Box2D physics
35
+ * - Each object has a Box2D body which can have multiple fixtures and joints
36
+ * - Provides interface for Box2D body and fixture functions
37
+ * @extends EngineObject
38
+ */
28
39
  class Box2dObject extends EngineObject
29
40
  {
30
- constructor(pos=vec2(), size, tileInfo, angle=0, color, bodyType=box2dBodyTypeDynamic, renderOrder=0)
41
+ /** Create a LittleJS object with Box2d physics
42
+ * @param {Vector2} [pos]
43
+ * @param {Vector2} [size]
44
+ * @param {TileInfo} [tileInfo]
45
+ * @param {number} [angle]
46
+ * @param {Color} [color]
47
+ * @param {number} [bodyType]
48
+ * @param {number} [renderOrder] */
49
+ constructor(pos=vec2(), size, tileInfo, angle=0, color, bodyType=box2d.bodyTypeDynamic, renderOrder=0)
31
50
  {
32
51
  super(pos, size, tileInfo, angle, color, renderOrder);
33
52
 
34
53
  // create physics body
35
- const bodyDef = new box2d.b2BodyDef();
54
+ const bodyDef = new box2d.instance.b2BodyDef();
36
55
  bodyDef.set_type(bodyType);
37
- bodyDef.set_position(pos.getBox2d());
56
+ bodyDef.set_position(box2d.vec2dTo(pos));
38
57
  bodyDef.set_angle(-angle);
39
- this.body = box2dWorld.CreateBody(bodyDef);
58
+ this.body = box2d.world.CreateBody(bodyDef);
40
59
  this.body.object = this;
41
60
  this.outlineColor = BLACK;
42
61
  }
43
62
 
63
+ /** Destroy this object and it's physics body */
44
64
  destroy()
45
65
  {
46
66
  // destroy physics body, fixtures, and joints
47
- this.body && box2dWorld.DestroyBody(this.body);
67
+ this.body && box2d.world.DestroyBody(this.body);
48
68
  this.body = 0;
49
69
  super.destroy();
50
70
  }
51
71
 
72
+ /** Copy box2d update sim data */
52
73
  update()
53
74
  {
54
75
  // use box2d physics update
55
- this.pos.setBox2d(this.body.GetPosition());
76
+ this.pos = box2d.vec2From(this.body.GetPosition());
56
77
  this.angle = -this.body.GetAngle();
57
78
  }
58
79
 
80
+ /** Render the object, uses box2d drawing if no tile info exists */
59
81
  render()
60
82
  {
61
83
  // use default render or draw fixtures
62
84
  if (this.tileInfo)
63
85
  super.render();
64
86
  else
65
- this.box2dDrawFixtures(this.color, this.outlineColor, this.lineWidth);
87
+ this.drawFixtures(this.color, this.outlineColor, this.lineWidth, mainContext);
66
88
  }
67
89
 
90
+ /** Render debug info */
68
91
  renderDebugInfo()
69
92
  {
70
93
  const isAsleep = !this.getIsAwake();
71
- const isStatic = this.getIsStatic();
72
- const color = rgb(isAsleep?1:0 ,isAsleep?1:0, isStatic?1:0, .5);
73
- this.box2dDrawFixtures(color);
94
+ const isStatic = this.getBodyType() == box2d.bodyTypeStatic;
95
+ const color = rgb(isAsleep?1:0, isAsleep?1:0, isStatic?1:0, .5);
96
+ this.drawFixtures(color);
74
97
  }
75
98
 
76
- box2dDrawFixtures(fillColor=WHITE, outlineColor, lineWidth=.1)
99
+ /** Draws all this object's fixtures
100
+ * @param {Color} [color]
101
+ * @param {Color} [outlineColor]
102
+ * @param {number} [lineWidth]
103
+ * @param {CanvasRenderingContext2D} [context] */
104
+ drawFixtures(color=WHITE, outlineColor, lineWidth=.1, context)
77
105
  {
78
106
  this.getFixtureList().forEach(fixture=>
79
- box2dDrawFixture(fixture, this.pos, this.angle, fillColor, outlineColor, lineWidth));
107
+ box2d.drawFixture(fixture, this.pos, this.angle, color, outlineColor, lineWidth, context));
80
108
  }
81
109
 
82
110
  ///////////////////////////////////////////////////////////////////////////////
83
111
  // physics contact callbacks
84
112
 
85
- beginContact(otherObject, contact) {}
86
- endContact(otherObject, contact) {}
113
+ /** Called when a contact begins
114
+ * @param {Box2dObject} otherObject */
115
+ beginContact(otherObject) {}
116
+
117
+ /** Called when a contact ends
118
+ * @param {Box2dObject} otherObject */
119
+ endContact(otherObject) {}
87
120
 
88
121
  ///////////////////////////////////////////////////////////////////////////////
89
122
  // physics fixtures and shapes
90
123
 
91
- addFixture(fixtureDef) { return this.body.CreateFixture(fixtureDef); }
92
- addShape(shape, density, friction, restitution, isSensor)
124
+ /** Add a shape fixture to the body
125
+ * @param {Object} shape
126
+ * @param {number} [density]
127
+ * @param {number} [friction]
128
+ * @param {number} [restitution]
129
+ * @param {boolean} [isSensor] */
130
+ addShape(shape, density=1, friction=.2, restitution=0, isSensor=false)
93
131
  {
94
- const fd = box2dCreateFixtureDef(shape, density, friction, restitution, isSensor);
95
- return this.addFixture(fd);
132
+ const fd = new box2d.instance.b2FixtureDef();
133
+ fd.set_shape(shape);
134
+ fd.set_density(density);
135
+ fd.set_friction(friction);
136
+ fd.set_restitution(restitution);
137
+ fd.set_isSensor(isSensor);
138
+ return this.body.CreateFixture(fd);
96
139
  }
97
140
 
141
+ /** Add a box shape to the body
142
+ * @param {Vector2} [size]
143
+ * @param {Vector2} [offset]
144
+ * @param {number} [angle]
145
+ * @param {number} [density]
146
+ * @param {number} [friction]
147
+ * @param {number} [restitution]
148
+ * @param {boolean} [isSensor] */
98
149
  addBox(size=vec2(1), offset=vec2(), angle=0, density, friction, restitution, isSensor)
99
150
  {
100
- const shape = new box2d.b2PolygonShape();
101
- shape.SetAsBox(size.x/2, size.y/2, offset.getBox2d(), angle);
151
+ const shape = new box2d.instance.b2PolygonShape();
152
+ shape.SetAsBox(size.x/2, size.y/2, box2d.vec2dTo(offset), angle);
102
153
  return this.addShape(shape, density, friction, restitution, isSensor);
103
154
  }
104
155
 
156
+ /** Add a polygon shape to the body
157
+ * @param {Array<Vector2>} points
158
+ * @param {number} [density]
159
+ * @param {number} [friction]
160
+ * @param {number} [restitution]
161
+ * @param {boolean} [isSensor] */
105
162
  addPoly(points, density, friction, restitution, isSensor)
106
163
  {
164
+ function box2dCreatePolygonShape(points)
165
+ {
166
+ function box2dCreatePointList(points)
167
+ {
168
+ const buffer = box2d.instance._malloc(points.length * 8);
169
+ for (let i=0, offset=0; i<points.length; ++i)
170
+ {
171
+ box2d.instance.HEAPF32[buffer + offset >> 2] = points[i].x;
172
+ offset += 4;
173
+ box2d.instance.HEAPF32[buffer + offset >> 2] = points[i].y;
174
+ offset += 4;
175
+ }
176
+ return box2d.instance.wrapPointer(buffer, box2d.instance.b2Vec2);
177
+ }
178
+
179
+ ASSERT(3 <= points.length && points.length <= 8);
180
+ const shape = new box2d.instance.b2PolygonShape();
181
+ const box2dPoints = box2dCreatePointList(points);
182
+ shape.Set(box2dPoints, points.length);
183
+ return shape;
184
+ }
185
+
107
186
  const shape = box2dCreatePolygonShape(points);
108
187
  return this.addShape(shape, density, friction, restitution, isSensor);
109
188
  }
110
189
 
190
+ /** Add a regular polygon shape to the body
191
+ * @param {number} [diameter]
192
+ * @param {number} [sides]
193
+ * @param {number} [density]
194
+ * @param {number} [friction]
195
+ * @param {number} [restitution]
196
+ * @param {boolean} [isSensor] */
111
197
  addRegularPoly(diameter=1, sides=8, density, friction, restitution, isSensor)
112
198
  {
113
199
  const points = [];
@@ -117,6 +203,12 @@ class Box2dObject extends EngineObject
117
203
  return this.addPoly(points, density, friction, restitution, isSensor);
118
204
  }
119
205
 
206
+ /** Add a random polygon shape to the body
207
+ * @param {number} [diameter]
208
+ * @param {number} [density]
209
+ * @param {number} [friction]
210
+ * @param {number} [restitution]
211
+ * @param {boolean} [isSensor] */
120
212
  addRandomPoly(diameter=1, density, friction, restitution, isSensor)
121
213
  {
122
214
  const sides = randInt(3, 9);
@@ -127,48 +219,74 @@ class Box2dObject extends EngineObject
127
219
  return this.addPoly(points, density, friction, restitution, isSensor);
128
220
  }
129
221
 
222
+ /** Add a circle shape to the body
223
+ * @param {number} [diameter]
224
+ * @param {Vector2} [offset]
225
+ * @param {number} [density]
226
+ * @param {number} [friction]
227
+ * @param {number} [restitution]
228
+ * @param {boolean} [isSensor] */
130
229
  addCircle(diameter=1, offset=vec2(), density, friction, restitution, isSensor)
131
230
  {
132
- const shape = new box2d.b2CircleShape();
133
- shape.set_m_p(offset.getBox2d());
231
+ const shape = new box2d.instance.b2CircleShape();
232
+ shape.set_m_p(box2d.vec2dTo(offset));
134
233
  shape.set_m_radius(diameter/2);
135
234
  return this.addShape(shape, density, friction, restitution, isSensor);
136
235
  }
137
236
 
237
+ /** Add an edge shape to the body
238
+ * @param {Vector2} point1
239
+ * @param {Vector2} point2
240
+ * @param {number} [density]
241
+ * @param {number} [friction]
242
+ * @param {number} [restitution]
243
+ * @param {boolean} [isSensor] */
138
244
  addEdge(point1, point2, density, friction, restitution, isSensor)
139
245
  {
140
- const shape = new box2d.b2EdgeShape();
141
- shape.Set(point1.getBox2d(), point2.getBox2d());
246
+ const shape = new box2d.instance.b2EdgeShape();
247
+ shape.Set(box2d.vec2dTo(point1), box2d.vec2dTo(point2));
142
248
  return this.addShape(shape, density, friction, restitution, isSensor);
143
249
  }
144
250
 
251
+ /** Add an edge loop to the body, an edge loop connects the end points
252
+ * @param {Array<Vector2>} points
253
+ * @param {number} [density]
254
+ * @param {number} [friction]
255
+ * @param {number} [restitution]
256
+ * @param {boolean} [isSensor] */
145
257
  addEdgeLoop(points, density, friction, restitution, isSensor)
146
258
  {
147
259
  const fixtures = [];
148
260
  const getPoint = i=> points[mod(i,points.length)];
149
261
  for (let i=0; i<points.length; ++i)
150
262
  {
151
- const shape = new box2d.b2EdgeShape();
152
- shape.set_m_vertex0(getPoint(i-1).getBox2d());
153
- shape.set_m_vertex1(getPoint(i+0).getBox2d());
154
- shape.set_m_vertex2(getPoint(i+1).getBox2d());
155
- shape.set_m_vertex3(getPoint(i+2).getBox2d());
263
+ const shape = new box2d.instance.b2EdgeShape();
264
+ shape.set_m_vertex0(box2d.vec2dTo(getPoint(i-1)));
265
+ shape.set_m_vertex1(box2d.vec2dTo(getPoint(i+0)));
266
+ shape.set_m_vertex2(box2d.vec2dTo(getPoint(i+1)));
267
+ shape.set_m_vertex3(box2d.vec2dTo(getPoint(i+2)));
156
268
  const f = this.addShape(shape, density, friction, restitution, isSensor);
157
269
  fixtures.push(f);
158
270
  }
159
271
  return fixtures;
160
272
  }
161
273
 
274
+ /** Add an edge list to the body
275
+ * @param {Array<Vector2>} points
276
+ * @param {number} [density]
277
+ * @param {number} [friction]
278
+ * @param {number} [restitution]
279
+ * @param {boolean} [isSensor] */
162
280
  addEdgeList(points, density, friction, restitution, isSensor)
163
281
  {
164
282
  const fixtures = [];
165
283
  for (let i=0; i<points.length-1; ++i)
166
284
  {
167
- const shape = new box2d.b2EdgeShape();
168
- points[i-1] && shape.set_m_vertex0(points[i-1].getBox2d());
169
- points[i+0] && shape.set_m_vertex1(points[i+0].getBox2d());
170
- points[i+1] && shape.set_m_vertex2(points[i+1].getBox2d());
171
- points[i+2] && shape.set_m_vertex3(points[i+2].getBox2d());
285
+ const shape = new box2d.instance.b2EdgeShape();
286
+ points[i-1] && shape.set_m_vertex0(box2d.vec2dTo(points[i-1]));
287
+ points[i+0] && shape.set_m_vertex1(box2d.vec2dTo(points[i+0]));
288
+ points[i+1] && shape.set_m_vertex2(box2d.vec2dTo(points[i+1]));
289
+ points[i+2] && shape.set_m_vertex3(box2d.vec2dTo(points[i+2]));
172
290
  const f = this.addShape(shape, density, friction, restitution, isSensor);
173
291
  fixtures.push(f);
174
292
  }
@@ -176,80 +294,130 @@ class Box2dObject extends EngineObject
176
294
  }
177
295
 
178
296
  ///////////////////////////////////////////////////////////////////////////////
179
- // lists of fixtures and joints
297
+ // physics get functions
180
298
 
181
- hasFixtures() { return !box2dIsNull(this.body.GetFixtureList()); }
182
- getFixtureList()
183
- {
184
- const fixtures = [];
185
- for (let fixture=this.body.GetFixtureList(); !box2dIsNull(fixture); )
186
- {
187
- fixtures.push(fixture);
188
- fixture = fixture.GetNext();
189
- }
190
- return fixtures;
191
- }
299
+ /** Gets the center of mass
300
+ * @return {Vector2} */
301
+ getCenterOfMass() { return box2d.vec2From(this.body.GetWorldCenter()); }
192
302
 
193
- hasJoints() { return !box2dIsNull(this.body.GetJointList()); }
194
- getJointList()
195
- {
196
- const joints = [];
197
- for (let joint=this.body.GetJointList(); !box2dIsNull(joint); )
198
- {
199
- joints.push(joint);
200
- joint = joint.get_next();
201
- }
202
- return joints;
203
- }
303
+ /** Gets the linear velocity
304
+ * @return {Vector2} */
305
+ getLinearVelocity() { return box2d.vec2From(this.body.GetLinearVelocity()); }
204
306
 
205
- ///////////////////////////////////////////////////////////////////////////////
206
- // physics get functions
207
-
208
- getCenterOfMass() { return vec2(this.body.GetWorldCenter()); }
209
- getLinearVelocity() { return vec2(this.body.GetLinearVelocity()); }
307
+ /** Gets the angular velocity
308
+ * @return {Vector2} */
210
309
  getAngularVelocity() { return this.body.GetAngularVelocity(); }
211
- getMass() { return this.body.GetMass(); }
212
- getInertia() { return this.body.GetInertia(); }
213
- getIsAwake() { return this.body.IsAwake(); }
214
- getBodyType() { return this.body.GetType(); }
215
- getIsStatic() { return this.getBodyType() == box2dBodyTypeStatic; }
216
- getIsKinematic() { return this.getBodyType() == box2dBodyTypeStatic; }
217
- getIsDynamic() { return this.getBodyType() == box2dBodyTypeDynamic; }
310
+
311
+ /** Gets the mass
312
+ * @return {number} */
313
+ getMass() { return this.body.GetMass(); }
314
+
315
+ /** Gets the rotational inertia
316
+ * @return {number} */
317
+ getInertia() { return this.body.GetInertia(); }
318
+
319
+ /** Check if this object is awake
320
+ * @return {boolean} */
321
+ getIsAwake() { return this.body.IsAwake(); }
322
+
323
+ /** Gets the physics body type
324
+ * @return {number} */
325
+ getBodyType() { return this.body.GetType(); }
218
326
 
219
327
  ///////////////////////////////////////////////////////////////////////////////
220
328
  // physics set functions
221
329
 
222
- setTransform(position, angle)
330
+ /** Sets the position and angle
331
+ * @param {Vector2} pos
332
+ * @param {number} angle */
333
+ setTransform(pos, angle)
223
334
  {
224
- this.pos = position;
335
+ this.pos = pos;
225
336
  this.angle = angle;
226
- this.body.SetTransform(position.getBox2d(), angle);
337
+ this.body.SetTransform(box2d.vec2dTo(pos), angle);
227
338
  }
228
- setPosition(position) { this.setTransform(position, this.body.GetAngle()); }
229
- setAngle(angle) { this.setTransform(vec2(this.body.GetPosition()), -angle); }
230
- setLinearVelocity(velocity) { this.body.SetLinearVelocity(velocity.getBox2d()); }
339
+
340
+ /** Sets the position
341
+ * @param {Vector2} pos */
342
+ setPosition(pos) { this.setTransform(pos, this.body.GetAngle()); }
343
+
344
+ /** Sets the angle
345
+ * @param {number} angle */
346
+ setAngle(angle) { this.setTransform(box2d.vec2From(this.body.GetPosition()), -angle); }
347
+
348
+ /** Sets the linear velocity
349
+ * @param {Vector2} velocity */
350
+ setLinearVelocity(velocity) { this.body.SetLinearVelocity(box2d.vec2dTo(velocity)); }
351
+
352
+ /** Sets the angular velocity
353
+ * @param {number} angularVelocity */
231
354
  setAngularVelocity(angularVelocity) { this.body.SetAngularVelocity(angularVelocity); }
232
- setLinearDamping(damping) { this.body.SetLinearDamping(damping); }
355
+
356
+ /** Sets the linear damping
357
+ * @param {number} damping */
358
+ setLinearDamping(damping) { this.body.SetLinearDamping(damping); }
359
+
360
+ /** Sets the angular damping
361
+ * @param {number} damping */
233
362
  setAngularDamping(damping) { this.body.SetAngularDamping(damping); }
234
- setGravityScale(scale=1) { this.body.SetGravityScale(this.gravityScale = scale); }
235
- setBullet(isBullet=true) { this.body.SetBullet(isBullet); }
236
- setAwake(isAwake=true) { this.body.SetAwake(isAwake); }
237
- setBodyType(type) { this.body.SetType(type); }
363
+
364
+ /** Sets the gravity scale
365
+ * @param {number} [scale] */
366
+ setGravityScale(scale=1) { this.body.SetGravityScale(this.gravityScale = scale); }
367
+
368
+ /** Should this body be treated like a bullet for continuous collision detection?
369
+ * @param {boolean} [isBullet] */
370
+ setBullet(isBullet=true) { this.body.SetBullet(isBullet); }
371
+
372
+ /** Set the sleep state of the body
373
+ * @param {boolean} [isAwake] */
374
+ setAwake(isAwake=true) { this.body.SetAwake(isAwake); }
375
+
376
+ /** Set the physics body type
377
+ * @param {number} type */
378
+ setBodyType(type) { this.body.SetType(type); }
379
+
380
+ /** Set whether the body is allowed to sleep
381
+ * @param {boolean} [isAllowed] */
238
382
  setSleepingAllowed(isAllowed=true) { this.body.SetSleepingAllowed(isAllowed); }
239
- setFixedRotation(isFixed=true) { this.body.SetFixedRotation(isFixed); }
240
- setCenterOfMass(center) { this.setMassData(center) }
241
- setMass(mass) { this.setMassData(undefined, mass) }
242
- setMomentOfInertia(I) { this.setMassData(undefined, undefined, I) }
243
- resetMassData() { this.body.ResetMassData(); }
383
+
384
+ /** Set whether the body can rotate
385
+ * @param {boolean} [isFixed] */
386
+ setFixedRotation(isFixed=true) { this.body.SetFixedRotation(isFixed); }
387
+
388
+ /** Set the center of mass of the body
389
+ * @param {Vector2} center */
390
+ setCenterOfMass(center) { this.setMassData(center) }
391
+
392
+ /** Set the mass of the body
393
+ * @param {number} mass */
394
+ setMass(mass) { this.setMassData(undefined, mass) }
395
+
396
+ /** Set the moment of inertia of the body
397
+ * @param {number} momentOfInertia */
398
+ setMomentOfInertia(momentOfInertia) { this.setMassData(undefined, undefined, momentOfInertia) }
399
+
400
+ /** Reset the mass, center of mass, and moment */
401
+ resetMassData() { this.body.ResetMassData(); }
402
+
403
+ /** Set the mass data of the body
404
+ * @param {Vector2} [localCenter]
405
+ * @param {number} [mass]
406
+ * @param {number} [momentOfInertia] */
244
407
  setMassData(localCenter, mass, momentOfInertia)
245
408
  {
246
- const data = new box2d.b2MassData();
409
+ const data = new box2d.instance.b2MassData();
247
410
  this.body.GetMassData(data);
248
- localCenter && data.set_center(localCenter.getBox2d());
411
+ localCenter && data.set_center(box2d.vec2dTo(localCenter));
249
412
  mass && data.set_mass(mass);
250
413
  momentOfInertia && data.set_I(momentOfInertia);
251
414
  this.body.SetMassData(data);
252
415
  }
416
+
417
+ /** Set the collision filter data for this body
418
+ * @param {number} [categoryBits]
419
+ * @param {number} [ignoreCategoryBits]
420
+ * @param {number} [groupIndex] */
253
421
  setFilterData(categoryBits=0, ignoreCategoryBits=0, groupIndex=0)
254
422
  {
255
423
  this.getFixtureList().forEach(fixture=>
@@ -260,694 +428,1442 @@ class Box2dObject extends EngineObject
260
428
  filter.set_groupIndex(groupIndex);
261
429
  });
262
430
  }
431
+
432
+ /** Set if this body is a sensor
433
+ * @param {boolean} [isSensor] */
263
434
  setSensor(isSensor=true)
264
435
  { this.getFixtureList().forEach(f=>f.SetSensor(isSensor)); }
265
436
 
266
437
  ///////////////////////////////////////////////////////////////////////////////
267
438
  // physics force and torque functions
268
439
 
440
+ /** Apply force to this object
441
+ * @param {Vector2} force
442
+ * @param {Vector2} [pos] */
269
443
  applyForce(force, pos)
270
444
  {
271
445
  pos ||= this.getCenterOfMass();
272
446
  this.setAwake();
273
- this.body.ApplyForce(force.getBox2d(), pos.getBox2d());
447
+ this.body.ApplyForce(box2d.vec2dTo(force), box2d.vec2dTo(pos));
274
448
  }
449
+
450
+ /** Apply acceleration to this object
451
+ * @param {Vector2} acceleration
452
+ * @param {Vector2} [pos] */
275
453
  applyAcceleration(acceleration, pos)
276
454
  {
277
455
  pos ||= this.getCenterOfMass();
278
456
  this.setAwake();
279
- this.body.ApplyLinearImpulse(acceleration.getBox2d(), pos.getBox2d());
457
+ this.body.ApplyLinearImpulse(box2d.vec2dTo(acceleration), box2d.vec2dTo(pos));
280
458
  }
459
+
460
+ /** Apply torque to this object
461
+ * @param {number} torque */
281
462
  applyTorque(torque)
282
463
  {
283
464
  this.setAwake();
284
465
  this.body.ApplyTorque(torque);
285
466
  }
467
+
468
+ /** Apply angular acceleration to this object
469
+ * @param {number} acceleration */
286
470
  applyAngularAcceleration(acceleration)
287
471
  {
288
472
  this.setAwake();
289
- this.ApplyAngularImpulse(acceleration);
473
+ this.body.ApplyAngularImpulse(acceleration);
474
+ }
475
+
476
+ ///////////////////////////////////////////////////////////////////////////////
477
+ // lists of fixtures and joints
478
+
479
+ /** Check if this object has any fixtures
480
+ * @return {boolean} */
481
+ hasFixtures() { return !box2d.isNull(this.body.GetFixtureList()); }
482
+
483
+ /** Get list of fixtures for this object
484
+ * @return {Array<Object>} */
485
+ getFixtureList()
486
+ {
487
+ const fixtures = [];
488
+ for (let fixture=this.body.GetFixtureList(); !box2d.isNull(fixture); )
489
+ {
490
+ fixtures.push(fixture);
491
+ fixture = fixture.GetNext();
492
+ }
493
+ return fixtures;
494
+ }
495
+
496
+ /** Check if this object has any joints
497
+ * @return {boolean} */
498
+ hasJoints() { return !box2d.isNull(this.body.GetJointList()); }
499
+
500
+ /** Get list of joints for this object
501
+ * @return {Array<Object>} */
502
+ getJointList()
503
+ {
504
+ const joints = [];
505
+ for (let joint=this.body.GetJointList(); !box2d.isNull(joint); )
506
+ {
507
+ joints.push(joint);
508
+ joint = joint.get_next();
509
+ }
510
+ return joints;
290
511
  }
291
512
  }
292
513
 
293
514
  ///////////////////////////////////////////////////////////////////////////////
294
- // Box2D Raycasting and Querying
295
-
296
- // result info for raycasts
515
+ /**
516
+ * Box2D Raycast Result
517
+ * - Holds results from a box2d raycast queries
518
+ * - Automatically created by box2d raycast functions
519
+ */
297
520
  class Box2dRaycastResult
298
521
  {
522
+ /** Create a raycast result
523
+ * @param {Object} fixture
524
+ * @param {Vector2} point
525
+ * @param {Vector2} normal
526
+ * @param {number} fraction */
299
527
  constructor(fixture, point, normal, fraction)
300
528
  {
529
+ /** @property {Box2dObject} - The box2d object */
530
+ this.object = fixture.GetBody().object;
531
+ /** @property {Object} - The fixture that was hit */
301
532
  this.fixture = fixture;
533
+ /** @property {Vector2} - The hit point */
302
534
  this.point = point;
535
+ /** @property {Vector2} - The hit normal */
303
536
  this.normal = normal;
537
+ /** @property {number} - Distance fraction at the point of intersection */
304
538
  this.fraction = fraction;
305
- this.object = fixture.GetBody().object;
306
539
  }
307
540
  }
308
541
 
309
- // raycast and return a list of all the results
310
- function box2dRaycastAll(start, end)
542
+ ///////////////////////////////////////////////////////////////////////////////
543
+ /**
544
+ * Box2D Joint
545
+ * - Base class for Box2D joints
546
+ * - A joint is used to connect objects together
547
+ */
548
+ class Box2dJoint
311
549
  {
312
- const raycastCallback = new box2d.JSRayCastCallback();
313
- raycastCallback.ReportFixture = function(fixturePointer, point, normal, fraction)
314
- {
315
- const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
316
- point = vec2().setBox2dPointer(point);
317
- normal = vec2().setBox2dPointer(normal);
318
- raycastResults.push(new Box2dRaycastResult(fixture, point, normal, fraction));
319
- return 1; // continue getting results
320
- };
321
-
322
- const raycastResults = [];
323
- box2dWorld.RayCast(raycastCallback, start.getBox2d(), end.getBox2d());
324
- debugRaycast && debugLine(start, end, raycastResults.length ? '#f00' : '#00f', .02);
325
- return raycastResults;
326
- }
550
+ /** Create a box2d joint, the base class is not intended to be used directly
551
+ * @param {Object} jointDef */
552
+ constructor(jointDef)
553
+ {
554
+ this.box2dJoint = box2d.castObjectType(box2d.world.CreateJoint(jointDef));
555
+ }
327
556
 
328
- // raycast and return the first result
329
- function box2dRaycast(start, end)
330
- {
331
- const raycastResults = box2dRaycastAll(start, end);
332
- if (!raycastResults.length)
333
- return undefined;
334
- return raycastResults.reduce((a,b)=>a.fraction < b.fraction ? a : b);
335
- }
557
+ /** Destroy this joint */
558
+ destroy() { box2d.world.DestroyJoint(this.box2dJoint); this.box2dJoint = 0; }
336
559
 
337
- // box aabb cast and return all the objects
338
- function box2dBoxCastAll(pos, size)
339
- {
340
- const queryCallback = new box2d.JSQueryCallback();
341
- queryCallback.ReportFixture = function(fixturePointer)
342
- {
343
- const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
344
- const o = fixture.GetBody().object;
345
- if (!queryObjects.includes(o))
346
- queryObjects.push(o); // add if not already in list
347
- return true; // continue getting results
348
- };
349
-
350
- const aabb = new box2d.b2AABB();
351
- aabb.set_lowerBound(pos.subtract(size.scale(.5)).getBox2d());
352
- aabb.set_upperBound(pos.add(size.scale(.5)).getBox2d());
353
-
354
- let queryObjects = [];
355
- box2dWorld.QueryAABB(queryCallback, aabb);
356
- debugRaycast && debugRect(pos, size, queryObjects.length ? '#f00' : '#00f', .02);
357
- return queryObjects;
358
- }
560
+ /** Get the first object attached to this joint
561
+ * @return {Box2dObject} */
562
+ getObjectA() { return this.box2dJoint.GetBodyA().object; }
563
+
564
+ /** Get the second object attached to this joint
565
+ * @return {Box2dObject} */
566
+ getObjectB() { return this.box2dJoint.GetBodyB().object; }
567
+
568
+ /** Get the first anchor for this joint in world coordinates
569
+ * @return {Vector2} */
570
+ getAnchorA() { return box2d.vec2From(this.box2dJoint.GetAnchorA());}
359
571
 
360
- // box aabb cast and return the first object
361
- function box2dBoxCast(pos, size)
362
- {
363
- const queryCallback = new box2d.JSQueryCallback();
364
- queryCallback.ReportFixture = function(fixturePointer)
365
- {
366
- const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
367
- queryObject = fixture.GetBody().object;
368
- return false; // stop getting results
369
- };
370
-
371
- const aabb = new box2d.b2AABB();
372
- aabb.set_lowerBound(pos.subtract(size.scale(.5)).getBox2d());
373
- aabb.set_upperBound(pos.add(size.scale(.5)).getBox2d());
374
-
375
- let queryObject;
376
- box2dWorld.QueryAABB(queryCallback, aabb);
377
- debugRaycast && debugRect(pos, size, queryObject ? '#f00' : '#00f', .02);
378
- return queryObject;
379
- }
572
+ /** Get the second anchor for this joint in world coordinates
573
+ * @return {Vector2} */
574
+ getAnchorB() { return box2d.vec2From(this.box2dJoint.GetAnchorB());}
575
+
576
+ /** Get the reaction force on bodyB at the joint anchor given a time step
577
+ * @param {number} time
578
+ * @return {Vector2} */
579
+ getReactionForce(time) { return box2d.vec2From(this.box2dJoint.GetReactionForce(1/time));}
580
+
581
+ /** Get the reaction torque on bodyB in N*m given a time step
582
+ * @param {number} time
583
+ * @return {number} */
584
+ getReactionTorque(time) { return this.box2dJoint.GetReactionTorque(1/time);}
585
+
586
+ /** Check if the connected bodies should collide
587
+ * @return {boolean} */
588
+ getCollideConnected() { return this.box2dJoint.getCollideConnected();}
380
589
 
381
- // circle cast and return all the objects
382
- function box2dCircleCastAll(pos, diameter)
383
- {
384
- const radius2 = (diameter/2)**2;
385
- const results = box2dBoxCastAll(pos, vec2(diameter));
386
- return results.filter(o=>o.pos.distanceSquared(pos) < radius2);
590
+ /** Check if either connected body is active
591
+ * @return {boolean} */
592
+ isActive() { return this.box2dJoint.IsActive();}
387
593
  }
388
594
 
389
- // circle cast and return the first object
390
- function box2dCircleCast(pos, diameter)
595
+ ///////////////////////////////////////////////////////////////////////////////
596
+ /**
597
+ * Box2D Target Joint, also known as a mouse joint
598
+ * - Used to make a point on a object track a specific world point target
599
+ * - This a soft constraint with a max force
600
+ * - This allows the constraint to stretch and without applying huge forces
601
+ * @extends Box2dJoint
602
+ */
603
+ class Box2dTargetJoint extends Box2dJoint
391
604
  {
392
- const radius2 = (diameter/2)**2;
393
- let results = box2dBoxCastAll(pos, vec2(diameter));
394
-
395
- let bestResult, bestDistance2;
396
- for (const result of results)
605
+ /** Create a target joint
606
+ * @param {Box2dObject} object
607
+ * @param {Box2dObject} fixedObject
608
+ * @param {Vector2} worldPos */
609
+ constructor(object, fixedObject, worldPos)
397
610
  {
398
- const distance2 = result.pos.distanceSquared(pos);
399
- if (distance2 < radius2 && (!bestResult || distance2 < bestDistance2))
400
- {
401
- bestResult = result;
402
- bestDistance2 = distance2;
403
- }
611
+ object.setAwake();
612
+ const jointDef = new box2d.instance.b2MouseJointDef();
613
+ jointDef.set_bodyA(fixedObject.body);
614
+ jointDef.set_bodyB(object.body);
615
+ jointDef.set_target(box2d.vec2dTo(worldPos));
616
+ jointDef.set_maxForce(2e3 * object.getMass());
617
+ super(jointDef);
404
618
  }
405
- return bestResult;
619
+
620
+ /** Set the target point in world coordinates
621
+ * @param {Vector2} pos */
622
+ setTarget(pos) { this.box2dJoint.SetTarget(box2d.vec2dTo(pos)); }
623
+
624
+ /** Get the target point in world coordinates
625
+ * @return {Vector2} */
626
+ getTarget(){ return box2d.vec2From(this.box2dJoint.GetTarget()); }
627
+
628
+ /** Sets the maximum force in Newtons
629
+ * @param {number} force */
630
+ setMaxForce(force) { this.box2dJoint.SetMaxForce(force); }
631
+
632
+ /** Gets the maximum force in Newtons
633
+ * @return {number} */
634
+ getMaxForce() { return this.box2dJoint.GetMaxForce(); }
635
+
636
+ /** Sets the joint frequency in Hertz
637
+ * @param {number} hz */
638
+ setFrequency(hz) { this.box2dJoint.SetFrequency(hz); }
639
+
640
+ /** Gets the joint frequency in Hertz
641
+ * @return {number} */
642
+ getFrequency() { return this.box2dJoint.GetFrequency(); }
406
643
  }
407
644
 
408
- // point cast and return the first object
409
- function box2dPointCast(pos, dynamicOnly=true)
645
+ ///////////////////////////////////////////////////////////////////////////////
646
+ /**
647
+ * Box2D Distance Joint
648
+ * - Constrains two points on two objects to remain at a fixed distance
649
+ * - You can view this as a massless, rigid rod
650
+ * @extends Box2dJoint
651
+ */
652
+ class Box2dDistanceJoint extends Box2dJoint
410
653
  {
411
- const queryCallback = new box2d.JSQueryCallback();
412
- queryCallback.ReportFixture = function(fixturePointer)
654
+ /** Create a distance joint
655
+ * @param {Box2dObject} objectA
656
+ * @param {Box2dObject} objectB
657
+ * @param {Vector2} anchorA
658
+ * @param {Vector2} anchorB
659
+ * @param {boolean} [collide] */
660
+ constructor(objectA, objectB, anchorA, anchorB, collide=false)
413
661
  {
414
- const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
415
- if (dynamicOnly && fixture.GetBody().GetType() != box2d.b2_dynamicBody)
416
- return true; // continue getting results
417
- if (!fixture.TestPoint(pos.getBox2d()))
418
- return true; // continue getting results
419
- queryObject = fixture.GetBody().object;
420
- return false; // stop getting results
421
- };
422
-
423
- const aabb = new box2d.b2AABB();
424
- aabb.set_lowerBound(pos.getBox2d());
425
- aabb.set_upperBound(pos.getBox2d());
426
-
427
- let queryObject;
428
- debugRaycast && debugRect(pos, vec2(), queryObject ? '#f00' : '#00f', .02);
429
- box2dWorld.QueryAABB(queryCallback, aabb);
430
- return queryObject;
431
- }
662
+ anchorA ||= box2d.vec2From(objectA.body.GetPosition());
663
+ anchorB ||= box2d.vec2From(objectB.body.GetPosition());
664
+ const localAnchorA = objectA.worldToLocal(anchorA);
665
+ const localAnchorB = objectB.worldToLocal(anchorB);
666
+ const jointDef = new box2d.instance.b2DistanceJointDef();
667
+ jointDef.set_bodyA(objectA.body);
668
+ jointDef.set_bodyB(objectB.body);
669
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
670
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
671
+ jointDef.set_length(anchorA.distance(anchorB));
672
+ jointDef.set_collideConnected(collide);
673
+ super(jointDef);
674
+ }
432
675
 
433
- // box aabb cast and return all the fixtures
434
- function box2dBoxCastAllFixtures(pos, size)
435
- {
436
- const queryCallback = new box2d.JSQueryCallback();
437
- queryCallback.ReportFixture = function(fixturePointer)
438
- {
439
- const fixture = box2d.wrapPointer(fixturePointer, box2d.b2Fixture);
440
- if (!queryFixtures.includes(fixture))
441
- queryFixtures.push(fixture); // add if not already in list
442
- return true; // continue getting results
443
- };
444
-
445
- const aabb = new box2d.b2AABB();
446
- aabb.set_lowerBound(pos.subtract(size.scale(.5)).getBox2d());
447
- aabb.set_upperBound(pos.add(size.scale(.5)).getBox2d());
448
-
449
- let queryFixtures = [];
450
- box2dWorld.QueryAABB(queryCallback, aabb);
451
- debugRaycast && debugRect(pos, size, queryFixtures.length ? '#f00' : '#00f', .02);
452
- return queryFixtures;
676
+ /** Get the local anchor point relative to objectA's origin
677
+ * @return {Vector2} */
678
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
679
+
680
+ /** Get the local anchor point relative to objectB's origin
681
+ * @return {Vector2} */
682
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
683
+
684
+ /** Set the length of the joint
685
+ * @param {number} length */
686
+ setLength(length) { this.box2dJoint.SetLength(length); }
687
+
688
+ /** Get the length of the joint
689
+ * @return {number} */
690
+ getLength() { return this.box2dJoint.GetLength(); }
691
+
692
+ /** Set the frequency in Hertz
693
+ * @param {number} hz */
694
+ setFrequency(hz) { this.box2dJoint.SetFrequency(hz); }
695
+
696
+ /** Get the frequency in Hertz
697
+ * @return {number} */
698
+ getFrequency() { return this.box2dJoint.GetFrequency(); }
699
+
700
+ /** Set the damping ratio
701
+ * @param {number} ratio */
702
+ setDampingRatio(ratio) { this.box2dJoint.SetDampingRatio(ratio); }
703
+
704
+ /** Get the damping ratio
705
+ * @return {number} */
706
+ getDampingRatio() { return this.box2dJoint.GetDampingRatio(); }
453
707
  }
454
708
 
455
709
  ///////////////////////////////////////////////////////////////////////////////
456
- // Box2D Joints
457
-
458
- function box2dCreateMouseJoint(object, fixedObject, worldPos)
710
+ /**
711
+ * Box2D Pin Joint
712
+ * - Pins two objects together at a point
713
+ * @extends Box2dDistanceJoint
714
+ */
715
+ class Box2dPinJoint extends Box2dDistanceJoint
459
716
  {
460
- object.setAwake();
461
- const jointDef = new box2d.b2MouseJointDef();
462
- jointDef.set_bodyA(fixedObject.body);
463
- jointDef.set_bodyB(object.body);
464
- jointDef.set_target(worldPos.getBox2d());
465
- jointDef.set_maxForce(2e3 * object.getMass());
466
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
717
+ /** Create a pin joint
718
+ * @param {Box2dObject} objectA
719
+ * @param {Box2dObject} objectB
720
+ * @param {Vector2} [pos]
721
+ * @param {boolean} [collide] */
722
+ constructor(objectA, objectB, pos=objectA.pos, collide=false)
723
+ {
724
+ super(objectA, objectB, undefined, pos, collide);
725
+ }
467
726
  }
468
727
 
469
- function box2dCreatePinJoint(objectA, objectB, collide=false)
728
+ ///////////////////////////////////////////////////////////////////////////////
729
+ /**
730
+ * Box2D Rope Joint
731
+ * - Enforces a maximum distance between two points on two objects
732
+ * @extends Box2dJoint
733
+ */
734
+ class Box2dRopeJoint extends Box2dJoint
470
735
  {
471
- return box2dCreateDistanceJoint(objectA, objectB, objectB.pos, undefined, collide);
472
- }
736
+ /** Create a rope joint
737
+ * @param {Box2dObject} objectA
738
+ * @param {Box2dObject} objectB
739
+ * @param {Vector2} anchorA
740
+ * @param {Vector2} anchorB
741
+ * @param {number} extraLength
742
+ * @param {boolean} [collide] */
743
+ constructor(objectA, objectB, anchorA, anchorB, extraLength=0, collide=false)
744
+ {
745
+ anchorA ||= box2d.vec2From(objectA.body.GetPosition());
746
+ anchorB ||= box2d.vec2From(objectB.body.GetPosition());
747
+ const localAnchorA = objectA.worldToLocal(anchorA);
748
+ const localAnchorB = objectB.worldToLocal(anchorB);
749
+ const jointDef = new box2d.instance.b2RopeJointDef();
750
+ jointDef.set_bodyA(objectA.body);
751
+ jointDef.set_bodyB(objectB.body);
752
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
753
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
754
+ jointDef.set_maxLength(anchorA.distance(anchorB)+extraLength);
755
+ jointDef.set_collideConnected(collide);
756
+ super(jointDef);
757
+ }
473
758
 
474
- function box2dCreateDistanceJoint(objectA, objectB, anchorA, anchorB, collide=false)
475
- {
476
- anchorA ||= vec2(objectA.body.GetPosition());
477
- anchorB ||= vec2(objectB.body.GetPosition());
478
- const localAnchorA = objectA.worldToLocal(anchorA);
479
- const localAnchorB = objectB.worldToLocal(anchorB);
480
- const jointDef = new box2d.b2DistanceJointDef();
481
- jointDef.set_bodyA(objectA.body);
482
- jointDef.set_bodyB(objectB.body);
483
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
484
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
485
- jointDef.set_length(anchorA.distance(anchorB));
486
- jointDef.set_collideConnected(collide);
487
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
488
- }
759
+ /** Get the local anchor point relative to objectA's origin
760
+ * @return {Vector2} */
761
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
489
762
 
490
- function box2dCreateRevoluteJoint(objectA, objectB, anchor, collide=false)
491
- {
492
- anchor ||= vec2(objectB.body.GetPosition());
493
- const localAnchorA = objectA.worldToLocal(anchor);
494
- const localAnchorB = objectB.worldToLocal(anchor);
495
- const jointDef = new box2d.b2RevoluteJointDef();
496
- jointDef.set_bodyA(objectA.body);
497
- jointDef.set_bodyB(objectB.body);
498
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
499
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
500
- jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
501
- jointDef.set_collideConnected(collide);
502
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
503
- }
763
+ /** Get the local anchor point relative to objectB's origin
764
+ * @return {Vector2} */
765
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
766
+
767
+ /** Set the max length of the joint
768
+ * @param {number} length */
769
+ setMaxLength(length) { this.box2dJoint.SetMaxLength(length); }
504
770
 
505
- function box2dCreatePrismaticJoint(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
506
- {
507
- anchor ||= vec2(objectB.body.GetPosition());
508
- const localAnchorA = objectA.worldToLocal(anchor);
509
- const localAnchorB = objectB.worldToLocal(anchor);
510
- const localAxisA = objectB.worldToLocalVector(worldAxis);
511
- const jointDef = new box2d.b2PrismaticJointDef();
512
- jointDef.set_bodyA(objectA.body);
513
- jointDef.set_bodyB(objectB.body);
514
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
515
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
516
- jointDef.set_localAxisA(localAxisA.getBox2d());
517
- jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
518
- jointDef.set_collideConnected(collide);
519
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
771
+ /** Get the max length of the joint
772
+ * @return {number} */
773
+ getMaxLength() { return this.box2dJoint.GetMaxLength(); }
520
774
  }
521
775
 
522
- function box2dCreateWheelJoint(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
776
+ ///////////////////////////////////////////////////////////////////////////////
777
+ /**
778
+ * Box2D Revolute Joint
779
+ * - Constrains two objects to share a point while they are free to rotate around the point
780
+ * - The relative rotation about the shared point is the joint angle
781
+ * - You can limit the relative rotation with a joint limit
782
+ * - You can use a motor to drive the relative rotation about the shared point
783
+ * - A maximum motor torque is provided so that infinite forces are not generated
784
+ * @extends Box2dJoint
785
+ */
786
+ class Box2dRevoluteJoint extends Box2dJoint
523
787
  {
524
- anchor ||= vec2(objectB.body.GetPosition());
525
- const localAnchorA = objectA.worldToLocal(anchor);
526
- const localAnchorB = objectB.worldToLocal(anchor);
527
- const localAxisA = objectB.worldToLocalVector(worldAxis);
528
- const jointDef = new box2d.b2WheelJointDef();
529
- jointDef.set_bodyA(objectA.body);
530
- jointDef.set_bodyB(objectB.body);
531
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
532
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
533
- jointDef.set_localAxisA(localAxisA.getBox2d());
534
- jointDef.set_collideConnected(collide);
535
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
536
- }
788
+ /** Create a revolute joint
789
+ * @param {Box2dObject} objectA
790
+ * @param {Box2dObject} objectB
791
+ * @param {Vector2} anchor
792
+ * @param {boolean} [collide] */
793
+ constructor(objectA, objectB, anchor, collide=false)
794
+ {
795
+ anchor ||= box2d.vec2From(objectB.body.GetPosition());
796
+ const localAnchorA = objectA.worldToLocal(anchor);
797
+ const localAnchorB = objectB.worldToLocal(anchor);
798
+ const jointDef = new box2d.instance.b2RevoluteJointDef();
799
+ jointDef.set_bodyA(objectA.body);
800
+ jointDef.set_bodyB(objectB.body);
801
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
802
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
803
+ jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
804
+ jointDef.set_collideConnected(collide);
805
+ super(jointDef);
806
+ }
537
807
 
538
- function box2dCreateWeldJoint(objectA, objectB, anchor, collide=false)
539
- {
540
- anchor ||= vec2(objectB.body.GetPosition());
541
- const localAnchorA = objectA.worldToLocal(anchor);
542
- const localAnchorB = objectB.worldToLocal(anchor);
543
- const jointDef = new box2d.b2WeldJointDef();
544
- jointDef.set_bodyA(objectA.body);
545
- jointDef.set_bodyB(objectB.body);
546
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
547
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
548
- jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
549
- jointDef.set_collideConnected(collide);
550
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
551
- }
808
+ /** Get the local anchor point relative to objectA's origin
809
+ * @return {Vector2} */
810
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
552
811
 
553
- function box2dCreateFrictionJoint(objectA, objectB, anchor, collide=false)
554
- {
555
- anchor ||= vec2(objectB.body.GetPosition());
556
- const localAnchorA = objectA.worldToLocal(anchor);
557
- const localAnchorB = objectB.worldToLocal(anchor);
558
- const jointDef = new box2d.b2FrictionJointDef();
559
- jointDef.set_bodyA(objectA.body);
560
- jointDef.set_bodyB(objectB.body);
561
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
562
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
563
- jointDef.set_collideConnected(collide);
564
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
565
- }
812
+ /** Get the local anchor point relative to objectB's origin
813
+ * @return {Vector2} */
814
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
566
815
 
567
- function box2dCreateRopeJoint(objectA, objectB, anchorA, anchorB, extraLength=0, collide=false)
568
- {
569
- anchorA ||= vec2(objectA.body.GetPosition());
570
- anchorB ||= vec2(objectB.body.GetPosition());
571
- const localAnchorA = objectA.worldToLocal(anchorA);
572
- const localAnchorB = objectB.worldToLocal(anchorB);
573
- const jointDef = new box2d.b2RopeJointDef();
574
- jointDef.set_bodyA(objectA.body);
575
- jointDef.set_bodyB(objectB.body);
576
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
577
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
578
- jointDef.set_maxLength(anchorA.distance(anchorB)+extraLength);
579
- jointDef.set_collideConnected(collide);
580
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
581
- }
816
+ /** Get the reference angle, objectB angle minus objectA angle in the reference state
817
+ * @return {number} */
818
+ getReferenceAngle() { return this.box2dJoint.GetReferenceAngle(); }
582
819
 
583
- function box2dCreatePulleyJoint(objectA, objectB, groundAnchorA, groundAnchorB, anchorA, anchorB, ratio=1, collide=false)
584
- {
585
- anchorA ||= vec2(objectA.body.GetPosition());
586
- anchorB ||= vec2(objectB.body.GetPosition());
587
- const localAnchorA = objectA.worldToLocal(anchorA);
588
- const localAnchorB = objectB.worldToLocal(anchorB);
589
- const jointDef = new box2d.b2PulleyJointDef();
590
- jointDef.set_bodyA(objectA.body);
591
- jointDef.set_bodyB(objectB.body);
592
- jointDef.set_groundAnchorA(groundAnchorA.getBox2d());
593
- jointDef.set_groundAnchorB(groundAnchorB.getBox2d());
594
- jointDef.set_localAnchorA(localAnchorA.getBox2d());
595
- jointDef.set_localAnchorB(localAnchorB.getBox2d());
596
- jointDef.set_ratio(ratio);
597
- jointDef.set_lengthA(groundAnchorA.distance(anchorA));
598
- jointDef.set_lengthB(groundAnchorB.distance(anchorB));
599
- jointDef.set_collideConnected(collide);
600
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
601
- }
820
+ /** Get the current joint angle
821
+ * @return {number} */
822
+ getJointAngle() { return this.box2dJoint.GetJointAngle(); }
602
823
 
603
- function box2dCreateMotorJoint(objectA, objectB)
604
- {
605
- const linearOffset = objectA.worldToLocal(vec2(objectB.body.GetPosition()));
606
- const angularOffset = objectB.body.GetAngle() - objectA.body.GetAngle();
607
- const jointDef = new box2d.b2MotorJointDef();
608
- jointDef.set_bodyA(objectA.body);
609
- jointDef.set_bodyB(objectB.body);
610
- jointDef.set_linearOffset(linearOffset.getBox2d());
611
- jointDef.set_angularOffset(angularOffset);
612
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
613
- }
824
+ /** Get the current joint angle speed in radians per second
825
+ * @return {number} */
826
+ getJointSpeed() { return this.box2dJoint.GetJointSpeed(); }
614
827
 
615
- function box2dCreateGearJoint(objectA, objectB, joint1, joint2, ratio=1)
616
- {
617
- const jointDef = new box2d.b2GearJointDef();
618
- jointDef.set_bodyA(objectA.body);
619
- jointDef.set_bodyB(objectB.body);
620
- jointDef.set_joint1(joint1);
621
- jointDef.set_joint2(joint2);
622
- jointDef.set_ratio(ratio);
623
- return box2dCastObject(box2dWorld.CreateJoint(jointDef));
624
- }
828
+ /** Is the joint limit enabled?
829
+ * @return {boolean} */
830
+ isLimitEnabled() { return this.box2dJoint.IsLimitEnabled(); }
625
831
 
626
- function box2dDestroyJoint(joint) { box2dWorld.DestroyJoint(joint); }
832
+ /** Enable/disable the joint limit
833
+ * @param {boolean} [enable] */
834
+ enableLimit(enable=true) { return this.box2dJoint.enableLimit(enable); }
627
835
 
628
- ///////////////////////////////////////////////////////////////////////////////
629
- // Box2D Helper Functions
836
+ /** Get the lower joint limit
837
+ * @return {number} */
838
+ getLowerLimit() { return this.box2dJoint.GetLowerLimit(); }
630
839
 
631
- function box2dIsNull(object) { return !box2d.getPointer(object); }
840
+ /** Get the upper joint limit
841
+ * @return {number} */
842
+ getUpperLimit() { return this.box2dJoint.GetUpperLimit(); }
632
843
 
633
- function box2dCreateFixtureDef(shape, density=1, friction=.2, restitution=0, isSensor=false)
634
- {
635
- const fd = new box2d.b2FixtureDef();
636
- fd.set_shape(shape);
637
- fd.set_density(density);
638
- fd.set_friction(friction);
639
- fd.set_restitution(restitution);
640
- fd.set_isSensor(isSensor);
641
- return fd;
642
- }
844
+ /** Set the joint limits
845
+ * @param {number} min
846
+ * @param {number} max */
847
+ setLimits(min, max) { return this.box2dJoint.SetLimits(min, max); }
643
848
 
644
- function box2dCreatePointList(points)
645
- {
646
- const buffer = box2d._malloc(points.length * 8);
647
- for (let i=0, offset=0; i<points.length; ++i)
648
- {
649
- box2d.HEAPF32[buffer + offset >> 2] = points[i].x;
650
- offset += 4;
651
- box2d.HEAPF32[buffer + offset >> 2] = points[i].y;
652
- offset += 4;
653
- }
654
- return box2d.wrapPointer(buffer, box2d.b2Vec2);
655
- }
849
+ /** Is the joint motor enabled?
850
+ * @return {boolean} */
851
+ isMotorEnabled() { return this.box2dJoint.IsMotorEnabled(); }
656
852
 
657
- function box2dCreatePolygonShape(points)
658
- {
659
- ASSERT(3 <= points.length && points.length <= 8);
660
- const shape = new box2d.b2PolygonShape();
661
- const box2dPoints = box2dCreatePointList(points);
662
- shape.Set(box2dPoints, points.length);
663
- return shape;
853
+ /** Enable/disable the joint motor
854
+ * @param {boolean} [enable] */
855
+ enableMotor(enable=true) { return this.box2dJoint.EnableMotor(enable); }
856
+
857
+ /** Set the motor speed
858
+ * @param {number} speed */
859
+ setMotorSpeed(speed) { return this.box2dJoint.SetMotorSpeed(speed); }
860
+
861
+ /** Get the motor speed
862
+ * @return {number} */
863
+ getMotorSpeed() { return this.box2dJoint.GetMotorSpeed(); }
864
+
865
+ /** Set the motor torque
866
+ * @param {number} torque */
867
+ setMaxMotorTorque(torque) { return this.box2dJoint.SetMaxMotorTorque(torque); }
868
+
869
+ /** Get the max motor torque
870
+ * @return {number} */
871
+ getMaxMotorTorque() { return this.box2dJoint.GetMaxMotorTorque(); }
872
+
873
+ /** Get the motor torque given a time step
874
+ * @param {number} time
875
+ * @return {number} */
876
+ getMotorTorque(time) { return this.box2dJoint.GetMotorTorque(1/time); }
664
877
  }
665
878
 
666
- function box2dCastObject(object)
879
+ ///////////////////////////////////////////////////////////////////////////////
880
+ /**
881
+ * Box2D Gear Joint
882
+ * - A gear joint is used to connect two joints together
883
+ * - Either joint can be a revolute or prismatic joint
884
+ * - You specify a gear ratio to bind the motions together
885
+ * @extends Box2dJoint
886
+ */
887
+ class Box2dGearJoint extends Box2dJoint
667
888
  {
668
- if (object instanceof box2d.b2Shape)
669
- {
670
- switch (object.GetType())
671
- {
672
- case box2d.b2Shape.e_circle:
673
- return box2d.castObject(object, box2d.b2CircleShape);
674
- case box2d.b2Shape.e_edge:
675
- return box2d.castObject(object, box2d.b2EdgeShape);
676
- case box2d.b2Shape.e_polygon:
677
- return box2d.castObject(object, box2d.b2PolygonShape);
678
- case box2d.b2Shape.e_chain:
679
- return box2d.castObject(object, box2d.b2ChainShape);
680
- }
681
- }
682
- else if (object instanceof box2d.b2Joint)
889
+ /** Create a gear joint
890
+ * @param {Box2dObject} objectA
891
+ * @param {Box2dObject} objectB
892
+ * @param {Box2dJoint} joint1
893
+ * @param {Box2dJoint} joint2
894
+ * @param {ratio} [ratio] */
895
+ constructor(objectA, objectB, joint1, joint2, ratio=1)
683
896
  {
684
- switch (object.GetType())
685
- {
686
- case box2d.e_revoluteJoint:
687
- return box2d.castObject(object, box2d.b2RevoluteJoint);
688
- case box2d.e_prismaticJoint:
689
- return box2d.castObject(object, box2d.b2PrismaticJoint);
690
- case box2d.e_distanceJoint:
691
- return box2d.castObject(object, box2d.b2DistanceJoint);
692
- case box2d.e_pulleyJoint:
693
- return box2d.castObject(object, box2d.b2PulleyJoint);
694
- case box2d.e_mouseJoint:
695
- return box2d.castObject(object, box2d.b2MouseJoint);
696
- case box2d.e_gearJoint:
697
- return box2d.castObject(object, box2d.b2GearJoint);
698
- case box2d.e_wheelJoint:
699
- return box2d.castObject(object, box2d.b2WheelJoint);
700
- case box2d.e_weldJoint:
701
- return box2d.castObject(object, box2d.b2WeldJoint);
702
- case box2d.e_frictionJoint:
703
- return box2d.castObject(object, box2d.b2FrictionJoint);
704
- case box2d.e_ropeJoint:
705
- return box2d.castObject(object, box2d.b2RopeJoint);
706
- case box2d.e_motorJoint:
707
- return box2d.castObject(object, box2d.b2MotorJoint);
708
- }
897
+ const jointDef = new box2d.instance.b2GearJointDef();
898
+ jointDef.set_bodyA(objectA.body);
899
+ jointDef.set_bodyB(objectB.body);
900
+ jointDef.set_joint1(joint1.box2dJoint);
901
+ jointDef.set_joint2(joint2.box2dJoint);
902
+ jointDef.set_ratio(ratio);
903
+ super(jointDef);
904
+
905
+ this.joint1 = joint1;
906
+ this.joint2 = joint2;
709
907
  }
710
-
711
- ASSERT(false, 'Unknown object type');
712
- }
713
908
 
714
- function box2dWarmup(frames=100)
715
- {
716
- // run the sim for a few frames to let objects settle
717
- for (let i=frames; i--;)
718
- box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
909
+ /** Get the first joint
910
+ * @return {Box2dJoint} */
911
+ getJoint1() { return this.joint1; }
912
+
913
+ /** Get the second joint
914
+ * @return {Box2dJoint} */
915
+ getJoint2() { return this.joint2; }
916
+
917
+ /** Set the gear ratio
918
+ * @param {number} ratio */
919
+ setRatio(ratio) { return this.box2dJoint.SetRatio(ratio); }
920
+
921
+ /** Get the gear ratio
922
+ * @return {number} */
923
+ getRatio() { return this.box2dJoint.GetRatio(); }
719
924
  }
720
925
 
721
926
  ///////////////////////////////////////////////////////////////////////////////
722
- // Box2D Drawing
723
-
724
- function box2dDrawFixture(fixture, pos, angle, fillColor, outlineColor, lineWidth)
927
+ /**
928
+ * Box2D Prismatic Joint
929
+ * - Provides one degree of freedom: translation along an axis fixed in objectA
930
+ * - Relative rotation is prevented
931
+ * - You can use a joint limit to restrict the range of motion
932
+ * - You can use a joint motor to drive the motion or to model joint friction
933
+ * @extends Box2dJoint
934
+ */
935
+ class Box2dPrismaticJoint extends Box2dJoint
725
936
  {
726
- const shape = box2dCastObject(fixture.GetShape());
727
- switch (shape.GetType())
937
+ /** Create a prismatic joint
938
+ * @param {Box2dObject} objectA
939
+ * @param {Box2dObject} objectB
940
+ * @param {Vector2} anchor
941
+ * @param {Vector2} worldAxis
942
+ * @param {boolean} [collide] */
943
+ constructor(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
728
944
  {
729
- case box2d.b2Shape.e_polygon:
730
- {
731
- let points = [];
732
- for (let i=shape.GetVertexCount(); i--;)
733
- points.push(vec2(shape.GetVertex(i)));
734
- box2dDrawPoly(pos, angle, points, fillColor, outlineColor, lineWidth);
735
- break;
736
- }
737
- case box2d.b2Shape.e_circle:
738
- {
739
- const radius = shape.get_m_radius();
740
- box2dDrawCircle(pos, radius, fillColor, outlineColor, lineWidth);
741
- break;
742
- }
743
- case box2d.b2Shape.e_edge:
744
- {
745
- const v1 = vec2(shape.get_m_vertex1());
746
- const v2 = vec2(shape.get_m_vertex2());
747
- box2dDrawLine(pos, angle, v1, v2, fillColor, lineWidth);
748
- break;
749
- }
945
+ anchor ||= box2d.vec2From(objectB.body.GetPosition());
946
+ const localAnchorA = objectA.worldToLocal(anchor);
947
+ const localAnchorB = objectB.worldToLocal(anchor);
948
+ const localAxisA = objectB.worldToLocalVector(worldAxis);
949
+ const jointDef = new box2d.instance.b2PrismaticJointDef();
950
+ jointDef.set_bodyA(objectA.body);
951
+ jointDef.set_bodyB(objectB.body);
952
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
953
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
954
+ jointDef.set_localAxisA(box2d.vec2dTo(localAxisA));
955
+ jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
956
+ jointDef.set_collideConnected(collide);
957
+ super(jointDef);
750
958
  }
959
+
960
+ /** Get the local anchor point relative to objectA's origin
961
+ * @return {Vector2} */
962
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
963
+
964
+ /** Get the local anchor point relative to objectB's origin
965
+ * @return {Vector2} */
966
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
967
+
968
+ /** Get the local joint axis relative to bodyA
969
+ * @return {Vector2} */
970
+ getLocalAxisA() { return box2d.vec2From(this.box2dJoint.GetLocalAxisA()); }
971
+
972
+ /** Get the reference angle
973
+ * @return {number} */
974
+ getReferenceAngle() { return this.box2dJoint.GetReferenceAngle(); }
975
+
976
+ /** Get the current joint translation
977
+ * @return {number} */
978
+ getJointTranslation() { return this.box2dJoint.GetJointTranslation(); }
979
+
980
+ /** Get the current joint translation speed
981
+ * @return {number} */
982
+ getJointSpeed() { return this.box2dJoint.GetJointSpeed(); }
983
+
984
+ /** Is the joint limit enabled?
985
+ * @return {boolean} */
986
+ isLimitEnabled() { return this.box2dJoint.IsLimitEnabled(); }
987
+
988
+ /** Enable/disable the joint limit
989
+ * @param {boolean} [enable] */
990
+ enableLimit(enable=true) { return this.box2dJoint.enableLimit(enable); }
991
+
992
+ /** Get the lower joint limit
993
+ * @return {number} */
994
+ getLowerLimit() { return this.box2dJoint.GetLowerLimit(); }
995
+
996
+ /** Get the upper joint limit
997
+ * @return {number} */
998
+ getUpperLimit() { return this.box2dJoint.GetUpperLimit(); }
999
+
1000
+ /** Set the joint limits
1001
+ * @param {number} min
1002
+ * @param {number} max */
1003
+ setLimits(min, max) { return this.box2dJoint.SetLimits(min, max); }
1004
+
1005
+ /** Is the motor enabled?
1006
+ * @return {boolean} */
1007
+ isMotorEnabled() { return this.box2dJoint.IsMotorEnabled(); }
1008
+
1009
+ /** Enable/disable the joint motor
1010
+ * @param {boolean} [enable] */
1011
+ enableMotor(enable=true) { return this.box2dJoint.EnableMotor(enable); }
1012
+
1013
+ /** Set the motor speed
1014
+ * @param {number} speed */
1015
+ setMotorSpeed(speed) { return this.box2dJoint.SetMotorSpeed(speed); }
1016
+
1017
+ /** Get the motor speed
1018
+ * @return {number} */
1019
+ getMotorSpeed() { return this.box2dJoint.GetMotorSpeed(); }
1020
+
1021
+ /** Set the maximum motor force
1022
+ * @param {number} force */
1023
+ setMaxMotorForce(force) { return this.box2dJoint.SetMaxMotorForce(force); }
1024
+
1025
+ /** Get the maximum motor force
1026
+ * @return {number} */
1027
+ getMaxMotorForce() { return this.box2dJoint.GetMaxMotorForce(); }
1028
+
1029
+ /** Get the motor force given a time step
1030
+ * @param {number} time
1031
+ * @return {number} */
1032
+ getMotorForce(time) { return this.box2dJoint.GetMotorForce(1/time); }
751
1033
  }
752
1034
 
753
- function box2dDrawCircle(pos, radius, color=WHITE, outlineColor, lineWidth=.1, context)
1035
+ ///////////////////////////////////////////////////////////////////////////////
1036
+ /**
1037
+ * Box2D Wheel Joint
1038
+ * - Provides two degrees of freedom: translation along an axis fixed in objectA and rotation
1039
+ * - You can use a joint limit to restrict the range of motion
1040
+ * - You can use a joint motor to drive the motion or to model joint friction
1041
+ * - This joint is designed for vehicle suspensions
1042
+ * @extends Box2dJoint
1043
+ */
1044
+ class Box2dWheelJoint extends Box2dJoint
754
1045
  {
755
- drawCanvas2D(pos, vec2(1), 0, 0, context=>
1046
+ /** Create a wheel joint
1047
+ * @param {Box2dObject} objectA
1048
+ * @param {Box2dObject} objectB
1049
+ * @param {Vector2} anchor
1050
+ * @param {Vector2} worldAxis
1051
+ * @param {boolean} [collide] */
1052
+ constructor(objectA, objectB, anchor, worldAxis=vec2(0,1), collide=false)
756
1053
  {
757
- context.beginPath();
758
- context.arc(0, 0, radius, 0, 9);
759
- box2dDrawFillStroke(context, color, outlineColor, lineWidth);
760
- }, 0, context);
1054
+ anchor ||= box2d.vec2From(objectB.body.GetPosition());
1055
+ const localAnchorA = objectA.worldToLocal(anchor);
1056
+ const localAnchorB = objectB.worldToLocal(anchor);
1057
+ const localAxisA = objectB.worldToLocalVector(worldAxis);
1058
+ const jointDef = new box2d.instance.b2WheelJointDef();
1059
+ jointDef.set_bodyA(objectA.body);
1060
+ jointDef.set_bodyB(objectB.body);
1061
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
1062
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
1063
+ jointDef.set_localAxisA(box2d.vec2dTo(localAxisA));
1064
+ jointDef.set_collideConnected(collide);
1065
+ super(jointDef);
1066
+ }
1067
+
1068
+ /** Get the local anchor point relative to objectA's origin
1069
+ * @return {Vector2} */
1070
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
1071
+
1072
+ /** Get the local anchor point relative to objectB's origin
1073
+ * @return {Vector2} */
1074
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
1075
+
1076
+ /** Get the local joint axis relative to bodyA
1077
+ * @return {Vector2} */
1078
+ getLocalAxisA() { return box2d.vec2From(this.box2dJoint.GetLocalAxisA()); }
1079
+
1080
+ /** Get the current joint translation
1081
+ * @return {number} */
1082
+ getJointTranslation() { return this.box2dJoint.GetJointTranslation(); }
1083
+
1084
+ /** Get the current joint translation speed
1085
+ * @return {number} */
1086
+ getJointSpeed() { return this.box2dJoint.GetJointSpeed(); }
1087
+
1088
+ /** Is the joint motor enabled?
1089
+ * @return {boolean} */
1090
+ isMotorEnabled() { return this.box2dJoint.IsMotorEnabled(); }
1091
+
1092
+ /** Enable/disable the joint motor
1093
+ * @param {boolean} [enable] */
1094
+ enableMotor(enable=true) { return this.box2dJoint.EnableMotor(enable); }
1095
+
1096
+ /** Set the motor speed
1097
+ * @param {number} speed */
1098
+ setMotorSpeed(speed) { return this.box2dJoint.SetMotorSpeed(speed); }
1099
+
1100
+ /** Get the motor speed
1101
+ * @return {number} */
1102
+ getMotorSpeed() { return this.box2dJoint.GetMotorSpeed(); }
1103
+
1104
+ /** Set the maximum motor torque
1105
+ * @param {number} torque */
1106
+ setMaxMotorTorque(torque) { return this.box2dJoint.SetMaxMotorTorque(torque); }
1107
+
1108
+ /** Get the max motor torque
1109
+ * @return {number} */
1110
+ getMaxMotorTorque() { return this.box2dJoint.GetMaxMotorTorque(); }
1111
+
1112
+ /** Get the motor torque for a time step
1113
+ * @return {number} */
1114
+ getMotorTorque(time) { return this.box2dJoint.GetMotorTorque(1/time); }
1115
+
1116
+ /** Set the spring frequency in Hertz
1117
+ * @param {number} hz */
1118
+ setSpringFrequencyHz(hz) { return this.box2dJoint.SetSpringFrequencyHz(hz); }
1119
+
1120
+ /** Get the spring frequency in Hertz
1121
+ * @return {number} */
1122
+ getSpringFrequencyHz() { return this.box2dJoint.GetSpringFrequencyHz(); }
1123
+
1124
+ /** Set the spring damping ratio
1125
+ * @param {number} ratio */
1126
+ setSpringDampingRatio(ratio) { return this.box2dJoint.SetSpringDampingRatio(ratio); }
1127
+
1128
+ /** Get the spring damping ratio
1129
+ * @return {number} */
1130
+ getSpringDampingRatio() { return this.box2dJoint.GetSpringDampingRatio(); }
761
1131
  }
762
1132
 
763
- function box2dDrawPoly(pos, angle, points, color=WHITE, outlineColor, lineWidth=.1, context)
1133
+ ///////////////////////////////////////////////////////////////////////////////
1134
+ /**
1135
+ * Box2D Weld Joint
1136
+ * - Glues two objects together
1137
+ * @extends Box2dJoint
1138
+ */
1139
+ class Box2dWeldJoint extends Box2dJoint
764
1140
  {
765
- drawCanvas2D(pos, vec2(1), angle, 0, context=>
1141
+ /** Create a weld joint
1142
+ * @param {Box2dObject} objectA
1143
+ * @param {Box2dObject} objectB
1144
+ * @param {Vector2} anchor
1145
+ * @param {boolean} [collide] */
1146
+ constructor(objectA, objectB, anchor, collide=false)
766
1147
  {
767
- context.beginPath();
768
- points.forEach(p=>context.lineTo(p.x, p.y));
769
- context.closePath();
770
- box2dDrawFillStroke(context, color, outlineColor, lineWidth);
771
- }, 0, context);
1148
+ anchor ||= box2d.vec2From(objectB.body.GetPosition());
1149
+ const localAnchorA = objectA.worldToLocal(anchor);
1150
+ const localAnchorB = objectB.worldToLocal(anchor);
1151
+ const jointDef = new box2d.instance.b2WeldJointDef();
1152
+ jointDef.set_bodyA(objectA.body);
1153
+ jointDef.set_bodyB(objectB.body);
1154
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
1155
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
1156
+ jointDef.set_referenceAngle(objectA.body.GetAngle() - objectB.body.GetAngle());
1157
+ jointDef.set_collideConnected(collide);
1158
+ super(jointDef);
1159
+ }
1160
+
1161
+ /** Get the local anchor point relative to objectA's origin
1162
+ * @return {Vector2} */
1163
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
1164
+
1165
+ /** Get the local anchor point relative to objectB's origin
1166
+ * @return {Vector2} */
1167
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
1168
+
1169
+ /** Get the reference angle
1170
+ * @return {number} */
1171
+ getReferenceAngle() { return this.box2dJoint.GetReferenceAngle(); }
1172
+
1173
+ /** Set the frequency in Hertz
1174
+ * @param {number} hz */
1175
+ setFrequency(hz) { return this.box2dJoint.SetFrequency(hz); }
1176
+
1177
+ /** Get the frequency in Hertz
1178
+ * @return {number} */
1179
+ getFrequency() { return this.box2dJoint.GetFrequency(); }
1180
+
1181
+ /** Set the damping ratio
1182
+ * @param {number} ratio */
1183
+ setSpringDampingRatio(ratio) { return this.box2dJoint.SetSpringDampingRatio(ratio); }
1184
+
1185
+ /** Get the damping ratio
1186
+ * @return {number} */
1187
+ getSpringDampingRatio() { return this.box2dJoint.GetSpringDampingRatio(); }
772
1188
  }
773
1189
 
774
- function box2dDrawLine(pos, angle, posA, posB, color=WHITE, lineWidth=.1, context)
1190
+ ///////////////////////////////////////////////////////////////////////////////
1191
+ /**
1192
+ * Box2D Friction Joint
1193
+ * - Used to apply top-down friction
1194
+ * - Provides 2D translational friction and angular friction
1195
+ * @extends Box2dJoint
1196
+ */
1197
+ class Box2dFrictionJoint extends Box2dJoint
775
1198
  {
776
- drawCanvas2D(pos, vec2(1), angle, 0, context=>
1199
+ /** Create a friction joint
1200
+ * @param {Box2dObject} objectA
1201
+ * @param {Box2dObject} objectB
1202
+ * @param {Vector2} anchor
1203
+ * @param {boolean} [collide] */
1204
+ constructor(objectA, objectB, anchor, collide=false)
777
1205
  {
778
- context.beginPath();
779
- context.lineTo(posA.x, posA.y);
780
- context.lineTo(posB.x, posB.y);
781
- box2dDrawFillStroke(context, 0, color, lineWidth);
782
- }, 0, context);
1206
+ anchor ||= box2d.vec2From(objectB.body.GetPosition());
1207
+ const localAnchorA = objectA.worldToLocal(anchor);
1208
+ const localAnchorB = objectB.worldToLocal(anchor);
1209
+ const jointDef = new box2d.instance.b2FrictionJointDef();
1210
+ jointDef.set_bodyA(objectA.body);
1211
+ jointDef.set_bodyB(objectB.body);
1212
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
1213
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
1214
+ jointDef.set_collideConnected(collide);
1215
+ super(jointDef);
1216
+ }
1217
+
1218
+ /** Get the local anchor point relative to objectA's origin
1219
+ * @return {Vector2} */
1220
+ getLocalAnchorA() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorA()); }
1221
+
1222
+ /** Get the local anchor point relative to objectB's origin
1223
+ * @return {Vector2} */
1224
+ getLocalAnchorB() { return box2d.vec2From(this.box2dJoint.GetLocalAnchorB()); }
1225
+
1226
+ /** Set the maximum friction force
1227
+ * @param {number} force */
1228
+ setMaxForce(force) { this.box2dJoint.SetMaxForce(force); }
1229
+
1230
+ /** Get the maximum friction force
1231
+ * @return {number} */
1232
+ getMaxForce() { return this.box2dJoint.GetMaxForce(); }
1233
+
1234
+ /** Set the maximum friction torque
1235
+ * @param {number} torque */
1236
+ setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(torque); }
1237
+
1238
+ /** Get the maximum friction torque
1239
+ * @return {number} */
1240
+ getMaxTorque() { return this.box2dJoint.GetMaxTorque(); }
783
1241
  }
784
1242
 
785
- function box2dDrawFillStroke(context, color, outlineColor, lineWidth)
1243
+ ///////////////////////////////////////////////////////////////////////////////
1244
+ /**
1245
+ * Box2D Pulley Joint
1246
+ * - Connects to two objects and two fixed ground points
1247
+ * - The pulley supports a ratio such that: length1 + ratio * length2 <= constant
1248
+ * - The force transmitted is scaled by the ratio
1249
+ * @extends Box2dJoint
1250
+ */
1251
+ class Box2dPulleyJoint extends Box2dJoint
786
1252
  {
787
- if (color)
1253
+ /** Create a pulley joint
1254
+ * @param {Box2dObject} objectA
1255
+ * @param {Box2dObject} objectB
1256
+ * @param {Vector2} groundAnchorA
1257
+ * @param {Vector2} groundAnchorB
1258
+ * @param {Vector2} anchorA
1259
+ * @param {Vector2} anchorB
1260
+ * @param {number} [ratio]
1261
+ * @param {boolean} [collide] */
1262
+ constructor(objectA, objectB, groundAnchorA, groundAnchorB, anchorA, anchorB, ratio=1, collide=false)
788
1263
  {
789
- context.fillStyle = color.toString();
790
- context.fill();
791
- }
792
- if (outlineColor && lineWidth)
793
- {
794
- context.lineWidth = lineWidth;
795
- context.lineJoin = context.lineCap = 'round';
796
- context.strokeStyle = outlineColor.toString();
797
- context.stroke();
1264
+ anchorA ||= box2d.vec2From(objectA.body.GetPosition());
1265
+ anchorB ||= box2d.vec2From(objectB.body.GetPosition());
1266
+ const localAnchorA = objectA.worldToLocal(anchorA);
1267
+ const localAnchorB = objectB.worldToLocal(anchorB);
1268
+ const jointDef = new box2d.instance.b2PulleyJointDef();
1269
+ jointDef.set_bodyA(objectA.body);
1270
+ jointDef.set_bodyB(objectB.body);
1271
+ jointDef.set_groundAnchorA(box2d.vec2dTo(groundAnchorA));
1272
+ jointDef.set_groundAnchorB(box2d.vec2dTo(groundAnchorB));
1273
+ jointDef.set_localAnchorA(box2d.vec2dTo(localAnchorA));
1274
+ jointDef.set_localAnchorB(box2d.vec2dTo(localAnchorB));
1275
+ jointDef.set_ratio(ratio);
1276
+ jointDef.set_lengthA(groundAnchorA.distance(anchorA));
1277
+ jointDef.set_lengthB(groundAnchorB.distance(anchorB));
1278
+ jointDef.set_collideConnected(collide);
1279
+ super(jointDef);
798
1280
  }
1281
+
1282
+ /** Get the first ground anchor
1283
+ * @return {Vector2} */
1284
+ getGroundAnchorA() { return box2d.vec2From(this.box2dJoint.GetGroundAnchorA()); }
1285
+
1286
+ /** Get the second ground anchor
1287
+ * @return {Vector2} */
1288
+ getGroundAnchorB() { return box2d.vec2From(this.box2dJoint.GetGroundAnchorB()); }
1289
+
1290
+ /** Get the current length of the segment attached to objectA
1291
+ * @return {number} */
1292
+ getLengthA() { return this.box2dJoint.GetLengthA(); }
1293
+
1294
+ /** Get the current length of the segment attached to objectB
1295
+ * @return {number} */
1296
+ getLengthB(){ return this.box2dJoint.GetLengthB(); }
1297
+
1298
+ /** Get the pulley ratio
1299
+ * @return {number} */
1300
+ getRatio() { return this.box2dJoint.GetRatio(); }
1301
+
1302
+ /** Get the current length of the segment attached to objectA
1303
+ * @return {number} */
1304
+ getCurrentLengthA() { return this.box2dJoint.GetCurrentLengthA(); }
1305
+
1306
+ /** Get the current length of the segment attached to objectB
1307
+ * @return {number} */
1308
+ getCurrentLengthB() { return this.box2dJoint.GetCurrentLengthB(); }
799
1309
  }
800
1310
 
801
1311
  ///////////////////////////////////////////////////////////////////////////////
802
- // Box2D Setup
803
-
804
- function box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources)
1312
+ /**
1313
+ * Box2D Motor Joint
1314
+ * - Controls the relative motion between two objects
1315
+ * - Typical usage is to control the movement of a object with respect to the ground
1316
+ * @extends Box2dJoint
1317
+ */
1318
+ class Box2dMotorJoint extends Box2dJoint
805
1319
  {
806
- Box2D().then(_box2d=>
1320
+ /** Create a motor joint
1321
+ * @param {Box2dObject} objectA
1322
+ * @param {Box2dObject} objectB */
1323
+ constructor(objectA, objectB)
807
1324
  {
808
- // setup box2d
809
- box2d = _box2d;
810
- box2dWorld = new box2d.b2World();
1325
+ const linearOffset = objectA.worldToLocal(box2d.vec2From(objectB.body.GetPosition()));
1326
+ const angularOffset = objectB.body.GetAngle() - objectA.body.GetAngle();
1327
+ const jointDef = new box2d.instance.b2MotorJointDef();
1328
+ jointDef.set_bodyA(objectA.body);
1329
+ jointDef.set_bodyB(objectB.body);
1330
+ jointDef.set_linearOffset(box2d.vec2dTo(linearOffset));
1331
+ jointDef.set_angularOffset(angularOffset);
1332
+ super(jointDef);
1333
+ }
811
1334
 
812
- // override functions for box2d
813
- setGravity = function(newGravity)
814
- {
815
- box2dWorld.SetGravity(vec2(0,newGravity).getBox2d());
816
- gravity = newGravity*timeDelta*timeDelta; // engine gravity
817
- }
1335
+ /** Set the target linear offset, in frame A, in meters.
1336
+ * @param {Vector2} offset */
1337
+ setLinearOffset(offset) { this.box2dJoint.SetLinearOffset(box2d.vec2dTo(offset)); }
818
1338
 
819
- // allow passing box2d vectors to vec2
820
- const defaultVec2 = vec2;
821
- vec2 = function(x, y)
822
- {
823
- return (x instanceof box2d.b2Vec2) ?
824
- new Vector2(x.get_x(), x.get_y()) : defaultVec2(x, y);
825
- }
1339
+ /** Get the target linear offset, in frame A, in meters.
1340
+ * @return {Vector2} */
1341
+ getLinearOffset() { return box2d.vec2From(this.box2dJoint.GetLinearOffset()); }
1342
+
1343
+ /** Set the target angular offset
1344
+ * @param {number} offset */
1345
+ setAngularOffset(offset) { this.box2dJoint.SetAngularOffset(offset); }
1346
+
1347
+ /** Get the target angular offset
1348
+ * @return {number} */
1349
+ getAngularOffset() { return this.box2dJoint.GetAngularOffset(); }
1350
+
1351
+ /** Set the maximum friction force
1352
+ * @param {number} force */
1353
+ setMaxForce(force) { this.box2dJoint.SetMaxForce(force); }
1354
+
1355
+ /** Get the maximum friction force
1356
+ * @return {number} */
1357
+ getMaxForce() { return this.box2dJoint.GetMaxForce(); }
826
1358
 
827
- // functions to convert between vec2 and box2d vectors
828
- Vector2.prototype.setBox2d = function(p) { return this.set(p.get_x(), p.get_y()); }
829
- Vector2.prototype.getBox2d = function() { return new box2d.b2Vec2(this.x, this.y); }
830
- Vector2.prototype.setBox2dPointer = function(p)
831
- { return this.setBox2d(box2d.wrapPointer(p, box2d.b2Vec2)); }
1359
+ /** Set the maximum torque
1360
+ * @param {number} torque */
1361
+ setMaxTorque(torque) { this.box2dJoint.SetMaxTorque(torque); }
832
1362
 
833
- // functions to convert between color and box2d colors
834
- Color.prototype.setBox2d = function(c)
835
- { return this.set(c.get_r(), c.get_g(), c.get_b()); }
836
- Color.prototype.setBox2dPointer = function(c)
837
- { return this.setBox2d(box2d.wrapPointer(c, box2d.b2Color)); }
1363
+ /** Get the maximum torque
1364
+ * @return {number} */
1365
+ getMaxTorque() { return this.box2dJoint.GetMaxTorque(); }
1366
+
1367
+ /** Set the position correction factor in the range [0,1]
1368
+ * @param {number} factor */
1369
+ setCorrectionFactor(factor) { this.box2dJoint.SetCorrectionFactor(factor); }
1370
+
1371
+ /** Get the position correction factor in the range [0,1]
1372
+ * @return {number} */
1373
+ getCorrectionFactor() { return this.box2dJoint.GetCorrectionFactor(); }
1374
+ }
1375
+
1376
+ ///////////////////////////////////////////////////////////////////////////////
1377
+ /**
1378
+ * Box2D Global Object
1379
+ * - Wraps Box2d world and provides global functions
1380
+ */
1381
+ class Box2dPlugin
1382
+ {
1383
+ /** Create the global UI system object
1384
+ * @param {Object} instance */
1385
+ constructor(instance)
1386
+ {
1387
+ ASSERT(!box2d, 'Box2D already initialized');
1388
+ box2d = this;
1389
+ this.instance = instance;
1390
+ this.world = new box2d.instance.b2World();
1391
+
1392
+ /** @property {number} - Velocity iterations per update*/
1393
+ this.velocityIterations = 8;
1394
+ /** @property {number} - Position iterations per update*/
1395
+ this.positionIterations = 3;
1396
+ /** @property {number} - Static, zero mass, zero velocity, may be manually moved */
1397
+ this.bodyTypeStatic = instance.b2_staticBody;
1398
+ /** @property {number} - Kinematic, zero mass, non-zero velocity set by user, moved by solver */
1399
+ this.bodyTypeKinematic = instance.b2_kinematicBody;
1400
+ /** @property {number} - Dynamic, positive mass, non-zero velocity determined by forces, moved by solver */
1401
+ this.bodyTypeDynamic = instance.b2_dynamicBody;
838
1402
 
839
1403
  // setup contact listener
840
- const listener = new box2d.JSContactListener();
1404
+ const listener = new box2d.instance.JSContactListener();
841
1405
  listener.BeginContact = function(contactPtr)
842
1406
  {
843
- const contact = box2d.wrapPointer(contactPtr, box2d.b2Contact);
1407
+ const contact = box2d.instance.wrapPointer(contactPtr, box2d.instance.b2Contact);
844
1408
  const fixtureA = contact.GetFixtureA();
845
1409
  const fixtureB = contact.GetFixtureB();
846
1410
  const objectA = fixtureA.GetBody().object;
847
1411
  const objectB = fixtureB.GetBody().object;
848
- objectA.beginContact(objectB, contact);
849
- objectB.beginContact(objectA, contact);
1412
+ objectA.beginContact(objectB);
1413
+ objectB.beginContact(objectA);
850
1414
  }
851
1415
  listener.EndContact = function(contactPtr)
852
1416
  {
853
- const contact = box2d.wrapPointer(contactPtr, box2d.b2Contact);
1417
+ const contact = box2d.instance.wrapPointer(contactPtr, box2d.instance.b2Contact);
854
1418
  const fixtureA = contact.GetFixtureA();
855
1419
  const fixtureB = contact.GetFixtureB();
856
1420
  const objectA = fixtureA.GetBody().object;
857
1421
  const objectB = fixtureB.GetBody().object;
858
- objectA.endContact(objectB, contact);
859
- objectB.endContact(objectA, contact);
1422
+ objectA.endContact(objectB);
1423
+ objectB.endContact(objectA);
860
1424
  };
861
1425
  listener.PreSolve = function() {};
862
1426
  listener.PostSolve = function() {};
863
- box2dWorld.SetContactListener(listener);
1427
+ box2d.world.SetContactListener(listener);
1428
+ }
864
1429
 
865
- // setup debug draw
866
- box2dDebugDraw = box2dGetDebugDraw();
867
- box2dDebugDraw.AppendFlags(box2d.b2Draw.e_shapeBit);
868
- box2dDebugDraw.AppendFlags(box2d.b2Draw.e_jointBit);
869
- //box2dDebugDraw.AppendFlags(box2d.b2Draw.e_aabbBit);
870
- //box2dDebugDraw.AppendFlags(box2d.b2Draw.e_pairBit);
871
- //box2dDebugDraw.AppendFlags(box2d.b2Draw.e_centerOfMassBit);
872
- box2dWorld.SetDebugDraw(box2dDebugDraw);
873
-
874
- // hook up box2d plugin to update and render
875
- engineAddPlugin(box2dUpdate, box2dRender);
876
- function box2dUpdate()
1430
+ /** Step the physics world simulation
1431
+ * @param {number} [frames] */
1432
+ step(frames=1)
1433
+ {
1434
+ box2d.world.SetGravity(box2d.vec2dTo(gravity));
1435
+ for (let i=frames; i--;)
1436
+ box2d.world.Step(timeDelta, this.velocityIterations, this.positionIterations);
1437
+ }
1438
+
1439
+ ///////////////////////////////////////////////////////////////////////////////
1440
+ // raycasting and querying
1441
+
1442
+ /** raycast and return a list of all the results
1443
+ * @param {Vector2} start
1444
+ * @param {Vector2} end */
1445
+ raycastAll(start, end)
1446
+ {
1447
+ const raycastCallback = new box2d.instance.JSRayCastCallback();
1448
+ raycastCallback.ReportFixture = function(fixturePointer, point, normal, fraction)
877
1449
  {
878
- if (!paused)
879
- box2dWorld.Step(timeDelta, box2dStepIterations, box2dStepIterations);
880
- }
881
- function box2dRender()
1450
+ const fixture = box2d.instance.wrapPointer(fixturePointer, box2d.instance.b2Fixture);
1451
+ point = box2d.vec2FromPointer(point);
1452
+ normal = box2d.vec2FromPointer(normal);
1453
+ raycastResults.push(new Box2dRaycastResult(fixture, point, normal, fraction));
1454
+ return 1; // continue getting results
1455
+ };
1456
+
1457
+ const raycastResults = [];
1458
+ box2d.world.RayCast(raycastCallback, box2d.vec2dTo(start), box2d.vec2dTo(end));
1459
+ debugRaycast && debugLine(start, end, raycastResults.length ? '#f00' : '#00f', .02);
1460
+ return raycastResults;
1461
+ }
1462
+
1463
+ /** raycast and return the first result
1464
+ * @param {Vector2} start
1465
+ * @param {Vector2} end */
1466
+ raycast(start, end)
1467
+ {
1468
+ const raycastResults = box2d.raycastAll(start, end);
1469
+ if (!raycastResults.length)
1470
+ return undefined;
1471
+ return raycastResults.reduce((a,b)=>a.fraction < b.fraction ? a : b);
1472
+ }
1473
+
1474
+ /** box aabb cast and return all the objects
1475
+ * @param {Vector2} pos
1476
+ * @param {Vector2} size */
1477
+ boxCastAll(pos, size)
1478
+ {
1479
+ const queryCallback = new box2d.instance.JSQueryCallback();
1480
+ queryCallback.ReportFixture = function(fixturePointer)
882
1481
  {
883
- if (box2dDebug || debugPhysics && debugOverlay)
884
- box2dWorld.DrawDebugData();
885
- }
1482
+ const fixture = box2d.instance.wrapPointer(fixturePointer, box2d.instance.b2Fixture);
1483
+ const o = fixture.GetBody().object;
1484
+ if (!queryObjects.includes(o))
1485
+ queryObjects.push(o); // add if not already in list
1486
+ return true; // continue getting results
1487
+ };
886
1488
 
887
- // start littlejs
888
- engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources);
889
-
890
- // box2d debug drawing implementation
891
- function box2dGetDebugDraw()
1489
+ const aabb = new box2d.instance.b2AABB();
1490
+ aabb.set_lowerBound(box2d.vec2dTo(pos.subtract(size.scale(.5))));
1491
+ aabb.set_upperBound(box2d.vec2dTo(pos.add(size.scale(.5))));
1492
+
1493
+ let queryObjects = [];
1494
+ box2d.world.QueryAABB(queryCallback, aabb);
1495
+ debugRaycast && debugRect(pos, size, queryObjects.length ? '#f00' : '#00f', .02);
1496
+ return queryObjects;
1497
+ }
1498
+
1499
+ /** box aabb cast and return the first object
1500
+ * @param {Vector2} pos
1501
+ * @param {Vector2} size */
1502
+ boxCast(pos, size)
1503
+ {
1504
+ const queryCallback = new box2d.instance.JSQueryCallback();
1505
+ queryCallback.ReportFixture = function(fixturePointer)
892
1506
  {
893
- const debugDraw = new box2d.JSDraw();
894
- debugDraw.DrawSegment = function(point1, point2, color)
895
- {
896
- color = getDebugColor(color);
897
- point1 = vec2().setBox2dPointer(point1);
898
- point2 = vec2().setBox2dPointer(point2);
899
- box2dDrawLine(vec2(), 0, point1, point2, color, undefined, overlayContext);
900
- };
901
- debugDraw.DrawPolygon = function(vertices, vertexCount, color)
902
- {
903
- color = getDebugColor(color);
904
- const points = getPointsList(vertices, vertexCount);
905
- box2dDrawPoly(vec2(), 0, points, undefined, color, undefined, overlayContext);
906
- };
907
- debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
908
- {
909
- color = getDebugColor(color);
910
- const points = getPointsList(vertices, vertexCount);
911
- box2dDrawPoly(vec2(), 0, points, color, color, undefined, overlayContext);
912
- };
913
- debugDraw.box2dDrawCircle = function(center, radius, color)
914
- {
915
- color = getDebugColor(color);
916
- center = vec2().setBox2dPointer(center);
917
- box2dDrawCircle(center, radius, undefined, color, undefined, overlayContext);
918
- };
919
- debugDraw.DrawSolidCircle = function(center, radius, axis, color)
1507
+ const fixture = box2d.instance.wrapPointer(fixturePointer, box2d.instance.b2Fixture);
1508
+ queryObject = fixture.GetBody().object;
1509
+ return false; // stop getting results
1510
+ };
1511
+
1512
+ const aabb = new box2d.instance.b2AABB();
1513
+ aabb.set_lowerBound(box2d.vec2dTo(pos.subtract(size.scale(.5))));
1514
+ aabb.set_upperBound(box2d.vec2dTo(pos.add(size.scale(.5))));
1515
+
1516
+ let queryObject;
1517
+ box2d.world.QueryAABB(queryCallback, aabb);
1518
+ debugRaycast && debugRect(pos, size, queryObject ? '#f00' : '#00f', .02);
1519
+ return queryObject;
1520
+ }
1521
+
1522
+ /** circle cast and return all the objects
1523
+ * @param {Vector2} pos
1524
+ * @param {number} diameter */
1525
+ circleCastAll(pos, diameter)
1526
+ {
1527
+ const radius2 = (diameter/2)**2;
1528
+ const results = box2d.boxCastAll(pos, vec2(diameter));
1529
+ return results.filter(o=>o.pos.distanceSquared(pos) < radius2);
1530
+ }
1531
+
1532
+ /** circle cast and return the first object
1533
+ * @param {Vector2} pos
1534
+ * @param {number} diameter */
1535
+ circleCast(pos, diameter)
1536
+ {
1537
+ const radius2 = (diameter/2)**2;
1538
+ let results = box2d.boxCastAll(pos, vec2(diameter));
1539
+
1540
+ let bestResult, bestDistance2;
1541
+ for (const result of results)
1542
+ {
1543
+ const distance2 = result.pos.distanceSquared(pos);
1544
+ if (distance2 < radius2 && (!bestResult || distance2 < bestDistance2))
920
1545
  {
921
- color = getDebugColor(color);
922
- center = vec2().setBox2dPointer(center);
923
- axis = vec2().setBox2dPointer(axis).scale(radius);
924
- box2dDrawCircle(center, radius, color, color, undefined, overlayContext);
925
- box2dDrawLine(center, 0, vec2(), axis, color, undefined, overlayContext);
926
- };
927
- debugDraw.DrawTransform = function(transform)
1546
+ bestResult = result;
1547
+ bestDistance2 = distance2;
1548
+ }
1549
+ }
1550
+ return bestResult;
1551
+ }
1552
+
1553
+ /** point cast and return the first object
1554
+ * @param {Vector2} pos
1555
+ * @param {boolean} dynamicOnly */
1556
+ pointCast(pos, dynamicOnly=true)
1557
+ {
1558
+ const queryCallback = new box2d.instance.JSQueryCallback();
1559
+ queryCallback.ReportFixture = function(fixturePointer)
1560
+ {
1561
+ const fixture = box2d.instance.wrapPointer(fixturePointer, box2d.instance.b2Fixture);
1562
+ if (dynamicOnly && fixture.GetBody().GetType() != box2d.instance.b2_dynamicBody)
1563
+ return true; // continue getting results
1564
+ if (!fixture.TestPoint(box2d.vec2dTo(pos)))
1565
+ return true; // continue getting results
1566
+ queryObject = fixture.GetBody().object;
1567
+ return false; // stop getting results
1568
+ };
1569
+
1570
+ const aabb = new box2d.instance.b2AABB();
1571
+ aabb.set_lowerBound(box2d.vec2dTo(pos));
1572
+ aabb.set_upperBound(box2d.vec2dTo(pos));
1573
+
1574
+ let queryObject;
1575
+ box2d.world.QueryAABB(queryCallback, aabb);
1576
+ debugRaycast && debugRect(pos, vec2(), queryObject ? '#f00' : '#00f', .02);
1577
+ return queryObject;
1578
+ }
1579
+
1580
+ ///////////////////////////////////////////////////////////////////////////////
1581
+ // drawing
1582
+
1583
+ /** draws a fixture
1584
+ * @param {Object} fixture
1585
+ * @param {Vector2} pos
1586
+ * @param {number} angle
1587
+ * @param {Color} [color]
1588
+ * @param {Color} [outlineColor]
1589
+ * @param {number} [lineWidth]
1590
+ * @param {CanvasRenderingContext2D} [context] */
1591
+ drawFixture(fixture, pos, angle, color=WHITE, outlineColor=BLACK, lineWidth=.1, context=mainContext)
1592
+ {
1593
+ const shape = box2d.castObjectType(fixture.GetShape());
1594
+ switch (shape.GetType())
1595
+ {
1596
+ case box2d.instance.b2Shape.e_polygon:
928
1597
  {
929
- transform = box2d.wrapPointer(transform, box2d.b2Transform);
930
- const pos = vec2(transform.get_p());
931
- const angle = -transform.get_q().GetAngle();
932
- const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8)
933
- const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
934
- box2dDrawLine(pos, angle, vec2(), p1, c1, undefined, overlayContext);
935
- box2dDrawLine(pos, angle, vec2(), p2, c2, undefined, overlayContext);
1598
+ let points = [];
1599
+ for (let i=shape.GetVertexCount(); i--;)
1600
+ points.push(box2d.vec2From(shape.GetVertex(i)));
1601
+ box2d.drawPoly(pos, angle, points, color, outlineColor, lineWidth, context);
1602
+ break;
936
1603
  }
937
- function getDebugColor(color)
1604
+ case box2d.instance.b2Shape.e_circle:
938
1605
  {
939
- color = rgb().setBox2dPointer(color);
940
- color.a = .8;
941
- return color;
1606
+ const radius = shape.get_m_radius();
1607
+ box2d.drawCircle(pos, radius, color, outlineColor, lineWidth, context);
1608
+ break;
942
1609
  }
943
- function getPointsList(vertices, vertexCount)
1610
+ case box2d.instance.b2Shape.e_edge:
944
1611
  {
945
- const points = [];
946
- for (let i=vertexCount; i--;)
947
- points.push(vec2().setBox2dPointer(vertices+i*8));
948
- return points
1612
+ const v1 = box2d.vec2From(shape.get_m_vertex1());
1613
+ const v2 = box2d.vec2From(shape.get_m_vertex2());
1614
+ box2d.drawLine(pos, angle, v1, v2, color, lineWidth, context);
1615
+ break;
949
1616
  }
950
- return debugDraw;
951
1617
  }
1618
+ }
1619
+
1620
+ /** draws a circle
1621
+ * @param {Vector2} pos
1622
+ * @param {number} radius
1623
+ * @param {Color} [color]
1624
+ * @param {Color} [outlineColor]
1625
+ * @param {number} [lineWidth]
1626
+ * @param {CanvasRenderingContext2D} [context] */
1627
+ drawCircle(pos, radius, color=WHITE, outlineColor=BLACK, lineWidth=.1, context=mainContext)
1628
+ {
1629
+ drawCanvas2D(pos, vec2(1), 0, 0, context=>
1630
+ {
1631
+ context.beginPath();
1632
+ context.arc(0, 0, radius, 0, 9);
1633
+ box2d.drawFillStroke(color, outlineColor, lineWidth, context);
1634
+ }, 0, context);
1635
+ }
1636
+
1637
+ /** draws a polygon
1638
+ * @param {Vector2} pos
1639
+ * @param {number} angle
1640
+ * @param {Array<Vector2>} points
1641
+ * @param {Color} [color]
1642
+ * @param {Color} [outlineColor]
1643
+ * @param {number} [lineWidth]
1644
+ * @param {CanvasRenderingContext2D} [context] */
1645
+ drawPoly(pos, angle, points, color=WHITE, outlineColor=BLACK, lineWidth=.1, context=mainContext)
1646
+ {
1647
+ drawCanvas2D(pos, vec2(1), angle, 0, context=>
1648
+ {
1649
+ context.beginPath();
1650
+ points.forEach(p=>context.lineTo(p.x, p.y));
1651
+ context.closePath();
1652
+ box2d.drawFillStroke(color, outlineColor, lineWidth, context);
1653
+ }, 0, context);
1654
+ }
1655
+
1656
+ /** draws a line
1657
+ * @param {Vector2} pos
1658
+ * @param {number} angle
1659
+ * @param {Vector2} posA
1660
+ * @param {Vector2} posB
1661
+ * @param {Color} [color]
1662
+ * @param {number} [lineWidth]
1663
+ * @param {CanvasRenderingContext2D} [context] */
1664
+ drawLine(pos, angle, posA, posB, color=WHITE, lineWidth=.1, context=mainContext)
1665
+ {
1666
+ drawCanvas2D(pos, vec2(1), angle, 0, context=>
1667
+ {
1668
+ context.beginPath();
1669
+ context.lineTo(posA.x, posA.y);
1670
+ context.lineTo(posB.x, posB.y);
1671
+ box2d.drawFillStroke(0, color, lineWidth, context);
1672
+ }, 0, context);
1673
+ }
1674
+
1675
+ /** performs a fill or stroke as a helper to the other draw functions
1676
+ * @param {Color} [color]
1677
+ * @param {Color} [outlineColor]
1678
+ * @param {number} [lineWidth]
1679
+ * @param {CanvasRenderingContext2D} [context] */
1680
+ drawFillStroke(color=WHITE, outlineColor=BLACK, lineWidth=.1, context=mainContext)
1681
+ {
1682
+ if (color)
1683
+ {
1684
+ context.fillStyle = color.toString();
1685
+ context.fill();
1686
+ }
1687
+ if (outlineColor && lineWidth)
1688
+ {
1689
+ context.lineWidth = lineWidth;
1690
+ context.lineJoin = context.lineCap = 'round';
1691
+ context.strokeStyle = outlineColor.toString();
1692
+ context.stroke();
1693
+ }
1694
+ }
1695
+
1696
+ ///////////////////////////////////////////////////////////////////////////////
1697
+ // helper functions
1698
+
1699
+ /** converts a box2d vec2 to a Vector2
1700
+ * @param {Object} v */
1701
+ vec2From(v)
1702
+ {
1703
+ ASSERT(v instanceof box2d.instance.b2Vec2);
1704
+ return new Vector2(v.get_x(), v.get_y());
1705
+ }
1706
+
1707
+ /** converts a box2d vec2 pointer to a Vector2
1708
+ * @param {Object} v */
1709
+ vec2FromPointer(v)
1710
+ {
1711
+ return box2d.vec2From(box2d.instance.wrapPointer(v, box2d.instance.b2Vec2));
1712
+ }
1713
+
1714
+ /** converts a Vector2 to a box2 vec2
1715
+ * @param {Vector2} v */
1716
+ vec2dTo(v)
1717
+ {
1718
+ ASSERT(v instanceof Vector2);
1719
+ return new box2d.instance.b2Vec2(v.x, v.y);
1720
+ }
1721
+
1722
+ /** checks if a box2d object is null
1723
+ * @param {Object} o */
1724
+ isNull(o) { return !box2d.instance.getPointer(o); }
1725
+
1726
+ /** casts a box2d object to its correct type
1727
+ * @param {Object} o */
1728
+ castObjectType(o)
1729
+ {
1730
+ switch (o.GetType())
1731
+ {
1732
+ case box2d.instance.b2Shape.e_circle:
1733
+ return box2d.instance.castObject(o, box2d.instance.b2CircleShape);
1734
+ case box2d.instance.b2Shape.e_edge:
1735
+ return box2d.instance.castObject(o, box2d.instance.b2EdgeShape);
1736
+ case box2d.instance.b2Shape.e_polygon:
1737
+ return box2d.instance.castObject(o, box2d.instance.b2PolygonShape);
1738
+ case box2d.instance.b2Shape.e_chain:
1739
+ return box2d.instance.castObject(o, box2d.instance.b2ChainShape);
1740
+ case box2d.instance.e_revoluteJoint:
1741
+ return box2d.instance.castObject(o, box2d.instance.b2RevoluteJoint);
1742
+ case box2d.instance.e_prismaticJoint:
1743
+ return box2d.instance.castObject(o, box2d.instance.b2PrismaticJoint);
1744
+ case box2d.instance.e_distanceJoint:
1745
+ return box2d.instance.castObject(o, box2d.instance.b2DistanceJoint);
1746
+ case box2d.instance.e_pulleyJoint:
1747
+ return box2d.instance.castObject(o, box2d.instance.b2PulleyJoint);
1748
+ case box2d.instance.e_mouseJoint:
1749
+ return box2d.instance.castObject(o, box2d.instance.b2MouseJoint);
1750
+ case box2d.instance.e_gearJoint:
1751
+ return box2d.instance.castObject(o, box2d.instance.b2GearJoint);
1752
+ case box2d.instance.e_wheelJoint:
1753
+ return box2d.instance.castObject(o, box2d.instance.b2WheelJoint);
1754
+ case box2d.instance.e_weldJoint:
1755
+ return box2d.instance.castObject(o, box2d.instance.b2WeldJoint);
1756
+ case box2d.instance.e_frictionJoint:
1757
+ return box2d.instance.castObject(o, box2d.instance.b2FrictionJoint);
1758
+ case box2d.instance.e_ropeJoint:
1759
+ return box2d.instance.castObject(o, box2d.instance.b2RopeJoint);
1760
+ case box2d.instance.e_motorJoint:
1761
+ return box2d.instance.castObject(o, box2d.instance.b2MotorJoint);
1762
+ }
1763
+
1764
+ ASSERT(false, 'Unknown box2d object type');
1765
+ }
1766
+ }
1767
+
1768
+ ///////////////////////////////////////////////////////////////////////////////
1769
+ /** Box2d Init - Startup LittleJS engine with your callback functions
1770
+ * @param {Function|function():Promise} gameInit - Called once after the engine starts up
1771
+ * @param {Function} gameUpdate - Called every frame before objects are updated
1772
+ * @param {Function} gameUpdatePost - Called after physics and objects are updated, even when paused
1773
+ * @param {Function} gameRender - Called before objects are rendered, for drawing the background
1774
+ * @param {Function} gameRenderPost - Called after objects are rendered, useful for drawing UI
1775
+ * @param {Array<string>} [imageSources=[]] - List of images to load
1776
+ * @param {HTMLElement} [rootElement] - Root element to attach to, the document body by default */
1777
+ function box2dEngineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources, rootElement)
1778
+ {
1779
+ Box2D().then(box2dInstance=>
1780
+ {
1781
+ // create box2d object
1782
+ new Box2dPlugin(box2dInstance);
1783
+ setupDebugDraw();
1784
+
1785
+ // start littlejs
1786
+ engineAddPlugin(box2dUpdate, box2dRender);
1787
+ engineInit(gameInit, gameUpdate, gameUpdatePost, gameRender, gameRenderPost, imageSources, rootElement);
952
1788
  });
953
- }
1789
+
1790
+ // hook up box2d plugin to update and render
1791
+ function box2dUpdate()
1792
+ {
1793
+ if (!paused)
1794
+ box2d.step();
1795
+ }
1796
+ function box2dRender()
1797
+ {
1798
+ if (box2dDebug || debugPhysics && debugOverlay)
1799
+ box2d.world.DrawDebugData();
1800
+ }
1801
+
1802
+ // box2d debug drawing
1803
+ function setupDebugDraw()
1804
+ {
1805
+ // setup debug draw
1806
+ const debugDraw = new box2d.instance.JSDraw();
1807
+ const box2dColor = (c)=> new Color(c.get_r(), c.get_g(), c.get_b());
1808
+ const box2dColorPointer = (c)=>
1809
+ box2dColor(box2d.instance.wrapPointer(c, box2d.instance.b2Color));
1810
+ const getDebugColor = (color)=>box2dColorPointer(color).scale(1,.8);
1811
+ const getPointsList = (vertices, vertexCount) =>
1812
+ {
1813
+ const points = [];
1814
+ for (let i=vertexCount; i--;)
1815
+ points.push(box2d.vec2FromPointer(vertices+i*8));
1816
+ return points;
1817
+ }
1818
+ debugDraw.DrawSegment = function(point1, point2, color)
1819
+ {
1820
+ color = getDebugColor(color);
1821
+ point1 = box2d.vec2FromPointer(point1);
1822
+ point2 = box2d.vec2FromPointer(point2);
1823
+ box2d.drawLine(vec2(), 0, point1, point2, color, undefined, overlayContext);
1824
+ };
1825
+ debugDraw.DrawPolygon = function(vertices, vertexCount, color)
1826
+ {
1827
+ color = getDebugColor(color);
1828
+ const points = getPointsList(vertices, vertexCount);
1829
+ box2d.drawPoly(vec2(), 0, points, undefined, color, undefined, overlayContext);
1830
+ };
1831
+ debugDraw.DrawSolidPolygon = function(vertices, vertexCount, color)
1832
+ {
1833
+ color = getDebugColor(color);
1834
+ const points = getPointsList(vertices, vertexCount);
1835
+ box2d.drawPoly(vec2(), 0, points, color, color, undefined, overlayContext);
1836
+ };
1837
+ debugDraw.DrawCircle = function(center, radius, color)
1838
+ {
1839
+ color = getDebugColor(color);
1840
+ center = box2d.vec2FromPointer(center);
1841
+ box2d.drawCircle(center, radius, undefined, color, undefined, overlayContext);
1842
+ };
1843
+ debugDraw.DrawSolidCircle = function(center, radius, axis, color)
1844
+ {
1845
+ color = getDebugColor(color);
1846
+ center = box2d.vec2FromPointer(center);
1847
+ axis = box2d.vec2FromPointer(axis).scale(radius);
1848
+ box2d.drawCircle(center, radius, color, color, undefined, overlayContext);
1849
+ box2d.drawLine(center, 0, vec2(), axis, color, undefined, overlayContext);
1850
+ };
1851
+ debugDraw.DrawTransform = function(transform)
1852
+ {
1853
+ transform = box2d.instance.wrapPointer(transform, box2d.instance.b2Transform);
1854
+ const pos = vec2(transform.get_p());
1855
+ const angle = -transform.get_q().GetAngle();
1856
+ const p1 = vec2(1,0), c1 = rgb(.75,0,0,.8);
1857
+ const p2 = vec2(0,1), c2 = rgb(0,.75,0,.8);
1858
+ box2d.drawLine(pos, angle, vec2(), p1, c1, undefined, overlayContext);
1859
+ box2d.drawLine(pos, angle, vec2(), p2, c2, undefined, overlayContext);
1860
+ }
1861
+
1862
+ debugDraw.AppendFlags(box2d.instance.b2Draw.e_shapeBit);
1863
+ debugDraw.AppendFlags(box2d.instance.b2Draw.e_jointBit);
1864
+ //debugDraw.AppendFlags(box2d.instance.b2Draw.e_aabbBit);
1865
+ //debugDraw.AppendFlags(box2d.instance.b2Draw.e_pairBit);
1866
+ //debugDraw.AppendFlags(box2d.instance.b2Draw.e_centerOfMassBit);
1867
+ box2d.world.SetDebugDraw(debugDraw);
1868
+ }
1869
+ }