@blorkfield/overlay-core 0.8.9 → 0.8.11
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/README.md +2 -0
- package/dist/index.cjs +40 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +40 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -389,6 +389,8 @@ The offset calculation is your responsibility - overlay-core uses whatever posit
|
|
|
389
389
|
|
|
390
390
|
Grab uses delta-based movement: when grabbed, the entity and mouse become linked. The entity moves BY the same amount as the mouse moves, not TO the mouse position. This ensures the entity stays at its original position on grab and follows mouse movement naturally.
|
|
391
391
|
|
|
392
|
+
Grab detection uses a two-pass approach to handle fast-moving bodies. The first pass does an exact point query at the click position. If that misses (the body tunneled through the cursor between frames), a second pass sweeps the body's recent position history (last 5 frames, 20px radius) to catch it. This means you can grab entities even when they are moving quickly.
|
|
393
|
+
|
|
392
394
|
```typescript
|
|
393
395
|
// Grab object at current mouse position (only 'grabable' tagged objects)
|
|
394
396
|
const grabbedId = scene.startGrab();
|
package/dist/index.cjs
CHANGED
|
@@ -1424,7 +1424,6 @@ var OverlayScene = class {
|
|
|
1424
1424
|
this.boundaries = [];
|
|
1425
1425
|
this.updateCallbacks = [];
|
|
1426
1426
|
this.animationFrameId = null;
|
|
1427
|
-
this.mouse = null;
|
|
1428
1427
|
this.fonts = [];
|
|
1429
1428
|
this.fontsInitialized = false;
|
|
1430
1429
|
this.letterDebugInfo = /* @__PURE__ */ new Map();
|
|
@@ -1451,6 +1450,12 @@ var OverlayScene = class {
|
|
|
1451
1450
|
this.lastGrabMousePosition = null;
|
|
1452
1451
|
this.grabbedWasDynamic = false;
|
|
1453
1452
|
this.grabVelocity = { x: 0, y: 0 };
|
|
1453
|
+
// Position history for sweep-based grab detection (catches fast-moving bodies)
|
|
1454
|
+
this.bodyPositionHistory = /* @__PURE__ */ new Map();
|
|
1455
|
+
this.grabHistoryFrames = 5;
|
|
1456
|
+
this.grabHistoryRadius = 20;
|
|
1457
|
+
// Number of physics substeps per frame — more substeps = better collision at high speeds, more CPU
|
|
1458
|
+
this.substeps = 2;
|
|
1454
1459
|
/** Handle mouse down - start grab via programmatic API */
|
|
1455
1460
|
this.handleMouseDown = (event) => {
|
|
1456
1461
|
const rect = this.canvas.getBoundingClientRect();
|
|
@@ -1527,13 +1532,14 @@ var OverlayScene = class {
|
|
|
1527
1532
|
};
|
|
1528
1533
|
// ==================== PRIVATE ====================
|
|
1529
1534
|
this.loop = () => {
|
|
1535
|
+
const substepDelta = 1e3 / 60 / this.substeps;
|
|
1536
|
+
for (let i = 0; i < this.substeps; i++) {
|
|
1537
|
+
import_matter_js5.default.Engine.update(this.engine, substepDelta);
|
|
1538
|
+
}
|
|
1530
1539
|
this.effectManager.update();
|
|
1531
1540
|
this.checkTTLExpiration();
|
|
1532
1541
|
this.checkDespawnBelowFloor();
|
|
1533
1542
|
this.updatePressure();
|
|
1534
|
-
if (!this.followTargets.has("mouse") && this.mouse) {
|
|
1535
|
-
this.followTargets.set("mouse", { x: this.mouse.position.x, y: this.mouse.position.y });
|
|
1536
|
-
}
|
|
1537
1543
|
if (this.grabbedObjectId && this.lastGrabMousePosition) {
|
|
1538
1544
|
const entry = this.objects.get(this.grabbedObjectId);
|
|
1539
1545
|
const mouseTarget = this.followTargets.get("mouse");
|
|
@@ -1570,6 +1576,14 @@ var OverlayScene = class {
|
|
|
1570
1576
|
if (entry.domElement && entry.tags.includes("falling")) {
|
|
1571
1577
|
this.updateDOMElementTransform(entry);
|
|
1572
1578
|
}
|
|
1579
|
+
if (entry.tags.includes("grabable") && !entry.body.isStatic) {
|
|
1580
|
+
const history = this.bodyPositionHistory.get(entry.body.id) ?? [];
|
|
1581
|
+
history.push({ x: entry.body.position.x, y: entry.body.position.y });
|
|
1582
|
+
if (history.length > this.grabHistoryFrames) {
|
|
1583
|
+
history.shift();
|
|
1584
|
+
}
|
|
1585
|
+
this.bodyPositionHistory.set(entry.body.id, history);
|
|
1586
|
+
}
|
|
1573
1587
|
}
|
|
1574
1588
|
if (!this.config.debug) {
|
|
1575
1589
|
this.drawTTFGlyphs();
|
|
@@ -1595,7 +1609,6 @@ var OverlayScene = class {
|
|
|
1595
1609
|
this.floorSegments = boundariesResult.floorSegments;
|
|
1596
1610
|
import_matter_js5.default.Composite.add(this.engine.world, this.boundaries);
|
|
1597
1611
|
this.checkInitialFloorIntegrity();
|
|
1598
|
-
this.mouse = import_matter_js5.default.Mouse.create(canvas);
|
|
1599
1612
|
canvas.addEventListener("mousedown", this.handleMouseDown);
|
|
1600
1613
|
canvas.addEventListener("mousemove", this.handleMouseMove);
|
|
1601
1614
|
canvas.addEventListener("mouseup", this.handleMouseUp);
|
|
@@ -1831,7 +1844,7 @@ var OverlayScene = class {
|
|
|
1831
1844
|
}
|
|
1832
1845
|
}
|
|
1833
1846
|
if (parts.length > 0) {
|
|
1834
|
-
|
|
1847
|
+
logger.debug("[Pressure]", parts.join(" "));
|
|
1835
1848
|
}
|
|
1836
1849
|
}
|
|
1837
1850
|
/** Calculate weighted pressure from a set of object IDs */
|
|
@@ -2008,7 +2021,6 @@ var OverlayScene = class {
|
|
|
2008
2021
|
}
|
|
2009
2022
|
start() {
|
|
2010
2023
|
import_matter_js5.default.Render.run(this.render);
|
|
2011
|
-
import_matter_js5.default.Runner.run(this.runner, this.engine);
|
|
2012
2024
|
this.loop();
|
|
2013
2025
|
}
|
|
2014
2026
|
stop() {
|
|
@@ -2375,13 +2387,13 @@ var OverlayScene = class {
|
|
|
2375
2387
|
* @returns The ID of the grabbed object, or null if no grabable object at position
|
|
2376
2388
|
*/
|
|
2377
2389
|
startGrab() {
|
|
2378
|
-
const
|
|
2379
|
-
const position = mouseTarget ?? (this.mouse ? { x: this.mouse.position.x, y: this.mouse.position.y } : null);
|
|
2390
|
+
const position = this.followTargets.get("mouse") ?? null;
|
|
2380
2391
|
if (!position) return null;
|
|
2381
2392
|
const bodies = import_matter_js5.default.Query.point(
|
|
2382
2393
|
import_matter_js5.default.Composite.allBodies(this.engine.world),
|
|
2383
2394
|
position
|
|
2384
2395
|
);
|
|
2396
|
+
logger.debug("OverlayScene", "Grabbed position " + position + ', had "' + bodies.length + '"');
|
|
2385
2397
|
for (const body of bodies) {
|
|
2386
2398
|
const entry = this.findObjectByBody(body);
|
|
2387
2399
|
if (entry && entry.tags.includes("grabable")) {
|
|
@@ -2393,6 +2405,24 @@ var OverlayScene = class {
|
|
|
2393
2405
|
return entry.id;
|
|
2394
2406
|
}
|
|
2395
2407
|
}
|
|
2408
|
+
const r2 = this.grabHistoryRadius * this.grabHistoryRadius;
|
|
2409
|
+
for (const entry of this.objects.values()) {
|
|
2410
|
+
if (!entry.tags.includes("grabable")) continue;
|
|
2411
|
+
const history = this.bodyPositionHistory.get(entry.body.id);
|
|
2412
|
+
if (!history) continue;
|
|
2413
|
+
for (const pastPos of history) {
|
|
2414
|
+
const dx = position.x - pastPos.x;
|
|
2415
|
+
const dy = position.y - pastPos.y;
|
|
2416
|
+
if (dx * dx + dy * dy <= r2) {
|
|
2417
|
+
this.grabbedObjectId = entry.id;
|
|
2418
|
+
this.lastGrabMousePosition = { x: position.x, y: position.y };
|
|
2419
|
+
this.grabVelocity = { x: 0, y: 0 };
|
|
2420
|
+
this.grabbedWasDynamic = !entry.body.isStatic;
|
|
2421
|
+
import_matter_js5.default.Body.setStatic(entry.body, true);
|
|
2422
|
+
return entry.id;
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2396
2426
|
return null;
|
|
2397
2427
|
}
|
|
2398
2428
|
/**
|
|
@@ -2406,6 +2436,7 @@ var OverlayScene = class {
|
|
|
2406
2436
|
import_matter_js5.default.Sleeping.set(entry.body, false);
|
|
2407
2437
|
import_matter_js5.default.Body.setVelocity(entry.body, this.grabVelocity);
|
|
2408
2438
|
import_matter_js5.default.Body.setAngularVelocity(entry.body, 0);
|
|
2439
|
+
this.bodyPositionHistory.delete(entry.body.id);
|
|
2409
2440
|
}
|
|
2410
2441
|
}
|
|
2411
2442
|
this.grabbedObjectId = null;
|