@blorkfield/overlay-core 0.8.10 → 0.9.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/README.md +51 -9
- package/dist/index.cjs +69 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +68 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,6 +40,7 @@ scene.spawnObject({ tags: [FALLING, GRABABLE], ... });
|
|
|
40
40
|
| `TAG_FALLING` / `TAGS.FALLING` | `'falling'` | Object is dynamic and affected by gravity |
|
|
41
41
|
| `TAG_FOLLOW_WINDOW` / `TAGS.FOLLOW_WINDOW` | `'follow_window'` | Object follows mouse position when grounded |
|
|
42
42
|
| `TAG_GRABABLE` / `TAGS.GRABABLE` | `'grabable'` | Object can be grabbed and moved with mouse |
|
|
43
|
+
| `TAG_GRAVITY_OVERRIDE` / `TAGS.GRAVITY_OVERRIDE` | `'gravity_override'` | Object uses its own gravity (set via `gravityOverride` in config) |
|
|
43
44
|
|
|
44
45
|
Without the `falling` tag, objects are static and won't move.
|
|
45
46
|
|
|
@@ -82,7 +83,7 @@ const { canvas, bounds } = OverlayScene.createContainer(container, {
|
|
|
82
83
|
// Create scene
|
|
83
84
|
const scene = new OverlayScene(canvas, {
|
|
84
85
|
bounds,
|
|
85
|
-
gravity: 1,
|
|
86
|
+
gravity: { x: 0, y: 1 },
|
|
86
87
|
wrapHorizontal: true,
|
|
87
88
|
background: 'transparent'
|
|
88
89
|
});
|
|
@@ -426,7 +427,7 @@ const currentGrab = scene.getGrabbedObject(); // Returns ID or null
|
|
|
426
427
|
```typescript
|
|
427
428
|
const scene = new OverlayScene(canvas, {
|
|
428
429
|
bounds: { top: 0, bottom: 600, left: 0, right: 800 },
|
|
429
|
-
gravity: 1,
|
|
430
|
+
gravity: { x: 0, y: 1 },
|
|
430
431
|
wrapHorizontal: true,
|
|
431
432
|
debug: false,
|
|
432
433
|
background: '#16213e',
|
|
@@ -443,7 +444,7 @@ const scene = new OverlayScene(canvas, {
|
|
|
443
444
|
|
|
444
445
|
| Option | Default | Description |
|
|
445
446
|
|--------|---------|-------------|
|
|
446
|
-
| `gravity` | 1 | Gravity
|
|
447
|
+
| `gravity` | `{ x: 0, y: 1 }` | Gravity vector. Both axes support negative values |
|
|
447
448
|
| `wrapHorizontal` | true | Objects wrap around screen edges |
|
|
448
449
|
| `debug` | false | Show collision wireframes |
|
|
449
450
|
| `background` | transparent | Canvas background color |
|
|
@@ -641,13 +642,53 @@ setLogLevel('debug'); // Options: debug, info, warn, error
|
|
|
641
642
|
## Lifecycle
|
|
642
643
|
|
|
643
644
|
```typescript
|
|
644
|
-
scene.start();
|
|
645
|
-
scene.stop();
|
|
646
|
-
scene.resize(w, h);
|
|
647
|
-
scene.setDebug(true);
|
|
648
|
-
scene.
|
|
645
|
+
scene.start(); // Start simulation
|
|
646
|
+
scene.stop(); // Pause simulation
|
|
647
|
+
scene.resize(w, h); // Resize canvas and bounds
|
|
648
|
+
scene.setDebug(true); // Toggle wireframe mode
|
|
649
|
+
scene.setGravity({ x: 0, y: -1 }); // Set gravity (negative y = upward)
|
|
650
|
+
scene.setGravity({ x: 0, y: 0 }); // Zero gravity
|
|
651
|
+
scene.setGravity({ x: 1, y: 0 }); // Sideways gravity
|
|
652
|
+
scene.destroy(); // Clean up resources
|
|
649
653
|
```
|
|
650
654
|
|
|
655
|
+
### Per-Object Gravity Override
|
|
656
|
+
|
|
657
|
+
Individual dynamic objects can have their own gravity vector, independent of the scene gravity. This is done via the `gravityOverride` field in `ObjectConfig`, which automatically adds the `gravity_override` tag to the object.
|
|
658
|
+
|
|
659
|
+
```typescript
|
|
660
|
+
// Spawn a floaty object that drifts upward
|
|
661
|
+
scene.spawnObject({
|
|
662
|
+
x: 200, y: 300,
|
|
663
|
+
radius: 20,
|
|
664
|
+
fillStyle: '#4a90d9',
|
|
665
|
+
tags: ['falling', 'grabable'],
|
|
666
|
+
gravityOverride: { x: 0, y: -0.3 } // floats upward
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// Zero gravity — hovers in place
|
|
670
|
+
scene.spawnObject({
|
|
671
|
+
x: 400, y: 200,
|
|
672
|
+
radius: 15,
|
|
673
|
+
fillStyle: '#e94560',
|
|
674
|
+
tags: ['falling'],
|
|
675
|
+
gravityOverride: { x: 0, y: 0 }
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
// Change or clear a gravity override at runtime
|
|
679
|
+
scene.setObjectGravityOverride(id, { x: 0.5, y: 0 }); // drift sideways
|
|
680
|
+
scene.setObjectGravityOverride(id, null); // restore scene gravity
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
Tags are either boolean (presence = true) or carry a value. `gravity_override` is a value tag — its Vector2 value is set via `gravityOverride` in the config. Boolean tags (`falling`, `grabable`, `follow_window`) need no value.
|
|
684
|
+
|
|
685
|
+
| Tag | Type | Behavior |
|
|
686
|
+
|-----|------|----------|
|
|
687
|
+
| `falling` | boolean | Dynamic body affected by gravity |
|
|
688
|
+
| `grabable` | boolean | Can be grabbed with mouse |
|
|
689
|
+
| `follow_window` | boolean | Walks toward mouse when grounded |
|
|
690
|
+
| `gravity_override` | Vector2 | Uses own gravity instead of scene gravity |
|
|
691
|
+
|
|
651
692
|
## Examples
|
|
652
693
|
|
|
653
694
|
Working examples are provided in the `/examples` directory:
|
|
@@ -673,6 +714,7 @@ import type {
|
|
|
673
714
|
// Scene configuration
|
|
674
715
|
OverlaySceneConfig,
|
|
675
716
|
Bounds,
|
|
717
|
+
Vector2,
|
|
676
718
|
ContainerOptions,
|
|
677
719
|
FloorConfig,
|
|
678
720
|
|
|
@@ -732,5 +774,5 @@ import type {
|
|
|
732
774
|
} from '@blorkfield/overlay-core';
|
|
733
775
|
|
|
734
776
|
// Tag constants (values, not types)
|
|
735
|
-
import { TAGS, TAG_FALLING, TAG_GRABABLE, TAG_FOLLOW_WINDOW } from '@blorkfield/overlay-core';
|
|
777
|
+
import { TAGS, TAG_FALLING, TAG_GRABABLE, TAG_FOLLOW_WINDOW, TAG_GRAVITY_OVERRIDE } from '@blorkfield/overlay-core';
|
|
736
778
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
TAG_FALLING: () => TAG_FALLING,
|
|
37
37
|
TAG_FOLLOW_WINDOW: () => TAG_FOLLOW_WINDOW,
|
|
38
38
|
TAG_GRABABLE: () => TAG_GRABABLE,
|
|
39
|
+
TAG_GRAVITY_OVERRIDE: () => TAG_GRAVITY_OVERRIDE,
|
|
39
40
|
clearFontCache: () => clearFontCache,
|
|
40
41
|
getGlyphData: () => getGlyphData,
|
|
41
42
|
getKerning: () => getKerning,
|
|
@@ -54,7 +55,8 @@ var import_matter_js5 = __toESM(require("matter-js"), 1);
|
|
|
54
55
|
var import_matter_js = __toESM(require("matter-js"), 1);
|
|
55
56
|
function createEngine(gravity) {
|
|
56
57
|
const engine = import_matter_js.default.Engine.create();
|
|
57
|
-
engine.gravity.
|
|
58
|
+
engine.gravity.x = gravity.x;
|
|
59
|
+
engine.gravity.y = gravity.y;
|
|
58
60
|
return engine;
|
|
59
61
|
}
|
|
60
62
|
function createRender(engine, canvas, config) {
|
|
@@ -1424,7 +1426,6 @@ var OverlayScene = class {
|
|
|
1424
1426
|
this.boundaries = [];
|
|
1425
1427
|
this.updateCallbacks = [];
|
|
1426
1428
|
this.animationFrameId = null;
|
|
1427
|
-
this.mouse = null;
|
|
1428
1429
|
this.fonts = [];
|
|
1429
1430
|
this.fontsInitialized = false;
|
|
1430
1431
|
this.letterDebugInfo = /* @__PURE__ */ new Map();
|
|
@@ -1457,6 +1458,8 @@ var OverlayScene = class {
|
|
|
1457
1458
|
this.grabHistoryRadius = 20;
|
|
1458
1459
|
// Number of physics substeps per frame — more substeps = better collision at high speeds, more CPU
|
|
1459
1460
|
this.substeps = 2;
|
|
1461
|
+
// Tracks only the bodies with a gravity override — engine gravity handles everyone else
|
|
1462
|
+
this.gravityOverrideEntries = /* @__PURE__ */ new Set();
|
|
1460
1463
|
/** Handle mouse down - start grab via programmatic API */
|
|
1461
1464
|
this.handleMouseDown = (event) => {
|
|
1462
1465
|
const rect = this.canvas.getBoundingClientRect();
|
|
@@ -1534,16 +1537,22 @@ var OverlayScene = class {
|
|
|
1534
1537
|
// ==================== PRIVATE ====================
|
|
1535
1538
|
this.loop = () => {
|
|
1536
1539
|
const substepDelta = 1e3 / 60 / this.substeps;
|
|
1540
|
+
const scale = this.engine.gravity.scale;
|
|
1537
1541
|
for (let i = 0; i < this.substeps; i++) {
|
|
1542
|
+
for (const entry of this.gravityOverrideEntries) {
|
|
1543
|
+
if (entry.body.isStatic || entry.body.isSleeping) continue;
|
|
1544
|
+
const g = entry.gravityOverride;
|
|
1545
|
+
import_matter_js5.default.Body.applyForce(entry.body, entry.body.position, {
|
|
1546
|
+
x: entry.body.mass * (g.x - this.engine.gravity.x) * scale,
|
|
1547
|
+
y: entry.body.mass * (g.y - this.engine.gravity.y) * scale
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1538
1550
|
import_matter_js5.default.Engine.update(this.engine, substepDelta);
|
|
1539
1551
|
}
|
|
1540
1552
|
this.effectManager.update();
|
|
1541
1553
|
this.checkTTLExpiration();
|
|
1542
1554
|
this.checkDespawnBelowFloor();
|
|
1543
1555
|
this.updatePressure();
|
|
1544
|
-
if (!this.followTargets.has("mouse") && this.mouse) {
|
|
1545
|
-
this.followTargets.set("mouse", { x: this.mouse.position.x, y: this.mouse.position.y });
|
|
1546
|
-
}
|
|
1547
1556
|
if (this.grabbedObjectId && this.lastGrabMousePosition) {
|
|
1548
1557
|
const entry = this.objects.get(this.grabbedObjectId);
|
|
1549
1558
|
const mouseTarget = this.followTargets.get("mouse");
|
|
@@ -1600,7 +1609,7 @@ var OverlayScene = class {
|
|
|
1600
1609
|
};
|
|
1601
1610
|
this.canvas = canvas;
|
|
1602
1611
|
this.config = {
|
|
1603
|
-
gravity: 1,
|
|
1612
|
+
gravity: { x: 0, y: 1 },
|
|
1604
1613
|
wrapHorizontal: true,
|
|
1605
1614
|
debug: false,
|
|
1606
1615
|
...config
|
|
@@ -1613,7 +1622,6 @@ var OverlayScene = class {
|
|
|
1613
1622
|
this.floorSegments = boundariesResult.floorSegments;
|
|
1614
1623
|
import_matter_js5.default.Composite.add(this.engine.world, this.boundaries);
|
|
1615
1624
|
this.checkInitialFloorIntegrity();
|
|
1616
|
-
this.mouse = import_matter_js5.default.Mouse.create(canvas);
|
|
1617
1625
|
canvas.addEventListener("mousedown", this.handleMouseDown);
|
|
1618
1626
|
canvas.addEventListener("mousemove", this.handleMouseMove);
|
|
1619
1627
|
canvas.addEventListener("mouseup", this.handleMouseUp);
|
|
@@ -2067,6 +2075,39 @@ var OverlayScene = class {
|
|
|
2067
2075
|
}
|
|
2068
2076
|
}
|
|
2069
2077
|
}
|
|
2078
|
+
/**
|
|
2079
|
+
* Set gravity at runtime. Supports any direction including negative values.
|
|
2080
|
+
* @example
|
|
2081
|
+
* scene.setGravity({ x: 0, y: 1 }); // Normal downward gravity
|
|
2082
|
+
* scene.setGravity({ x: 0, y: -1 }); // Upward gravity
|
|
2083
|
+
* scene.setGravity({ x: 1, y: 0 }); // Sideways gravity
|
|
2084
|
+
* scene.setGravity({ x: 0, y: 0 }); // Zero gravity
|
|
2085
|
+
*/
|
|
2086
|
+
setGravity(gravity) {
|
|
2087
|
+
this.config.gravity = gravity;
|
|
2088
|
+
this.engine.gravity.x = gravity.x;
|
|
2089
|
+
this.engine.gravity.y = gravity.y;
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Set or clear a per-object gravity override at runtime.
|
|
2093
|
+
* Pass `null` to remove the override and restore scene gravity for that object.
|
|
2094
|
+
*/
|
|
2095
|
+
setObjectGravityOverride(id, gravity) {
|
|
2096
|
+
const entry = this.objects.get(id);
|
|
2097
|
+
if (!entry) return;
|
|
2098
|
+
if (gravity === null) {
|
|
2099
|
+
entry.gravityOverride = void 0;
|
|
2100
|
+
this.gravityOverrideEntries.delete(entry);
|
|
2101
|
+
const idx = entry.tags.indexOf("gravity_override");
|
|
2102
|
+
if (idx !== -1) entry.tags.splice(idx, 1);
|
|
2103
|
+
} else {
|
|
2104
|
+
entry.gravityOverride = gravity;
|
|
2105
|
+
this.gravityOverrideEntries.add(entry);
|
|
2106
|
+
if (!entry.tags.includes("gravity_override")) {
|
|
2107
|
+
entry.tags.push("gravity_override");
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2070
2111
|
/**
|
|
2071
2112
|
* Update the background configuration at runtime.
|
|
2072
2113
|
*/
|
|
@@ -2124,7 +2165,10 @@ var OverlayScene = class {
|
|
|
2124
2165
|
return result.id;
|
|
2125
2166
|
}
|
|
2126
2167
|
const id = crypto.randomUUID();
|
|
2127
|
-
const tags = config.tags ?? [];
|
|
2168
|
+
const tags = [...config.tags ?? []];
|
|
2169
|
+
if (config.gravityOverride && !tags.includes("gravity_override")) {
|
|
2170
|
+
tags.push("gravity_override");
|
|
2171
|
+
}
|
|
2128
2172
|
const isStatic = !tags.includes("falling");
|
|
2129
2173
|
logger.debug("OverlayScene", `Spawning object`, {
|
|
2130
2174
|
id,
|
|
@@ -2164,9 +2208,11 @@ var OverlayScene = class {
|
|
|
2164
2208
|
pressureThreshold,
|
|
2165
2209
|
shadow,
|
|
2166
2210
|
originalPosition: shadow || clicksRemaining !== void 0 ? { x: config.x, y: config.y } : void 0,
|
|
2167
|
-
clicksRemaining
|
|
2211
|
+
clicksRemaining,
|
|
2212
|
+
gravityOverride: config.gravityOverride
|
|
2168
2213
|
};
|
|
2169
2214
|
this.objects.set(id, entry);
|
|
2215
|
+
if (config.gravityOverride) this.gravityOverrideEntries.add(entry);
|
|
2170
2216
|
import_matter_js5.default.Composite.add(this.engine.world, body);
|
|
2171
2217
|
if (isStatic && pressureThreshold !== void 0) {
|
|
2172
2218
|
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
@@ -2180,7 +2226,10 @@ var OverlayScene = class {
|
|
|
2180
2226
|
*/
|
|
2181
2227
|
async spawnObjectAsync(config) {
|
|
2182
2228
|
const id = crypto.randomUUID();
|
|
2183
|
-
const tags = config.tags ?? [];
|
|
2229
|
+
const tags = [...config.tags ?? []];
|
|
2230
|
+
if (config.gravityOverride && !tags.includes("gravity_override")) {
|
|
2231
|
+
tags.push("gravity_override");
|
|
2232
|
+
}
|
|
2184
2233
|
const isStatic = !tags.includes("falling");
|
|
2185
2234
|
logger.debug("OverlayScene", `Spawning object async`, {
|
|
2186
2235
|
id,
|
|
@@ -2220,9 +2269,11 @@ var OverlayScene = class {
|
|
|
2220
2269
|
pressureThreshold,
|
|
2221
2270
|
shadow,
|
|
2222
2271
|
originalPosition: shadow || clicksRemaining !== void 0 ? { x: config.x, y: config.y } : void 0,
|
|
2223
|
-
clicksRemaining
|
|
2272
|
+
clicksRemaining,
|
|
2273
|
+
gravityOverride: config.gravityOverride
|
|
2224
2274
|
};
|
|
2225
2275
|
this.objects.set(id, entry);
|
|
2276
|
+
if (config.gravityOverride) this.gravityOverrideEntries.add(entry);
|
|
2226
2277
|
import_matter_js5.default.Composite.add(this.engine.world, body);
|
|
2227
2278
|
if (isStatic && pressureThreshold !== void 0) {
|
|
2228
2279
|
this.obstaclePressure.set(id, /* @__PURE__ */ new Set());
|
|
@@ -2311,6 +2362,7 @@ var OverlayScene = class {
|
|
|
2311
2362
|
if (!entry) return;
|
|
2312
2363
|
this.emitLifecycleEvent("objectRemoved", this.toObjectState(entry));
|
|
2313
2364
|
import_matter_js5.default.Composite.remove(this.engine.world, entry.body);
|
|
2365
|
+
this.gravityOverrideEntries.delete(entry);
|
|
2314
2366
|
this.objects.delete(id);
|
|
2315
2367
|
}
|
|
2316
2368
|
removeObjects(ids) {
|
|
@@ -2323,6 +2375,7 @@ var OverlayScene = class {
|
|
|
2323
2375
|
import_matter_js5.default.Composite.remove(this.engine.world, entry.body);
|
|
2324
2376
|
}
|
|
2325
2377
|
this.objects.clear();
|
|
2378
|
+
this.gravityOverrideEntries.clear();
|
|
2326
2379
|
}
|
|
2327
2380
|
removeObjectsByTag(tag) {
|
|
2328
2381
|
const toRemove = [];
|
|
@@ -2392,8 +2445,7 @@ var OverlayScene = class {
|
|
|
2392
2445
|
* @returns The ID of the grabbed object, or null if no grabable object at position
|
|
2393
2446
|
*/
|
|
2394
2447
|
startGrab() {
|
|
2395
|
-
const
|
|
2396
|
-
const position = mouseTarget ?? (this.mouse ? { x: this.mouse.position.x, y: this.mouse.position.y } : null);
|
|
2448
|
+
const position = this.followTargets.get("mouse") ?? null;
|
|
2397
2449
|
if (!position) return null;
|
|
2398
2450
|
const bodies = import_matter_js5.default.Query.point(
|
|
2399
2451
|
import_matter_js5.default.Composite.allBodies(this.engine.world),
|
|
@@ -3440,10 +3492,12 @@ var OverlayScene = class {
|
|
|
3440
3492
|
var TAG_FALLING = "falling";
|
|
3441
3493
|
var TAG_FOLLOW_WINDOW = "follow_window";
|
|
3442
3494
|
var TAG_GRABABLE = "grabable";
|
|
3495
|
+
var TAG_GRAVITY_OVERRIDE = "gravity_override";
|
|
3443
3496
|
var TAGS = {
|
|
3444
3497
|
FALLING: TAG_FALLING,
|
|
3445
3498
|
FOLLOW_WINDOW: TAG_FOLLOW_WINDOW,
|
|
3446
|
-
GRABABLE: TAG_GRABABLE
|
|
3499
|
+
GRABABLE: TAG_GRABABLE,
|
|
3500
|
+
GRAVITY_OVERRIDE: TAG_GRAVITY_OVERRIDE
|
|
3447
3501
|
};
|
|
3448
3502
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3449
3503
|
0 && (module.exports = {
|
|
@@ -3453,6 +3507,7 @@ var TAGS = {
|
|
|
3453
3507
|
TAG_FALLING,
|
|
3454
3508
|
TAG_FOLLOW_WINDOW,
|
|
3455
3509
|
TAG_GRABABLE,
|
|
3510
|
+
TAG_GRAVITY_OVERRIDE,
|
|
3456
3511
|
clearFontCache,
|
|
3457
3512
|
getGlyphData,
|
|
3458
3513
|
getKerning,
|