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