emeraldengine 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +1498 -1659
  2. package/dist/types/index.d.ts +4 -1
  3. package/dist/types/src/Animator.d.ts +1 -1
  4. package/dist/types/src/CollisionLayers.d.ts +2 -2
  5. package/dist/types/src/Color.d.ts +1 -0
  6. package/dist/types/src/Drawable.d.ts +1 -1
  7. package/dist/types/src/EmeraldDB.d.ts +2 -2
  8. package/dist/types/src/InstancedTexture.d.ts +17 -2
  9. package/dist/types/src/Material.d.ts +2 -2
  10. package/dist/types/src/MathUtils.d.ts +2 -1
  11. package/dist/types/src/ParticleEmitter.d.ts +1 -1
  12. package/dist/types/src/Physics.d.ts +148 -18
  13. package/dist/types/src/Scene.d.ts +1 -1
  14. package/dist/types/src/Shaders.d.ts +2 -2
  15. package/dist/types/src/Tilemap.d.ts +1 -1
  16. package/dist/types/src/UI.d.ts +1 -1
  17. package/dist/types/src/components/Behaviour.d.ts +2 -2
  18. package/dist/types/src/components/Collider.d.ts +7 -1
  19. package/dist/types/src/components/GameObject.d.ts +2 -2
  20. package/dist/types/src/components/PolygonCollider.d.ts +33 -0
  21. package/dist/types/src/components/RigidBody.d.ts +281 -8
  22. package/dist/types/src/importers/Aseprite.d.ts +2 -2
  23. package/dist/types/src/importers/ForgeLevel.d.ts +97 -0
  24. package/dist/types/src/importers/TiledMap.d.ts +1 -1
  25. package/dist/types/src/managers/EventManager.d.ts +1 -1
  26. package/dist/types/src/managers/Gamepad.d.ts +102 -0
  27. package/dist/types/src/managers/InputManager.d.ts +93 -2
  28. package/dist/types/src/managers/NetworkManager.d.ts +2 -2
  29. package/dist/types/src/managers/RenderStats.d.ts +1 -1
  30. package/dist/types/src/managers/TextureManager.d.ts +1 -1
  31. package/dist/types/src/physics/AABB.d.ts +92 -0
  32. package/dist/types/src/physics/Body.d.ts +435 -0
  33. package/dist/types/src/physics/BodyType.d.ts +6 -0
  34. package/dist/types/src/physics/BroadPhase.d.ts +210 -0
  35. package/dist/types/src/physics/Collision.d.ts +102 -0
  36. package/dist/types/src/physics/Contact.d.ts +206 -0
  37. package/dist/types/src/physics/ContactSolver.d.ts +108 -0
  38. package/dist/types/src/physics/Distance.d.ts +54 -0
  39. package/dist/types/src/physics/DistanceJoint.d.ts +90 -0
  40. package/dist/types/src/physics/Fixture.d.ts +221 -0
  41. package/dist/types/src/physics/Island.d.ts +52 -0
  42. package/dist/types/src/physics/Joint.d.ts +59 -0
  43. package/dist/types/src/physics/Math2D.d.ts +371 -0
  44. package/dist/types/src/physics/RevoluteJoint.d.ts +119 -0
  45. package/dist/types/src/physics/Settings.d.ts +22 -0
  46. package/dist/types/src/physics/Shapes.d.ts +207 -0
  47. package/dist/types/src/physics/TimeOfImpact.d.ts +22 -0
  48. package/dist/types/src/physics/World.d.ts +274 -0
  49. package/dist/types/src/physics/index.d.ts +34 -0
  50. package/index.js +6 -0
  51. package/package.json +2 -3
  52. package/src/Animator.js +1 -1
  53. package/src/CollisionLayers.js +3 -3
  54. package/src/Color.js +8 -0
  55. package/src/Drawable.js +1 -1
  56. package/src/Emerald.js +1 -1
  57. package/src/EmeraldDB.js +2 -2
  58. package/src/InstancedTexture.js +57 -9
  59. package/src/Material.js +2 -2
  60. package/src/MathUtils.js +2 -1
  61. package/src/ParticleEmitter.js +1 -1
  62. package/src/Physics.js +270 -60
  63. package/src/Scene.js +1 -1
  64. package/src/Shaders.js +20 -20
  65. package/src/Tilemap.js +1 -1
  66. package/src/UI.js +1 -1
  67. package/src/components/Behaviour.js +2 -2
  68. package/src/components/BoxCollider.js +7 -9
  69. package/src/components/BoxColliderDebug.js +3 -4
  70. package/src/components/CircleCollider.js +7 -9
  71. package/src/components/CircleColliderDebug.js +3 -2
  72. package/src/components/Collider.js +13 -3
  73. package/src/components/GameObject.js +2 -2
  74. package/src/components/PolygonCollider.js +55 -0
  75. package/src/components/RigidBody.js +441 -14
  76. package/src/importers/Aseprite.js +2 -2
  77. package/src/importers/ForgeLevel.js +581 -0
  78. package/src/importers/TiledMap.js +1 -1
  79. package/src/managers/EventManager.js +1 -1
  80. package/src/managers/Gamepad.js +126 -0
  81. package/src/managers/InputManager.js +129 -3
  82. package/src/managers/NetworkManager.js +2 -2
  83. package/src/managers/RenderStats.js +1 -1
  84. package/src/managers/TextureManager.js +1 -1
  85. package/src/physics/AABB.js +207 -0
  86. package/src/physics/Body.js +862 -0
  87. package/src/physics/BodyType.js +16 -0
  88. package/src/physics/BroadPhase.js +641 -0
  89. package/src/physics/Collision.js +534 -0
  90. package/src/physics/Contact.js +500 -0
  91. package/src/physics/ContactSolver.js +526 -0
  92. package/src/physics/Distance.js +403 -0
  93. package/src/physics/DistanceJoint.js +227 -0
  94. package/src/physics/Fixture.js +346 -0
  95. package/src/physics/Island.js +203 -0
  96. package/src/physics/Joint.js +78 -0
  97. package/src/physics/Math2D.js +573 -0
  98. package/src/physics/RevoluteJoint.js +278 -0
  99. package/src/physics/Settings.js +78 -0
  100. package/src/physics/Shapes.js +549 -0
  101. package/src/physics/TimeOfImpact.js +87 -0
  102. package/src/physics/World.js +731 -0
  103. package/src/physics/index.js +79 -0
@@ -14,11 +14,12 @@ class CircleColliderDebug {
14
14
  this.physics = this.rigidbody.physics;
15
15
  this.scale = this.physics.getScale();
16
16
  this.color = color || new Color(255, 0, 0, 255);
17
+ const body = this.rigidbody.getBody();
17
18
  this.gameObject = new GameObject(
18
19
  "CircleColliderDebug",
19
20
  new Vector3(
20
- this.rigidbody.getBody().getPosition().x * this.scale,
21
- this.rigidbody.getBody().getPosition().y * this.scale,
21
+ body.getPosition().x * this.scale,
22
+ body.getPosition().y * this.scale,
22
23
  100
23
24
  ),
24
25
  this.rigidbody.getAngle(),
@@ -23,19 +23,29 @@ class Collider {
23
23
  * @method syncDebugShape
24
24
  * @description Mirrors a transform onto the debug shape, but only if one has
25
25
  * already been created. Never forces lazy creation.
26
+ *
27
+ * The rigidbody's spawn offset is added back, because the collider sits at
28
+ * `transform + offset` while the transform tracks the renderable. Without it
29
+ * the debug shape would drift away from the collider it is supposed to be
30
+ * showing on any body built with an offset.
31
+ *
26
32
  * @param {Transform} transform - The source transform
27
33
  */
28
34
  syncDebugShape(transform) {
29
35
  if (!this._debugShape) return;
30
36
  const t = this._debugShape.gameObject.transform;
31
- t.position.x = transform.position.x;
32
- t.position.y = transform.position.y;
37
+ const offset =
38
+ this.rigidbody && typeof this.rigidbody.getOffset === "function"
39
+ ? this.rigidbody.getOffset()
40
+ : null;
41
+ t.position.x = transform.position.x + (offset ? offset.x : 0);
42
+ t.position.y = transform.position.y + (offset ? offset.y : 0);
33
43
  t.rotation = transform.rotation;
34
44
  }
35
45
 
36
46
  /**
37
47
  * @method setFilter
38
- * @description Sets the raw planck collision filter on this collider's
48
+ * @description Sets the raw collision filter on this collider's
39
49
  * fixture. Two fixtures collide only when each one's category bit is present
40
50
  * in the other's mask. Subclasses must have created `this.collider`
41
51
  * (the fixture) first.
@@ -45,7 +45,7 @@ class GameObject {
45
45
 
46
46
  /**
47
47
  * @method setScreenSpace
48
- * @description When true, the object ignores the camera (fixed on screen)
48
+ * @description When true, the object ignores the camera (fixed on screen),
49
49
  * useful for HUD/UI. Position is then in pixels from the viewport center.
50
50
  * Note: not supported for InstancedTexture-based objects.
51
51
  * @param {boolean} value
@@ -223,7 +223,7 @@ class GameObject {
223
223
  * @description Permanently tears the object down: disposes every Drawable's
224
224
  * GPU resources, destroys physics bodies, and runs Behaviour.onDestroy().
225
225
  * Use it (or Scene.remove(obj, { dispose: true })) when an object will not
226
- * be re-added plain Scene.remove() keeps GPU resources alive for re-use.
226
+ * be re-added: plain Scene.remove() keeps GPU resources alive for re-use.
227
227
  * Safe to call twice.
228
228
  */
229
229
  destroy() {
@@ -0,0 +1,55 @@
1
+ import { PolygonShape } from "../physics/index.js";
2
+ import Collider from "./Collider.js";
3
+
4
+ /**
5
+ * @class PolygonCollider
6
+ * @extends Collider
7
+ * @description A convex-polygon fixture, for collision shapes a box or
8
+ * circle can't approximate, like ramps, wedges, or arbitrary tile outlines (see
9
+ * {@link ForgeLevel}). Points outside the convex hull of what you pass are
10
+ * dropped automatically; a concave shape needs more than one collider.
11
+ * @param {Rigidbody} rigidbody - The rigidbody to attach the collider to
12
+ * @param {Array<{x:number,y:number}>} points - Local-space points, in
13
+ * physics units, in any order
14
+ * @param {number} density - The density of the collider
15
+ * @param {number} friction - The friction of the collider
16
+ * @param {number} restitution - The restitution of the collider
17
+ * @param {boolean} [isSensor=false] - Whether the collider is a sensor
18
+ * @param {GameObject} [parentObject=null] - The parent object of the collider
19
+ * @param {Object} [filter=null] - Collision filter spec, see {@link Collider#setFilter}
20
+ */
21
+ class PolygonCollider extends Collider {
22
+ constructor(
23
+ rigidbody,
24
+ points,
25
+ density,
26
+ friction,
27
+ restitution,
28
+ isSensor = false,
29
+ parentObject = null,
30
+ filter = null
31
+ ) {
32
+ super(rigidbody, isSensor, parentObject);
33
+ this.collider = rigidbody.createFixture(new PolygonShape(points), {
34
+ density: density,
35
+ friction: friction,
36
+ restitution: restitution,
37
+ isSensor: isSensor,
38
+ });
39
+ this.points = points;
40
+ this.rigidbody.setCollider(this);
41
+ this.name = "PolygonCollider" + this.id;
42
+ this._applyFilterSpec(filter);
43
+ }
44
+
45
+ /**
46
+ * @method getPoints
47
+ * @description Returns the local-space points this collider was built from
48
+ * @returns {Array<{x:number,y:number}>}
49
+ */
50
+ getPoints() {
51
+ return this.points;
52
+ }
53
+ }
54
+
55
+ export default PolygonCollider;
@@ -1,10 +1,16 @@
1
- import * as planck from "planck";
1
+ import { Vec2 } from "../physics/index.js";
2
2
  import IDManager from "../managers/IDManager.js";
3
3
  import { Vector2 } from "../Physics.js";
4
4
  import Collider from "./Collider.js";
5
5
 
6
6
  /**
7
7
  * @class RigidBody
8
+ * @description The game-facing physics body: everything you can do to a body
9
+ * (move it, push it, spin it, sleep it, query its mass) is a method on this
10
+ * class directly, in world/pixel units. There is no separate lower-level
11
+ * body object you need to fetch first. `getBody()` still returns the raw,
12
+ * physics-unit {@link Body} underneath for the rare case you need it (writing
13
+ * a custom joint, say), but nothing in ordinary use requires it.
8
14
  * @param {Physics} physics - The physics engine
9
15
  * @param {string | "static" | "dynamic" | "kinematic"} type - The type of rigidbody
10
16
  * @param {Vector2} position - The position of the rigidbody
@@ -26,7 +32,7 @@ class RigidBody {
26
32
  this.type = type;
27
33
  this.body = physics.world.createBody({
28
34
  type: type,
29
- position: new planck.Vec2(
35
+ position: new Vec2(
30
36
  (position.x + offset.x) / physics.scale,
31
37
  (position.y + offset.y) / physics.scale
32
38
  ),
@@ -37,6 +43,8 @@ class RigidBody {
37
43
  this.collider = null;
38
44
  this.id = IDManager.generateUniqueID();
39
45
  this.name = "RigidBody" + this.id;
46
+ /** Freely usable by your own code; see {@link RigidBody#getUserData}. */
47
+ this.userData = null;
40
48
 
41
49
  this.body.setUserData(this);
42
50
  }
@@ -48,13 +56,41 @@ class RigidBody {
48
56
  */
49
57
  updatePosition(position) {
50
58
  this.body.setPosition(
51
- new planck.Vec2(
59
+ new Vec2(
52
60
  (position.x + this.offset.x) / this.physics.scale,
53
61
  (position.y + this.offset.y) / this.physics.scale
54
62
  )
55
63
  );
56
64
  }
57
65
 
66
+ /**
67
+ * @method setPosition
68
+ * @description Alias of {@link RigidBody#updatePosition}, named to match
69
+ * `setRotation`/`setTransform`.
70
+ * @param {Vector2} position - The new position, in world (pixel) units
71
+ */
72
+ setPosition(position) {
73
+ this.updatePosition(position);
74
+ }
75
+
76
+ /**
77
+ * @method setTransform
78
+ * @description Teleports the body to a new position and angle in one move
79
+ * (no velocity implied), and wakes it so contacts at the new spot are seen
80
+ * on the very next step.
81
+ * @param {Vector2} position - World (pixel) units
82
+ * @param {number} angle - Radians
83
+ */
84
+ setTransform(position, angle) {
85
+ this.body.setTransform(
86
+ new Vec2(
87
+ (position.x + this.offset.x) / this.physics.scale,
88
+ (position.y + this.offset.y) / this.physics.scale
89
+ ),
90
+ angle
91
+ );
92
+ }
93
+
58
94
  /**
59
95
  * @method getWorldX
60
96
  * @description The single source of truth for body(physics) -> world(pixel)
@@ -111,6 +147,29 @@ class RigidBody {
111
147
  }
112
148
  }
113
149
 
150
+ /**
151
+ * @method createFixture
152
+ * @description Attaches a shape directly, bypassing the Collider component
153
+ * classes ({@link BoxCollider}, {@link CircleCollider}, {@link PolygonCollider})
154
+ * that normally build one for you. `shape` is a physics-unit shape (e.g. from
155
+ * `Box()`/`Circle()`/`new PolygonShape()`), not pixels.
156
+ * @param {Shape} shape
157
+ * @param {Object|number} [def] - `{ density, friction, restitution, isSensor, ... }`
158
+ * @returns {Fixture}
159
+ */
160
+ createFixture(shape, def = {}) {
161
+ return this.body.createFixture(shape, def);
162
+ }
163
+
164
+ /**
165
+ * @method destroyFixture
166
+ * @description Removes a fixture created with {@link RigidBody#createFixture}.
167
+ * @param {Fixture} fixture
168
+ */
169
+ destroyFixture(fixture) {
170
+ this.body.destroyFixture(fixture);
171
+ }
172
+
114
173
  /**
115
174
  * @method setRotation
116
175
  * @description Sets the rotation of the rigidbody
@@ -129,7 +188,7 @@ class RigidBody {
129
188
  */
130
189
  setLinearVelocity(vx, vy) {
131
190
  this.body.setLinearVelocity(
132
- new planck.Vec2(vx / this.physics.scale, vy / this.physics.scale)
191
+ new Vec2(vx / this.physics.scale, vy / this.physics.scale)
133
192
  );
134
193
  }
135
194
 
@@ -143,20 +202,115 @@ class RigidBody {
143
202
  return { x: v.x * this.physics.scale, y: v.y * this.physics.scale };
144
203
  }
145
204
 
205
+ /**
206
+ * @method getLinearVelocityFromWorldPoint
207
+ * @description The velocity (world units/sec) of the material point of this
208
+ * body currently at `worldPoint`: linear velocity plus the contribution
209
+ * from spin. Useful for e.g. where a spinning platform's edge is moving.
210
+ * @param {Object} worldPoint - World (pixel) units
211
+ * @returns {Vector2}
212
+ */
213
+ getLinearVelocityFromWorldPoint(worldPoint) {
214
+ const scale = this.physics.scale;
215
+ const v = this.body.getLinearVelocityFromWorldPoint(
216
+ new Vec2(worldPoint.x / scale, worldPoint.y / scale)
217
+ );
218
+ return new Vector2(v.x * scale, v.y * scale);
219
+ }
220
+
221
+ /**
222
+ * @method setAngularVelocity
223
+ * @description Sets the body's spin, in radians per second.
224
+ * @param {number} omega
225
+ */
226
+ setAngularVelocity(omega) {
227
+ this.body.setAngularVelocity(omega);
228
+ }
229
+
230
+ /**
231
+ * @method getAngularVelocity
232
+ * @description Returns the body's spin, in radians per second.
233
+ * @returns {number}
234
+ */
235
+ getAngularVelocity() {
236
+ return this.body.getAngularVelocity();
237
+ }
238
+
146
239
  /**
147
240
  * @method applyImpulse
148
- * @description Applies a linear impulse (world units) at the body's center.
241
+ * @description Applies an instantaneous change in momentum (world units).
242
+ * This is what a jump or a knockback should use, since (unlike a force) it
243
+ * takes effect immediately rather than accumulating over the step.
149
244
  * @param {number} ix
150
245
  * @param {number} iy
246
+ * @param {Object} [point] - World (pixel) point to apply it at; defaults to
247
+ * the center of mass (no torque)
248
+ * @param {boolean} [wake=true]
151
249
  */
152
- applyImpulse(ix, iy) {
250
+ applyImpulse(ix, iy, point = null, wake = true) {
251
+ const scale = this.physics.scale;
153
252
  this.body.applyLinearImpulse(
154
- new planck.Vec2(ix / this.physics.scale, iy / this.physics.scale),
155
- this.body.getWorldCenter(),
156
- true
253
+ new Vec2(ix / scale, iy / scale),
254
+ point
255
+ ? new Vec2(point.x / scale, point.y / scale)
256
+ : this.body.getWorldCenter(),
257
+ wake
258
+ );
259
+ }
260
+
261
+ /**
262
+ * @method applyAngularImpulse
263
+ * @description Applies an instantaneous change in angular momentum.
264
+ * @param {number} impulse
265
+ * @param {boolean} [wake=true]
266
+ */
267
+ applyAngularImpulse(impulse, wake = true) {
268
+ this.body.applyAngularImpulse(impulse, wake);
269
+ }
270
+
271
+ /**
272
+ * @method applyForce
273
+ * @description Applies a force (world units) at a world point. Forces
274
+ * accumulate and are cleared at the end of every step, so this belongs in
275
+ * your update loop, unlike an impulse.
276
+ * @param {number} fx
277
+ * @param {number} fy
278
+ * @param {Object} [point] - World (pixel) point; defaults to the center of
279
+ * mass (no torque)
280
+ * @param {boolean} [wake=true]
281
+ */
282
+ applyForce(fx, fy, point = null, wake = true) {
283
+ const scale = this.physics.scale;
284
+ this.body.applyForce(
285
+ new Vec2(fx / scale, fy / scale),
286
+ point ? new Vec2(point.x / scale, point.y / scale) : null,
287
+ wake
157
288
  );
158
289
  }
159
290
 
291
+ /**
292
+ * @method applyForceToCenter
293
+ * @description Applies a force (world units) at the center of mass: no
294
+ * torque, unlike {@link RigidBody#applyForce} with a point.
295
+ * @param {number} fx
296
+ * @param {number} fy
297
+ * @param {boolean} [wake=true]
298
+ */
299
+ applyForceToCenter(fx, fy, wake = true) {
300
+ const scale = this.physics.scale;
301
+ this.body.applyForceToCenter(new Vec2(fx / scale, fy / scale), wake);
302
+ }
303
+
304
+ /**
305
+ * @method applyTorque
306
+ * @description Applies a torque about the center of mass.
307
+ * @param {number} torque
308
+ * @param {boolean} [wake=true]
309
+ */
310
+ applyTorque(torque, wake = true) {
311
+ this.body.applyTorque(torque, wake);
312
+ }
313
+
160
314
  /**
161
315
  * @method setAwake
162
316
  * @description Wakes or sleeps the body.
@@ -166,13 +320,74 @@ class RigidBody {
166
320
  this.body.setAwake(awake);
167
321
  }
168
322
 
323
+ /**
324
+ * @method isAwake
325
+ * @description Whether the body is currently simulated.
326
+ * @returns {boolean}
327
+ */
328
+ isAwake() {
329
+ return this.body.isAwake();
330
+ }
331
+
332
+ /**
333
+ * @method setSleepingAllowed
334
+ * @description Allows or forbids this body from ever sleeping.
335
+ * @param {boolean} flag
336
+ */
337
+ setSleepingAllowed(flag) {
338
+ this.body.setSleepingAllowed(flag);
339
+ }
340
+
341
+ /**
342
+ * @method isSleepingAllowed
343
+ * @returns {boolean}
344
+ */
345
+ isSleepingAllowed() {
346
+ return this.body.isSleepingAllowed();
347
+ }
348
+
349
+ /**
350
+ * @method setActive
351
+ * @description Adds or removes the body from collision detection without
352
+ * destroying it.
353
+ * @param {boolean} flag
354
+ */
355
+ setActive(flag) {
356
+ this.body.setActive(flag);
357
+ }
358
+
359
+ /**
360
+ * @method isActive
361
+ * @returns {boolean}
362
+ */
363
+ isActive() {
364
+ return this.body.isActive();
365
+ }
366
+
367
+ /**
368
+ * @method setFixedRotation
369
+ * @description Locks or unlocks the body's rotation at runtime.
370
+ * @param {boolean} flag
371
+ */
372
+ setFixedRotation(flag) {
373
+ this.body.setFixedRotation(flag);
374
+ }
375
+
376
+ /**
377
+ * @method isFixedRotation
378
+ * @returns {boolean}
379
+ */
380
+ isFixedRotation() {
381
+ return this.body.isFixedRotation();
382
+ }
383
+
169
384
  /**
170
385
  * @method setContinuous
171
386
  * @description Enables continuous collision detection (CCD) for this body by
172
387
  * marking it a "bullet". Fast-moving bodies (e.g. a dash, a projectile, a
173
388
  * player falling at high speed) otherwise sweep so far in a single fixed step
174
- * that they tunnel straight through thin static geometry; with CCD on, planck
175
- * solves the swept path against static bodies so the body stops at the wall
389
+ * that they tunnel straight through thin static geometry; with CCD on, the
390
+ * engine sweeps the body against static geometry so it stops at the wall
176
391
  * instead of teleporting past it. Costs more per step, so reserve it for the
177
392
  * handful of bodies that actually move fast.
178
393
  * @param {boolean} [enabled=true]
@@ -192,6 +407,202 @@ class RigidBody {
192
407
  return this.body.isBullet();
193
408
  }
194
409
 
410
+ /**
411
+ * @method setLinearDamping
412
+ * @description Sets the drag applied to linear motion each step.
413
+ * @param {number} damping
414
+ */
415
+ setLinearDamping(damping) {
416
+ this.body.setLinearDamping(damping);
417
+ }
418
+
419
+ /**
420
+ * @method getLinearDamping
421
+ * @returns {number}
422
+ */
423
+ getLinearDamping() {
424
+ return this.body.getLinearDamping();
425
+ }
426
+
427
+ /**
428
+ * @method setAngularDamping
429
+ * @description Sets the drag applied to rotation each step.
430
+ * @param {number} damping
431
+ */
432
+ setAngularDamping(damping) {
433
+ this.body.setAngularDamping(damping);
434
+ }
435
+
436
+ /**
437
+ * @method getAngularDamping
438
+ * @returns {number}
439
+ */
440
+ getAngularDamping() {
441
+ return this.body.getAngularDamping();
442
+ }
443
+
444
+ /**
445
+ * @method setGravityScale
446
+ * @description Scales how strongly gravity pulls on this body (0 disables
447
+ * it, 2 makes it twice as heavy-feeling).
448
+ * @param {number} scale
449
+ */
450
+ setGravityScale(scale) {
451
+ this.body.setGravityScale(scale);
452
+ }
453
+
454
+ /**
455
+ * @method getGravityScale
456
+ * @returns {number}
457
+ */
458
+ getGravityScale() {
459
+ return this.body.getGravityScale();
460
+ }
461
+
462
+ /**
463
+ * @method getMass
464
+ * @description Returns the body's mass, in physics units (derived from its
465
+ * fixtures' shapes and densities, unaffected by the pixel scale).
466
+ * @returns {number}
467
+ */
468
+ getMass() {
469
+ return this.body.getMass();
470
+ }
471
+
472
+ /**
473
+ * @method getInertia
474
+ * @description Returns the body's rotational inertia about its center of
475
+ * mass, in physics units.
476
+ * @returns {number}
477
+ */
478
+ getInertia() {
479
+ return this.body.getInertia();
480
+ }
481
+
482
+ /**
483
+ * @method resetMassData
484
+ * @description Re-derives mass/center/inertia from the body's current
485
+ * fixtures. Call this after changing a fixture's density at runtime.
486
+ */
487
+ resetMassData() {
488
+ this.body.resetMassData();
489
+ }
490
+
491
+ /**
492
+ * @method setMassData
493
+ * @description Overrides the computed mass properties directly.
494
+ * @param {Object} massData - `{ mass, center, I }`; `center` is a world
495
+ * (pixel) offset from the body's origin
496
+ */
497
+ setMassData(massData) {
498
+ const scale = this.physics.scale;
499
+ const center = massData.center || { x: 0, y: 0 };
500
+ this.body.setMassData({
501
+ mass: massData.mass,
502
+ center: new Vec2(center.x / scale, center.y / scale),
503
+ I: massData.I,
504
+ });
505
+ }
506
+
507
+ /**
508
+ * @method getWorldPoint
509
+ * @description Converts a point local to this body into world (pixel) space.
510
+ * @param {Object} localPoint - World-unit offset from the body's origin
511
+ * @returns {Vector2}
512
+ */
513
+ getWorldPoint(localPoint) {
514
+ const scale = this.physics.scale;
515
+ const p = this.body.getWorldPoint(
516
+ new Vec2(localPoint.x / scale, localPoint.y / scale)
517
+ );
518
+ return new Vector2(p.x * scale, p.y * scale);
519
+ }
520
+
521
+ /**
522
+ * @method getLocalPoint
523
+ * @description Converts a world (pixel) point into this body's local frame.
524
+ * @param {Object} worldPoint - World (pixel) units
525
+ * @returns {Vector2}
526
+ */
527
+ getLocalPoint(worldPoint) {
528
+ const scale = this.physics.scale;
529
+ const p = this.body.getLocalPoint(
530
+ new Vec2(worldPoint.x / scale, worldPoint.y / scale)
531
+ );
532
+ return new Vector2(p.x * scale, p.y * scale);
533
+ }
534
+
535
+ /**
536
+ * @method getWorldVector
537
+ * @description Rotates a local direction (not a point, unaffected by the
538
+ * body's position) into world space.
539
+ * @param {Object} localVector
540
+ * @returns {Vector2}
541
+ */
542
+ getWorldVector(localVector) {
543
+ const scale = this.physics.scale;
544
+ const v = this.body.getWorldVector(
545
+ new Vec2(localVector.x / scale, localVector.y / scale)
546
+ );
547
+ return new Vector2(v.x * scale, v.y * scale);
548
+ }
549
+
550
+ /**
551
+ * @method getLocalVector
552
+ * @description Rotates a world direction into this body's local frame.
553
+ * @param {Object} worldVector
554
+ * @returns {Vector2}
555
+ */
556
+ getLocalVector(worldVector) {
557
+ const scale = this.physics.scale;
558
+ const v = this.body.getLocalVector(
559
+ new Vec2(worldVector.x / scale, worldVector.y / scale)
560
+ );
561
+ return new Vector2(v.x * scale, v.y * scale);
562
+ }
563
+
564
+ /**
565
+ * @method getContactList
566
+ * @description Returns the raw physics contacts this body currently takes
567
+ * part in. Each contact's `fixtureA`/`fixtureB` point at physics-unit
568
+ * fixtures/bodies, not RigidBody wrappers. Walk `fixture.body.getUserData()`
569
+ * to get back to the owning RigidBody.
570
+ * @returns {Array<Contact>}
571
+ */
572
+ getContactList() {
573
+ return this.body.getContactList();
574
+ }
575
+
576
+ /**
577
+ * @method getWorld
578
+ * @description Returns the raw physics {@link World} this body lives in.
579
+ * @returns {World}
580
+ */
581
+ getWorld() {
582
+ return this.body.getWorld();
583
+ }
584
+
585
+ /**
586
+ * @method getUserData
587
+ * @description Returns whatever you last passed to
588
+ * {@link RigidBody#setUserData}. This is separate from the underlying
589
+ * physics body's own userData slot, which the engine itself uses internally
590
+ * to route collisions back to this RigidBody, so setting it here can never
591
+ * break that.
592
+ * @returns {*}
593
+ */
594
+ getUserData() {
595
+ return this.userData;
596
+ }
597
+
598
+ /**
599
+ * @method setUserData
600
+ * @param {*} data
601
+ */
602
+ setUserData(data) {
603
+ this.userData = data;
604
+ }
605
+
195
606
  /**
196
607
  * @method getPosition
197
608
  * @description Returns the live world-space position of the body (kept in sync
@@ -232,8 +643,11 @@ class RigidBody {
232
643
 
233
644
  /**
234
645
  * @method getBody
235
- * @description Returns the body of the rigidbody
236
- * @returns {planck.Body} - The body of the rigidbody
646
+ * @description Returns the underlying, physics-unit {@link Body}. Nothing in
647
+ * ordinary use needs this: every common operation is a method on RigidBody
648
+ * itself, in world/pixel units, but it's here for advanced cases (writing a
649
+ * custom joint or solver hook) that need the raw physics object.
650
+ * @returns {Body} - The body of the rigidbody
237
651
  */
238
652
  getBody() {
239
653
  return this.body;
@@ -263,7 +677,7 @@ class RigidBody {
263
677
  */
264
678
  detachCollider(collider) {
265
679
  if (collider && collider.getCollider()) {
266
- this.body.destroyFixture(collider.getCollider());
680
+ this.destroyFixture(collider.getCollider());
267
681
  this.collider = null;
268
682
  }
269
683
  }
@@ -285,6 +699,19 @@ class RigidBody {
285
699
  getType() {
286
700
  return this.type;
287
701
  }
702
+
703
+ /**
704
+ * @method setType
705
+ * @description Changes the body type at runtime (e.g. turning a kinematic
706
+ * moving platform into a dynamic one when it breaks apart). Resets
707
+ * velocities and re-derives mass; existing contacts are dropped so they
708
+ * rebuild against the new type.
709
+ * @param {string | "static" | "dynamic" | "kinematic"} type
710
+ */
711
+ setType(type) {
712
+ this.type = type;
713
+ this.body.setType(type);
714
+ }
288
715
  }
289
716
 
290
717
  export default RigidBody;
@@ -3,7 +3,7 @@
3
3
  * @description Imports sprite-sheet metadata exported from Aseprite
4
4
  * (File ▸ Export Sprite Sheet, with "JSON Data" on) and turns its frame tags
5
5
  * into engine animation clips. Works with both the Hash and Array JSON layouts.
6
- * Pure parsing pass it the already-parsed JSON object.
6
+ * Pure parsing: pass it the already-parsed JSON object.
7
7
  *
8
8
  * Assumes the sheet is a uniform grid in frame order (the common case), so the
9
9
  * frame indices line up with the engine's Texture/Animator frame numbering.
@@ -86,7 +86,7 @@ class Aseprite {
86
86
  * @method toClips
87
87
  * @description Builds clip descriptors from the sheet's frame tags. Each clip
88
88
  * is `{ name, frames, speed }` where `frames` are frame indices and `speed`
89
- * is the average frame duration (ms) the per-clip speed the Animator uses.
89
+ * is the average frame duration (ms), the per-clip speed the Animator uses.
90
90
  * If the sheet has no tags, a single "default" clip spanning all frames is
91
91
  * returned.
92
92
  * @param {Object} sheet - Parsed Aseprite JSON