incanto 0.74.0 → 0.76.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.
- package/bin/incanto-multiplay.mjs +52 -4
- package/dist/2d.d.ts +83 -7
- package/dist/2d.js +4 -3
- package/dist/3d.d.ts +861 -167
- package/dist/3d.js +6 -6
- package/dist/{agent8-CHTT4unP.js → agent8-B-bh3n0X.js} +1 -1
- package/dist/{audio-player-BOrk4eQx.d.ts → audio-player-BRo2uvG6.d.ts} +23 -2
- package/dist/{behavior-J1cmVah0.d.ts → behavior-B_245qRy.d.ts} +15 -1
- package/dist/{create-game-ssvGvP7W.js → create-game-BMIPKrQW.js} +84 -8
- package/dist/{create-game-BAiA-FjP.js → create-game-Dai5vfVu.js} +80 -11
- package/dist/debug.d.ts +1 -1
- package/dist/{editor-switch-CFU9mCec.d.ts → editor-switch-Bzt0GzVp.d.ts} +13 -3
- package/dist/editor.js +1600 -1529
- package/dist/env.d.ts +1 -1
- package/dist/{environment-presets-BkqlWewf.js → environment-presets-e_9YCnGu.js} +2361 -104
- package/dist/{gameplay-Bxe1sMVT.js → gameplay-Bfff_beb.js} +10919 -6798
- package/dist/gameplay.d.ts +573 -31
- package/dist/gameplay.js +3 -2
- package/dist/index.d.ts +150 -6
- package/dist/index.js +6 -6
- package/dist/{loader-BAeWMYfF.d.ts → loader-CbkVdXL8.d.ts} +2 -2
- package/dist/net.d.ts +2 -2
- package/dist/net.js +2 -2
- package/dist/{physics-2d-C-SfvjzH.js → physics-2d-Tuf5LAyq.js} +250 -31
- package/dist/{physics-3d-rOzeg890.js → physics-3d-C5MOujfQ.js} +673 -51
- package/dist/{picking-BMV34Pjl.js → picking-DIwE48sU.js} +10 -7
- package/dist/{save-slots-CEUGeLuB.js → pose-ByFB_J3O.js} +217 -11
- package/dist/react.d.ts +2 -2
- package/dist/react.js +1 -1
- package/dist/{register-DRQyZKGU.js → register-7E3P1gz6.js} +191 -430
- package/dist/{register-CjociOtt.js → register-B768Qedj.js} +596 -2
- package/dist/{replay-B6BbgGM_.js → replay-DrqQ6dZj.js} +133 -18
- package/dist/{replay-wZK5Ok5c.d.ts → replay-W5nCw_cU.d.ts} +21 -2
- package/dist/{rng-Bb-IutXB.d.ts → rng-BsXZg3D6.d.ts} +7 -0
- package/dist/{split-screen-BxZ5rBe7.js → split-screen-B-dIcQsQ.js} +10 -5
- package/dist/{split-screen-D6sx8z3Z.d.ts → split-screen-CiLJf2-C.d.ts} +3 -3
- package/dist/{sprite-animation-Bz3DHD_4.js → sprite-animation-SQa5gIu2.js} +8 -6
- package/dist/{src-CqtvYtSN.js → src-D823V07k.js} +1 -1
- package/dist/{test-CEl0y2Sw.js → test-CVbxnXlv.js} +487 -82
- package/dist/test.d.ts +90 -12
- package/dist/test.js +2 -2
- package/dist/vite.js +34 -5
- package/editor/assets/{agent8-BlzturGi.js → agent8-DA-I5qhv.js} +1 -1
- package/editor/assets/{debug-BMUi8usj.js → debug-99tEPoGm.js} +1 -1
- package/editor/assets/index-Db74LZY-.js +11579 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +1639 -280
- package/skills/incanto-3d-character.md +383 -20
- package/skills/incanto-audio.md +24 -2
- package/skills/incanto-building-2d-games.md +21 -2
- package/skills/incanto-building-3d-games.md +38 -3
- package/skills/incanto-editor.md +29 -8
- package/skills/incanto-environment.md +257 -14
- package/skills/incanto-game-feel.md +60 -1
- package/skills/incanto-gameplay-behaviors.md +668 -20
- package/skills/incanto-hud.md +116 -0
- package/skills/incanto-multiplayer.md +141 -2
- package/skills/incanto-node-reference.md +398 -16
- package/skills/incanto-performance.md +32 -0
- package/skills/incanto-physics-and-input.md +241 -13
- package/skills/incanto-playtesting.md +2 -2
- package/skills/incanto-save-slots.md +61 -2
- package/skills/incanto-scene-json-authoring.md +13 -0
- package/skills/incanto-verifying-your-game.md +86 -3
- package/templates-app/beacon-isle-3d/PROJECT/Status.md +12 -0
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/beacon-isle-3d/src/game.scene.json +157 -3
- package/templates-app/molehill-2d/PROJECT/Status.md +7 -0
- package/templates-app/molehill-2d/package.json +1 -1
- package/templates-app/molehill-2d/src/game.scene.json +129 -2
- package/templates-app/platformer-2d/PROJECT/Status.md +7 -0
- package/templates-app/platformer-2d/docs/project-2d-rules.md +3 -2
- package/templates-app/platformer-2d/package.json +1 -1
- package/templates-app/platformer-2d/src/behaviors.ts +40 -26
- package/templates-app/platformer-2d/src/game.scene.json +144 -2
- package/templates-app/platformer-2d/verify.ts +18 -1
- package/templates-app/star-survivor/PROJECT/Status.md +7 -0
- package/templates-app/star-survivor/package.json +1 -1
- package/templates-app/star-survivor/src/behaviors.ts +9 -8
- package/templates-app/star-survivor/src/game.scene.json +142 -4
- package/templates-app/star-survivor/verify.ts +13 -0
- package/templates-app/tps-3d/PROJECT/Status.md +6 -0
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/tps-3d/src/game.scene.json +71 -10
- package/templates-app/village-quest-3d/PROJECT/Status.md +7 -0
- package/templates-app/village-quest-3d/package.json +1 -1
- package/templates-app/village-quest-3d/src/behaviors.ts +8 -16
- package/templates-app/village-quest-3d/src/grove.scene.json +121 -5
- package/templates-app/village-quest-3d/src/village.scene.json +126 -2
- package/templates-app/village-quest-3d/verify.ts +52 -2
- package/editor/assets/index-CrUCQoaB.js +0 -11046
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { o as __exportAll } from "./json-CfTjpvW8.js";
|
|
2
|
-
import {
|
|
2
|
+
import { et as diagnose } from "./pose-ByFB_J3O.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { _ as
|
|
4
|
+
import { _ as Node2D, f as Area2D, g as StaticBody2D, h as RigidBody2D, m as PhysicsBody2D, p as CharacterBody2D, v as validateCollider2D } from "./gameplay-Bfff_beb.js";
|
|
5
5
|
import { n as registerDebugSource } from "./debug-draw-BM3DsvtT.js";
|
|
6
6
|
import { n as jointContacts, t as withoutRapierInitNoise } from "./quiet-rapier-C6fW4zcW.js";
|
|
7
|
+
import { p as Joint2D } from "./register-7E3P1gz6.js";
|
|
7
8
|
//#region src/2d/physics/physics-2d.ts
|
|
8
9
|
var physics_2d_exports = /* @__PURE__ */ __exportAll({
|
|
9
10
|
Physics2D: () => Physics2D,
|
|
@@ -98,13 +99,18 @@ var Physics2D = class {
|
|
|
98
99
|
for (const [node, e] of this.entries) {
|
|
99
100
|
const t = e.body.translation();
|
|
100
101
|
if (!e.body.isFixed() && !(node instanceof Area2D)) {
|
|
101
|
-
const [ox, oy] =
|
|
102
|
-
|
|
102
|
+
const [ox, oy, oa] = parentPose2D(node);
|
|
103
|
+
const dx = t.x * SCALE - ox;
|
|
104
|
+
const dy = t.y * SCALE - oy;
|
|
105
|
+
if (oa !== 0) {
|
|
106
|
+
const a = -oa * DEG;
|
|
107
|
+
node.position = [dx * Math.cos(a) - dy * Math.sin(a), dx * Math.sin(a) + dy * Math.cos(a)];
|
|
108
|
+
} else node.position = [dx, dy];
|
|
103
109
|
}
|
|
104
110
|
if (node instanceof RigidBody2D) {
|
|
105
111
|
if (!node.fixedRotation) {
|
|
106
|
-
node.rotation = e.body.rotation() / DEG;
|
|
107
|
-
e.lastRot =
|
|
112
|
+
node.rotation = e.body.rotation() / DEG - parentPose2D(node)[2];
|
|
113
|
+
e.lastRot = e.body.rotation() / DEG;
|
|
108
114
|
}
|
|
109
115
|
const lv = e.body.linvel();
|
|
110
116
|
node.linearVelocity = [lv.x * SCALE, lv.y * SCALE];
|
|
@@ -121,6 +127,7 @@ var Physics2D = class {
|
|
|
121
127
|
const a = this.byColliderHandle.get(h1);
|
|
122
128
|
const b = this.byColliderHandle.get(h2);
|
|
123
129
|
if (!a || !b) return;
|
|
130
|
+
if (!a.enabled || !b.enabled) return;
|
|
124
131
|
const sig = started ? "triggerEnter" : "triggerExit";
|
|
125
132
|
if (started === (this.overlaps.get(a)?.has(b) ?? false)) return;
|
|
126
133
|
this.trackOverlap(a, b, started);
|
|
@@ -149,6 +156,7 @@ var Physics2D = class {
|
|
|
149
156
|
* you can carry you.
|
|
150
157
|
*/
|
|
151
158
|
canCarry(rider, candidate) {
|
|
159
|
+
if (this.jointed.get(rider)?.has(candidate)) return false;
|
|
152
160
|
let at = candidate;
|
|
153
161
|
for (let depth = 0; depth < 8 && at; depth++) {
|
|
154
162
|
if (at === rider) return false;
|
|
@@ -171,6 +179,14 @@ var Physics2D = class {
|
|
|
171
179
|
const [gx, gy] = worldPosition2D(e.ground);
|
|
172
180
|
carryX = gx - (e.groundX ?? gx);
|
|
173
181
|
carryY = gy - (e.groundY ?? gy);
|
|
182
|
+
const gb = this.entries.get(e.ground);
|
|
183
|
+
if (gb && e.groundRot !== void 0 && e.riderX !== void 0 && e.riderY !== void 0) {
|
|
184
|
+
const d = gb.body.rotation() - e.groundRot;
|
|
185
|
+
const ox = e.riderX - (e.groundX ?? gx);
|
|
186
|
+
const oy = e.riderY - (e.groundY ?? gy);
|
|
187
|
+
carryX += ox * Math.cos(d) - oy * Math.sin(d) - ox;
|
|
188
|
+
carryY += ox * Math.sin(d) + oy * Math.cos(d) - oy;
|
|
189
|
+
}
|
|
174
190
|
} else e.ground = null;
|
|
175
191
|
const vyTerm = carryY < -.1 && node.isOnFloor?.() === true && vy > 0 ? 0 : vy * this.lastDt;
|
|
176
192
|
const desired = {
|
|
@@ -186,7 +202,15 @@ var Physics2D = class {
|
|
|
186
202
|
}
|
|
187
203
|
return true;
|
|
188
204
|
};
|
|
189
|
-
const
|
|
205
|
+
const noContact = this.jointed.get(node);
|
|
206
|
+
const filter = (c) => {
|
|
207
|
+
const other = this.byColliderHandle.get(c.handle);
|
|
208
|
+
if (!node.collideWithCharacters && other instanceof CharacterBody2D) return false;
|
|
209
|
+
if (other && noContact?.get(other) === false) return false;
|
|
210
|
+
return predicate(c);
|
|
211
|
+
};
|
|
212
|
+
this.kcc.setApplyImpulsesToDynamicBodies(node.pushes > 0);
|
|
213
|
+
if (node.pushes > 0) this.kcc.setCharacterMass(node.pushes);
|
|
190
214
|
this.kcc.computeColliderMovement(e.collider, desired, this.R.QueryFilterFlags.EXCLUDE_SENSORS, void 0, filter);
|
|
191
215
|
const corrected = this.kcc.computedMovement();
|
|
192
216
|
const t = e.body.translation();
|
|
@@ -214,6 +238,9 @@ var Physics2D = class {
|
|
|
214
238
|
const [gx, gy] = worldPosition2D(hit.node);
|
|
215
239
|
e.groundX = gx;
|
|
216
240
|
e.groundY = gy;
|
|
241
|
+
e.groundRot = this.entries.get(hit.node)?.body.rotation();
|
|
242
|
+
e.riderX = footX;
|
|
243
|
+
e.riderY = (t.y + corrected.y) * SCALE + charHalfHeight(node);
|
|
217
244
|
} else e.ground = null;
|
|
218
245
|
} else e.ground = null;
|
|
219
246
|
}
|
|
@@ -290,14 +317,28 @@ var Physics2D = class {
|
|
|
290
317
|
for (const [joint, impulse] of this.joints) if (!this.jointSet.has(joint)) {
|
|
291
318
|
this.world.removeImpulseJoint(impulse, true);
|
|
292
319
|
this.joints.delete(joint);
|
|
320
|
+
const pair = this.jointBodies.get(joint);
|
|
321
|
+
if (pair) this.unlinkJointed(pair[0], pair[1]);
|
|
322
|
+
this.jointBodies.delete(joint);
|
|
323
|
+
this.hingeState.delete(joint);
|
|
324
|
+
this.tetherState.delete(joint);
|
|
293
325
|
}
|
|
294
326
|
}
|
|
295
327
|
for (const joint of this.jointSet) {
|
|
296
|
-
if (this.joints.has(joint)) continue;
|
|
328
|
+
if (this.joints.has(joint) || this.refusedJoints.has(joint)) continue;
|
|
297
329
|
const { a, b } = joint._resolveBodies();
|
|
298
330
|
const ea = this.entries.get(a);
|
|
299
331
|
const eb = this.entries.get(b);
|
|
300
332
|
if (!ea || !eb) continue;
|
|
333
|
+
if (joint.type === "prismatic") {
|
|
334
|
+
let off = Math.abs(ea.body.rotation() - eb.body.rotation()) / DEG;
|
|
335
|
+
off = Math.abs((off + 180) % 360 - 180);
|
|
336
|
+
if (off > .5) {
|
|
337
|
+
this.refusedJoints.add(joint);
|
|
338
|
+
diagnose(this.engine, "error", `[incanto] Joint2D '${joint.getPath()}': its two bodies disagree about the slide axis by ${off.toFixed(1)}° — '${a.name}' and '${b.name}' are authored with different rotations, and Rapier reads the axis in BOTH bodies' local frames. Give both bodies the same rotation. The joint was not made.`);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
301
342
|
const a1 = {
|
|
302
343
|
x: (joint.anchor[0] ?? 0) / SCALE,
|
|
303
344
|
y: (joint.anchor[1] ?? 0) / SCALE
|
|
@@ -316,10 +357,63 @@ var Physics2D = class {
|
|
|
316
357
|
if (joint.type === "fixed") data = R.JointData.fixed(a1, 0, a2, 0);
|
|
317
358
|
else if (joint.type === "rope") data = R.JointData.rope(length, a1, a2);
|
|
318
359
|
else if (joint.type === "spring") data = R.JointData.spring(length, joint.stiffness, joint.damping, a1, a2);
|
|
319
|
-
else
|
|
360
|
+
else if (joint.type === "prismatic") {
|
|
361
|
+
const ax = joint.axis[0] ?? 0;
|
|
362
|
+
const ay = joint.axis[1] ?? 0;
|
|
363
|
+
const n = Math.hypot(ax, ay) || 1;
|
|
364
|
+
data = R.JointData.prismatic(a1, a2, {
|
|
365
|
+
x: ax / n,
|
|
366
|
+
y: ay / n
|
|
367
|
+
});
|
|
368
|
+
} else data = R.JointData.revolute(a1, a2);
|
|
320
369
|
const made = this.world.createImpulseJoint(data, ea.body, eb.body, true);
|
|
321
|
-
|
|
370
|
+
if (joint.type === "revolute" || joint.type === "prismatic") this.applyHinge(joint, made, true);
|
|
371
|
+
const contacts = jointContacts(joint.type, joint.collide);
|
|
372
|
+
made.setContactsEnabled(contacts);
|
|
322
373
|
this.joints.set(joint, made);
|
|
374
|
+
this.jointBodies.set(joint, [a, b]);
|
|
375
|
+
this.linkJointed(a, b, contacts);
|
|
376
|
+
if (joint.type === "rope" || joint.type === "spring") this.tetherState.set(joint, this.tetherKey(joint));
|
|
377
|
+
}
|
|
378
|
+
for (const [joint, made] of this.joints) {
|
|
379
|
+
if (joint.type !== "rope" && joint.type !== "spring") continue;
|
|
380
|
+
const key = this.tetherKey(joint);
|
|
381
|
+
if (this.tetherState.get(joint) === key) continue;
|
|
382
|
+
this.world.removeImpulseJoint(made, true);
|
|
383
|
+
this.joints.delete(joint);
|
|
384
|
+
const pair = this.jointBodies.get(joint);
|
|
385
|
+
if (pair) this.unlinkJointed(pair[0], pair[1]);
|
|
386
|
+
this.jointBodies.delete(joint);
|
|
387
|
+
this.tetherState.delete(joint);
|
|
388
|
+
}
|
|
389
|
+
for (const [joint, made] of this.joints) {
|
|
390
|
+
if (joint.type !== "revolute" && joint.type !== "prismatic") continue;
|
|
391
|
+
this.applyHinge(joint, made, false);
|
|
392
|
+
const pair = this.jointBodies.get(joint);
|
|
393
|
+
const ea = pair && this.entries.get(pair[0]);
|
|
394
|
+
const eb = pair && this.entries.get(pair[1]);
|
|
395
|
+
if (!ea || !eb) continue;
|
|
396
|
+
if (joint.type === "revolute") {
|
|
397
|
+
let deg = (ea.body.rotation() - eb.body.rotation()) / DEG;
|
|
398
|
+
deg = ((deg + 180) % 360 + 360) % 360 - 180;
|
|
399
|
+
joint.angle = deg;
|
|
400
|
+
} else {
|
|
401
|
+
const ra = ea.body.rotation();
|
|
402
|
+
const rb = eb.body.rotation();
|
|
403
|
+
const ta = ea.body.translation();
|
|
404
|
+
const tb = eb.body.translation();
|
|
405
|
+
const [ax, ay] = [joint.anchor[0] ?? 0, joint.anchor[1] ?? 0];
|
|
406
|
+
const [bx, by] = [joint.targetAnchor[0] ?? 0, joint.targetAnchor[1] ?? 0];
|
|
407
|
+
const wax = ta.x * SCALE + ax * Math.cos(ra) - ay * Math.sin(ra);
|
|
408
|
+
const way = ta.y * SCALE + ax * Math.sin(ra) + ay * Math.cos(ra);
|
|
409
|
+
const wbx = tb.x * SCALE + bx * Math.cos(rb) - by * Math.sin(rb);
|
|
410
|
+
const wby = tb.y * SCALE + bx * Math.sin(rb) + by * Math.cos(rb);
|
|
411
|
+
const [ux, uy] = [joint.axis[0] ?? 0, joint.axis[1] ?? 0];
|
|
412
|
+
const n = Math.hypot(ux, uy) || 1;
|
|
413
|
+
const ex = (ux * Math.cos(ra) - uy * Math.sin(ra)) / n;
|
|
414
|
+
const ey = (ux * Math.sin(ra) + uy * Math.cos(ra)) / n;
|
|
415
|
+
joint.travel = (wax - wbx) * ex + (way - wby) * ey;
|
|
416
|
+
}
|
|
323
417
|
}
|
|
324
418
|
}
|
|
325
419
|
/** Who is currently inside each sensor/body, maintained from the event drain. */
|
|
@@ -418,6 +512,11 @@ var Physics2D = class {
|
|
|
418
512
|
massOf(node) {
|
|
419
513
|
return this.entries.get(node)?.body.mass() ?? 1;
|
|
420
514
|
}
|
|
515
|
+
/** The solver's rotation of a body, in degrees (the node's own unit). */
|
|
516
|
+
rotationOf(node) {
|
|
517
|
+
const e = this.entries.get(node);
|
|
518
|
+
return e ? e.body.rotation() / DEG : 0;
|
|
519
|
+
}
|
|
421
520
|
/**
|
|
422
521
|
* Current solver velocity in px/s (fresher than the node prop mid-step).
|
|
423
522
|
*
|
|
@@ -436,10 +535,26 @@ var Physics2D = class {
|
|
|
436
535
|
* adapters while only one of them had it.
|
|
437
536
|
*/
|
|
438
537
|
applyImpulse(node, impulse) {
|
|
439
|
-
this.entries.get(node)
|
|
538
|
+
const e = this.entries.get(node);
|
|
539
|
+
if (!e) return;
|
|
540
|
+
if (node instanceof RigidBody2D) {
|
|
541
|
+
const vx = node.linearVelocity[0] ?? 0;
|
|
542
|
+
const vy = node.linearVelocity[1] ?? 0;
|
|
543
|
+
if (vx !== e.lastVX || vy !== e.lastVY) e.body.setLinvel({
|
|
544
|
+
x: vx / SCALE,
|
|
545
|
+
y: vy / SCALE
|
|
546
|
+
}, true);
|
|
547
|
+
}
|
|
548
|
+
e.body.applyImpulse({
|
|
440
549
|
x: impulse[0] / SCALE,
|
|
441
550
|
y: impulse[1] / SCALE
|
|
442
551
|
}, true);
|
|
552
|
+
if (node instanceof RigidBody2D) {
|
|
553
|
+
const lv = e.body.linvel();
|
|
554
|
+
node.linearVelocity = [lv.x * SCALE, lv.y * SCALE];
|
|
555
|
+
e.lastVX = node.linearVelocity[0] ?? 0;
|
|
556
|
+
e.lastVY = node.linearVelocity[1] ?? 0;
|
|
557
|
+
}
|
|
443
558
|
}
|
|
444
559
|
/**
|
|
445
560
|
* World-space raycast in PIXELS (y-down); `dir` may be any length (it is
|
|
@@ -514,6 +629,71 @@ var Physics2D = class {
|
|
|
514
629
|
lastRoot = null;
|
|
515
630
|
jointSet = /* @__PURE__ */ new Set();
|
|
516
631
|
joints = /* @__PURE__ */ new Map();
|
|
632
|
+
/** The two bodies each live joint ties, so a departed joint can be unlinked. */
|
|
633
|
+
jointBodies = /* @__PURE__ */ new Map();
|
|
634
|
+
/** What a pin's limits/motor were last pushed as, so a write re-applies. */
|
|
635
|
+
hingeState = /* @__PURE__ */ new Map();
|
|
636
|
+
/** Joints refused at creation (bodies that disagree about the axis) — said once. */
|
|
637
|
+
refusedJoints = /* @__PURE__ */ new Set();
|
|
638
|
+
/** A rope's or spring's numbers as last made, so a write REMAKES the joint. */
|
|
639
|
+
tetherState = /* @__PURE__ */ new Map();
|
|
640
|
+
tetherKey(joint) {
|
|
641
|
+
return `${joint.length}/${joint.stiffness}/${joint.damping}`;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Who is jointed to whom, and whether that pair's contacts are on — see the
|
|
645
|
+
* 3D twin: a joint's own bodies are not the world to the character
|
|
646
|
+
* controller (not its obstacle when the contacts are off, never its ground).
|
|
647
|
+
*/
|
|
648
|
+
jointed = /* @__PURE__ */ new Map();
|
|
649
|
+
linkJointed(a, b, contacts) {
|
|
650
|
+
for (const [x, y] of [[a, b], [b, a]]) {
|
|
651
|
+
let m = this.jointed.get(x);
|
|
652
|
+
if (!m) {
|
|
653
|
+
m = /* @__PURE__ */ new Map();
|
|
654
|
+
this.jointed.set(x, m);
|
|
655
|
+
}
|
|
656
|
+
m.set(y, contacts);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
unlinkJointed(a, b) {
|
|
660
|
+
this.jointed.get(a)?.delete(b);
|
|
661
|
+
this.jointed.get(b)?.delete(a);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Push a pin's `limits` and motor to the solver — on creation, and again
|
|
665
|
+
* whenever either changes. Limits and `angle` are this body relative to the
|
|
666
|
+
* target; Rapier measures the other way round, hence the flip.
|
|
667
|
+
*/
|
|
668
|
+
applyHinge(joint, made, first) {
|
|
669
|
+
const limits = JSON.stringify(joint.limits);
|
|
670
|
+
const motor = `${joint.motorSpeed}/${joint.motorStrength}`;
|
|
671
|
+
const last = this.hingeState.get(joint);
|
|
672
|
+
const unit = made;
|
|
673
|
+
const unitScale = joint.type === "revolute" ? DEG : 1 / SCALE;
|
|
674
|
+
if (first || last?.limits !== limits) {
|
|
675
|
+
if (joint.limits.length === 2) {
|
|
676
|
+
const lo = (joint.limits[0] ?? 0) * unitScale;
|
|
677
|
+
const hi = (joint.limits[1] ?? 0) * unitScale;
|
|
678
|
+
unit.setLimits(-Math.max(lo, hi), -Math.min(lo, hi));
|
|
679
|
+
} else if (!first) unit.setLimits(-1e9, 1e9);
|
|
680
|
+
}
|
|
681
|
+
if (first || last?.motor !== motor) {
|
|
682
|
+
if (joint.motorSpeed !== 0) unit.configureMotorVelocity(-joint.motorSpeed * unitScale, joint.motorStrength);
|
|
683
|
+
else if (!first) unit.configureMotorVelocity(0, 0);
|
|
684
|
+
}
|
|
685
|
+
this.hingeState.set(joint, {
|
|
686
|
+
limits,
|
|
687
|
+
motor
|
|
688
|
+
});
|
|
689
|
+
if (joint.motorSpeed !== 0) {
|
|
690
|
+
const pair = this.jointBodies.get(joint);
|
|
691
|
+
for (const b of pair ?? []) {
|
|
692
|
+
const e = this.entries.get(b);
|
|
693
|
+
if (e && !e.body.isFixed() && e.body.isSleeping()) e.body.wakeUp();
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
517
697
|
bodySet = /* @__PURE__ */ new Set();
|
|
518
698
|
/**
|
|
519
699
|
* Move every body to its node's current transform — outline mode only.
|
|
@@ -523,12 +703,12 @@ var Physics2D = class {
|
|
|
523
703
|
*/
|
|
524
704
|
poseFromTree() {
|
|
525
705
|
for (const [node, e] of this.entries) {
|
|
526
|
-
const [wx, wy] =
|
|
706
|
+
const [wx, wy, wa] = worldPose2D(node);
|
|
527
707
|
e.body.setTranslation({
|
|
528
708
|
x: wx / SCALE,
|
|
529
709
|
y: wy / SCALE
|
|
530
710
|
}, false);
|
|
531
|
-
e.body.setRotation(
|
|
711
|
+
e.body.setRotation(wa * DEG, false);
|
|
532
712
|
}
|
|
533
713
|
this.world.propagateModifiedBodyPositionsToColliders();
|
|
534
714
|
}
|
|
@@ -600,7 +780,7 @@ var Physics2D = class {
|
|
|
600
780
|
let e = this.entries.get(node);
|
|
601
781
|
const R = this.R;
|
|
602
782
|
if (!e) {
|
|
603
|
-
const [wx, wy] =
|
|
783
|
+
const [wx, wy, wa] = worldPose2D(node);
|
|
604
784
|
const x = wx / SCALE;
|
|
605
785
|
const y = wy / SCALE;
|
|
606
786
|
let desc;
|
|
@@ -608,7 +788,7 @@ var Physics2D = class {
|
|
|
608
788
|
if (dyn) desc = R.RigidBodyDesc.dynamic();
|
|
609
789
|
else if (node instanceof CharacterBody2D || node instanceof Area2D) desc = R.RigidBodyDesc.kinematicPositionBased();
|
|
610
790
|
else desc = R.RigidBodyDesc.fixed();
|
|
611
|
-
desc.setTranslation(x, y).setRotation(
|
|
791
|
+
desc.setTranslation(x, y).setRotation(wa * DEG);
|
|
612
792
|
if (node instanceof RigidBody2D) {
|
|
613
793
|
desc.setGravityScale(node.gravityScale);
|
|
614
794
|
desc.setLinearDamping(node.linearDamping).setAngularDamping(node.angularDamping);
|
|
@@ -629,7 +809,7 @@ var Physics2D = class {
|
|
|
629
809
|
collider,
|
|
630
810
|
lastX: wx,
|
|
631
811
|
lastY: wy,
|
|
632
|
-
lastRot:
|
|
812
|
+
lastRot: wa,
|
|
633
813
|
lastVX: 0,
|
|
634
814
|
lastVY: 0,
|
|
635
815
|
lastAV: dyn ? node.angularVelocity : 0,
|
|
@@ -658,7 +838,7 @@ var Physics2D = class {
|
|
|
658
838
|
node._enabledChanged = false;
|
|
659
839
|
if (node.enabled) this.armOverlaps(node, e.collider);
|
|
660
840
|
}
|
|
661
|
-
const [px, py] =
|
|
841
|
+
const [px, py, pa] = worldPose2D(node);
|
|
662
842
|
if (px !== e.lastX || py !== e.lastY) {
|
|
663
843
|
e.body.setTranslation({
|
|
664
844
|
x: px / SCALE,
|
|
@@ -667,9 +847,9 @@ var Physics2D = class {
|
|
|
667
847
|
e.lastX = px;
|
|
668
848
|
e.lastY = py;
|
|
669
849
|
}
|
|
670
|
-
if (
|
|
671
|
-
e.body.setRotation(
|
|
672
|
-
e.lastRot =
|
|
850
|
+
if (pa !== e.lastRot) {
|
|
851
|
+
e.body.setRotation(pa * DEG, true);
|
|
852
|
+
e.lastRot = pa;
|
|
673
853
|
}
|
|
674
854
|
if (node instanceof RigidBody2D) {
|
|
675
855
|
const vx = node.linearVelocity[0] ?? 0;
|
|
@@ -743,23 +923,62 @@ function platformTopPx(node) {
|
|
|
743
923
|
const c = node.collider;
|
|
744
924
|
return wy - (c.shape === "rect" ? (c.size?.[1] ?? 32) / 2 : c.shape === "capsule" ? (c.height ?? 32) / 2 + (c.radius ?? 8) : c.radius ?? 16);
|
|
745
925
|
}
|
|
746
|
-
|
|
926
|
+
const poseChain2 = [];
|
|
927
|
+
const poseOut = [
|
|
928
|
+
0,
|
|
929
|
+
0,
|
|
930
|
+
0
|
|
931
|
+
];
|
|
932
|
+
/**
|
|
933
|
+
* The WORLD pose of `node` — position and angle (degrees) composed through
|
|
934
|
+
* every Node2D ancestor, the way the renderer composes them. This used to sum
|
|
935
|
+
* positions only, so a sensor 120 px down +x of a body turned 90° was put at
|
|
936
|
+
* world +x while the renderer drew it at world +y. Scale is not composed.
|
|
937
|
+
*/
|
|
938
|
+
function worldPose2D(node) {
|
|
939
|
+
poseChain2.length = 0;
|
|
940
|
+
for (let n = node; n; n = n.parent) if (n instanceof Node2D) poseChain2.push(n);
|
|
747
941
|
let x = 0;
|
|
748
942
|
let y = 0;
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
943
|
+
let angle = 0;
|
|
944
|
+
for (let i = poseChain2.length - 1; i >= 0; i--) {
|
|
945
|
+
const n = poseChain2[i];
|
|
946
|
+
const lx = n.position[0] ?? 0;
|
|
947
|
+
const ly = n.position[1] ?? 0;
|
|
948
|
+
if (angle !== 0) {
|
|
949
|
+
const a = angle * DEG;
|
|
950
|
+
const c = Math.cos(a);
|
|
951
|
+
const s = Math.sin(a);
|
|
952
|
+
x += lx * c - ly * s;
|
|
953
|
+
y += lx * s + ly * c;
|
|
954
|
+
} else {
|
|
955
|
+
x += lx;
|
|
956
|
+
y += ly;
|
|
957
|
+
}
|
|
958
|
+
angle += n.rotation;
|
|
752
959
|
}
|
|
753
|
-
|
|
960
|
+
poseOut[0] = x;
|
|
961
|
+
poseOut[1] = y;
|
|
962
|
+
poseOut[2] = angle;
|
|
963
|
+
return poseOut;
|
|
754
964
|
}
|
|
755
|
-
function
|
|
756
|
-
|
|
757
|
-
|
|
965
|
+
function worldPosition2D(node) {
|
|
966
|
+
const p = worldPose2D(node);
|
|
967
|
+
return [p[0], p[1]];
|
|
968
|
+
}
|
|
969
|
+
/** The pose of `node`'s PARENT frame: [x, y, angleDeg]. */
|
|
970
|
+
function parentPose2D(node) {
|
|
971
|
+
let parent = null;
|
|
758
972
|
for (let n = node.parent; n; n = n.parent) if (n instanceof Node2D) {
|
|
759
|
-
|
|
760
|
-
|
|
973
|
+
parent = n;
|
|
974
|
+
break;
|
|
761
975
|
}
|
|
762
|
-
|
|
976
|
+
const p = worldPose2D(parent);
|
|
977
|
+
return [
|
|
978
|
+
p[0],
|
|
979
|
+
p[1],
|
|
980
|
+
p[2]
|
|
981
|
+
];
|
|
763
982
|
}
|
|
764
983
|
function parseCollider2D(R, collider, nodeName) {
|
|
765
984
|
validateCollider2D(collider, nodeName);
|