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