@blorkfield/overlay-core 0.9.0 → 0.10.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 +171 -40
- package/dist/index.cjs +300 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -35
- package/dist/index.d.ts +97 -35
- package/dist/index.js +297 -112
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,27 +22,82 @@ pnpm add @blorkfield/overlay-core
|
|
|
22
22
|
|
|
23
23
|
### Tag Based Behavior
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Tags are the source of truth for object behavior. Adding a tag activates the associated effect; removing it deactivates it. There are no separate type systems — the tag array on each object drives everything.
|
|
26
|
+
|
|
27
|
+
Import the tag constants to avoid magic strings:
|
|
26
28
|
|
|
27
29
|
```typescript
|
|
28
|
-
import { TAGS,
|
|
30
|
+
import { TAGS, TAG_STATIC, TAG_GRABABLE, TAG_FOLLOW_WINDOW } from '@blorkfield/overlay-core';
|
|
29
31
|
|
|
30
32
|
// Use individual constants
|
|
31
|
-
scene.spawnObject({ tags: [
|
|
33
|
+
scene.spawnObject({ tags: [TAG_GRABABLE], ... }); // dynamic by default
|
|
34
|
+
scene.spawnObject({ tags: [TAG_STATIC, TAG_GRABABLE], ... }); // static obstacle
|
|
32
35
|
|
|
33
36
|
// Or destructure from TAGS object
|
|
34
|
-
const {
|
|
35
|
-
scene.spawnObject({ tags: [
|
|
37
|
+
const { STATIC, GRABABLE } = TAGS;
|
|
38
|
+
scene.spawnObject({ tags: [STATIC], ... });
|
|
36
39
|
```
|
|
37
40
|
|
|
38
41
|
| Constant | Value | Behavior |
|
|
39
42
|
|----------|-------|----------|
|
|
40
|
-
| `
|
|
41
|
-
| `TAG_FOLLOW_WINDOW` / `TAGS.FOLLOW_WINDOW` | `'follow_window'` | Object
|
|
43
|
+
| `TAG_STATIC` / `TAGS.STATIC` | `'static'` | Object is a static obstacle, not affected by gravity. Without this tag, objects are dynamic by default. |
|
|
44
|
+
| `TAG_FOLLOW_WINDOW` / `TAGS.FOLLOW_WINDOW` | `'follow_window'` | Object walks toward a target when grounded (default: mouse) |
|
|
42
45
|
| `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
|
|
46
|
+
| `TAG_GRAVITY_OVERRIDE` / `TAGS.GRAVITY_OVERRIDE` | `'gravity_override'` | Object uses its own gravity vector instead of scene gravity |
|
|
47
|
+
| `TAG_SPEED_OVERRIDE` / `TAGS.SPEED_OVERRIDE` | `'speed_override'` | Multiplies movement speed for `follow_window` and future movement behaviors. Negative = run away from target. |
|
|
48
|
+
| `TAG_MASS_OVERRIDE` / `TAGS.MASS_OVERRIDE` | `'mass_override'` | Overrides the physics mass. Higher mass resists follow forces more; lower mass allows the follow force to overcome gravity. |
|
|
49
|
+
|
|
50
|
+
Tags can be added and removed at runtime to change behavior dynamically:
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
scene.addTag(id, 'static'); // freeze an object in place
|
|
54
|
+
scene.removeTag(id, 'static'); // release it to fall
|
|
55
|
+
scene.addTag(id, 'grabable'); // make it grabbable
|
|
56
|
+
scene.removeTag(id, 'grabable'); // prevent grabbing
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The `gravity_override` tag carries a value (a Vector2). Use `setObjectGravityOverride` to set the value and activate the tag, or pass it via `gravityOverride` in spawn config. Removing the tag restores scene gravity.
|
|
60
|
+
|
|
61
|
+
The `speed_override` tag carries a numeric multiplier. Use `setObjectSpeedOverride` to set the value and activate the tag, or pass it via `speedOverride` in spawn config. Removing the tag restores default speed.
|
|
62
|
+
|
|
63
|
+
The `mass_override` tag carries a numeric mass value. Use `setObjectMassOverride` to set the value and activate the tag, or pass it via `massOverride` in spawn config. Removing the tag restores the natural density-based mass.
|
|
64
|
+
|
|
65
|
+
### Entity Tags
|
|
66
|
+
|
|
67
|
+
Every spawned object is automatically assigned a permanent, human-readable **entity tag** that serves as its stable identity. The format is derived from the object's shape or image:
|
|
68
|
+
|
|
69
|
+
| Object type | Entity tag format | Example |
|
|
70
|
+
|-------------|-------------------|---------|
|
|
71
|
+
| Circle | `circle-<4hex>` | `circle-a3f2` |
|
|
72
|
+
| Rectangle | `rect-<4hex>` | `rect-b1c4` |
|
|
73
|
+
| Image-based | `<filename>-<4hex>` | `cat-e9d2` |
|
|
74
|
+
| Text letter | `letter-<char>-<4hex>` | `letter-h-7a3f` |
|
|
75
|
+
| DOM element | `dom-<4hex>` | `dom-5c21` |
|
|
76
|
+
|
|
77
|
+
Entity tags appear in `getAllTags()` alongside all other tags, making them usable as follow targets or for runtime queries. They **cannot be removed** — calling `removeTag(id, entityTag)` will log a warning and return without effect. This ensures every object always has a stable, identifiable tag.
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
### `follow_window` Tag Target
|
|
80
|
+
|
|
81
|
+
The `follow_window` tag walks an object toward a target when grounded. The default target is `'mouse'`. You can change it at any time:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// Follow the mouse (default)
|
|
85
|
+
scene.addTag(id, 'follow_window');
|
|
86
|
+
|
|
87
|
+
// Follow a named target (e.g., another entity's tag)
|
|
88
|
+
scene.setFollowWindowTarget(id, 'circle-a3f2');
|
|
89
|
+
|
|
90
|
+
// Follow any entity that has a given tag
|
|
91
|
+
scene.setFollowWindowTarget(id, 'letter-h-7a3f');
|
|
92
|
+
|
|
93
|
+
// Follow a string/word group tag
|
|
94
|
+
scene.setFollowWindowTarget(id, 'title-text');
|
|
95
|
+
|
|
96
|
+
// Stop following by removing the tag
|
|
97
|
+
scene.removeTag(id, 'follow_window');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Target resolution order: named follow targets (e.g., `'mouse'`) → object ID → first object with a matching tag.
|
|
46
101
|
|
|
47
102
|
### Pressure System
|
|
48
103
|
|
|
@@ -83,7 +138,7 @@ const { canvas, bounds } = OverlayScene.createContainer(container, {
|
|
|
83
138
|
// Create scene
|
|
84
139
|
const scene = new OverlayScene(canvas, {
|
|
85
140
|
bounds,
|
|
86
|
-
gravity: { x: 0, y: 1 },
|
|
141
|
+
gravity: { x: 0, y: -1 },
|
|
87
142
|
wrapHorizontal: true,
|
|
88
143
|
background: 'transparent'
|
|
89
144
|
});
|
|
@@ -98,22 +153,22 @@ All objects are created through `spawnObject()` (or `spawnObjectAsync()` for ima
|
|
|
98
153
|
### Basic Shapes
|
|
99
154
|
|
|
100
155
|
```typescript
|
|
101
|
-
// Circle (dynamic
|
|
156
|
+
// Circle (dynamic by default — falls with gravity)
|
|
102
157
|
scene.spawnObject({
|
|
103
158
|
x: 100,
|
|
104
159
|
y: 50,
|
|
105
160
|
radius: 20,
|
|
106
161
|
fillStyle: '#ff0000',
|
|
107
|
-
tags: ['falling']
|
|
108
162
|
});
|
|
109
163
|
|
|
110
|
-
// Rectangle (static
|
|
164
|
+
// Rectangle (static obstacle — won't move)
|
|
111
165
|
scene.spawnObject({
|
|
112
166
|
x: 200,
|
|
113
167
|
y: 300,
|
|
114
168
|
width: 100,
|
|
115
169
|
height: 20,
|
|
116
|
-
fillStyle: '#0000ff'
|
|
170
|
+
fillStyle: '#0000ff',
|
|
171
|
+
tags: ['static']
|
|
117
172
|
});
|
|
118
173
|
|
|
119
174
|
// Polygon shapes
|
|
@@ -122,7 +177,6 @@ scene.spawnObject({
|
|
|
122
177
|
y: 100,
|
|
123
178
|
radius: 25,
|
|
124
179
|
fillStyle: '#00ff00',
|
|
125
|
-
tags: ['falling'],
|
|
126
180
|
shape: { type: 'hexagon' }
|
|
127
181
|
});
|
|
128
182
|
```
|
|
@@ -137,7 +191,7 @@ const id = await scene.spawnObjectAsync({
|
|
|
137
191
|
y: 100,
|
|
138
192
|
imageUrl: '/images/coin.png',
|
|
139
193
|
size: 50,
|
|
140
|
-
tags: ['
|
|
194
|
+
tags: ['grabable'] // dynamic by default
|
|
141
195
|
});
|
|
142
196
|
```
|
|
143
197
|
|
|
@@ -242,14 +296,16 @@ const result = await scene.addTTFTextObstacles({
|
|
|
242
296
|
});
|
|
243
297
|
```
|
|
244
298
|
|
|
299
|
+
Text obstacles default to `isStatic: true` — they are static obstacles that things fall onto. Pass `isStatic: false` to spawn falling text instead.
|
|
300
|
+
|
|
245
301
|
### Managing Text Obstacles
|
|
246
302
|
|
|
247
303
|
```typescript
|
|
248
|
-
// Spawn text that immediately falls (
|
|
304
|
+
// Spawn text that immediately falls (isStatic: false)
|
|
249
305
|
const result = await scene.spawnFallingTextObstacles(config);
|
|
250
306
|
const result = await scene.spawnFallingTTFTextObstacles(config);
|
|
251
307
|
|
|
252
|
-
// Release text obstacles (
|
|
308
|
+
// Release static text obstacles (removes 'static' tag so they fall)
|
|
253
309
|
scene.releaseTextObstacles(wordTag);
|
|
254
310
|
|
|
255
311
|
// Release letters one by one with delay
|
|
@@ -280,7 +336,7 @@ scene.setEffect({
|
|
|
280
336
|
objectConfig: {
|
|
281
337
|
radius: 15,
|
|
282
338
|
fillStyle: '#4a90d9',
|
|
283
|
-
tags
|
|
339
|
+
// dynamic by default — no tags needed
|
|
284
340
|
},
|
|
285
341
|
probability: 1,
|
|
286
342
|
minScale: 0.8,
|
|
@@ -342,14 +398,24 @@ scene.removeAllObjects();
|
|
|
342
398
|
scene.removeAll(); // Alias for removeAllObjects
|
|
343
399
|
scene.removeAllByTag('tag'); // Alias for removeObjectsByTag
|
|
344
400
|
|
|
345
|
-
// Add or remove tags
|
|
346
|
-
scene.addTag(id, '
|
|
347
|
-
scene.
|
|
348
|
-
scene.
|
|
401
|
+
// Add or remove tags — tags drive behavior, changes take effect immediately
|
|
402
|
+
scene.addTag(id, 'grabable');
|
|
403
|
+
scene.removeTag(id, 'static'); // releases a static object to fall
|
|
404
|
+
scene.addTag(id, 'static'); // freezes a dynamic object in place
|
|
405
|
+
scene.addFallingTag(id); // convenience: removes 'static', adds 'grabable'
|
|
406
|
+
scene.setFollowWindowTarget(id, 'mouse'); // change what a follow_window object walks toward
|
|
407
|
+
scene.setObjectSpeedOverride(id, 2); // double movement speed (negative = run away)
|
|
408
|
+
scene.setObjectSpeedOverride(id, null); // remove speed override
|
|
409
|
+
scene.setObjectMassOverride(id, 50); // heavy object resists follow force
|
|
410
|
+
scene.setObjectMassOverride(id, null); // restore natural mass
|
|
411
|
+
scene.setPosition(id, { x: 400, y: 300 }); // teleport object
|
|
412
|
+
scene.setVelocity(id, { x: 10, y: 0 }); // launch object rightward
|
|
413
|
+
scene.setObjectAngularVelocity(id, Math.PI); // set spin (rad/s)
|
|
414
|
+
scene.setObjectScale(id, 2, 2); // scale x and y independently
|
|
349
415
|
|
|
350
416
|
// Get object info
|
|
351
417
|
const ids = scene.getObjectIds();
|
|
352
|
-
const tagged = scene.getObjectIdsByTag('
|
|
418
|
+
const tagged = scene.getObjectIdsByTag('static');
|
|
353
419
|
const allTags = scene.getAllTags();
|
|
354
420
|
|
|
355
421
|
// Get full object state
|
|
@@ -362,13 +428,25 @@ const objs = scene.getObjectsByTag('tag'); // Returns ObjectState[]
|
|
|
362
428
|
```typescript
|
|
363
429
|
// Apply force to objects
|
|
364
430
|
scene.applyForce(id, { x: 0.01, y: -0.02 });
|
|
365
|
-
scene.applyForceToTag('
|
|
431
|
+
scene.applyForceToTag('grabable', { x: 0.005, y: 0 });
|
|
366
432
|
|
|
367
|
-
// Set velocity directly
|
|
368
|
-
scene.setVelocity(id, { x: 5, y:
|
|
433
|
+
// Set velocity directly (physical convention: positive y = upward, negative y = downward)
|
|
434
|
+
scene.setVelocity(id, { x: 5, y: 10 }); // moving right and upward
|
|
435
|
+
scene.setVelocity(id, { x: 0, y: -10 }); // moving downward
|
|
436
|
+
scene.setVelocity(id, { x: 0, y: 0 }); // stop all movement
|
|
369
437
|
|
|
370
|
-
// Set position directly
|
|
438
|
+
// Set position directly (screen pixels, y=0 at top)
|
|
371
439
|
scene.setPosition(id, { x: 100, y: 200 });
|
|
440
|
+
|
|
441
|
+
// Set angular velocity (spin) in radians/second
|
|
442
|
+
// Positive = counter-clockwise, negative = clockwise
|
|
443
|
+
scene.setObjectAngularVelocity(id, Math.PI); // half-rotation per second CCW
|
|
444
|
+
scene.setObjectAngularVelocity(id, 0); // stop spinning
|
|
445
|
+
|
|
446
|
+
// Scale an object (updates both physics shape and sprite rendering)
|
|
447
|
+
scene.setObjectScale(id, 2, 2); // double size uniformly
|
|
448
|
+
scene.setObjectScale(id, 2, 0.5); // stretch wide, squash tall
|
|
449
|
+
scene.setObjectScale(id, 1, 1); // restore original size
|
|
372
450
|
```
|
|
373
451
|
|
|
374
452
|
## Mouse Position and Grab API
|
|
@@ -427,7 +505,7 @@ const currentGrab = scene.getGrabbedObject(); // Returns ID or null
|
|
|
427
505
|
```typescript
|
|
428
506
|
const scene = new OverlayScene(canvas, {
|
|
429
507
|
bounds: { top: 0, bottom: 600, left: 0, right: 800 },
|
|
430
|
-
gravity: { x: 0, y: 1 },
|
|
508
|
+
gravity: { x: 0, y: -1 },
|
|
431
509
|
wrapHorizontal: true,
|
|
432
510
|
debug: false,
|
|
433
511
|
background: '#16213e',
|
|
@@ -508,7 +586,7 @@ Called every frame with all dynamic object states:
|
|
|
508
586
|
|
|
509
587
|
```typescript
|
|
510
588
|
scene.onUpdate((data) => {
|
|
511
|
-
// data.objects contains all dynamic (
|
|
589
|
+
// data.objects contains all dynamic objects (those without the 'static' tag)
|
|
512
590
|
for (const obj of data.objects) {
|
|
513
591
|
console.log(obj.id, obj.x, obj.y, obj.angle, obj.tags);
|
|
514
592
|
}
|
|
@@ -654,7 +732,7 @@ scene.destroy(); // Clean up resources
|
|
|
654
732
|
|
|
655
733
|
### Per-Object Gravity Override
|
|
656
734
|
|
|
657
|
-
|
|
735
|
+
Dynamic objects can have their own gravity vector instead of the scene gravity. Set it via `gravityOverride` in spawn config, or change it at runtime with `setObjectGravityOverride`. This automatically adds or removes the `gravity_override` tag.
|
|
658
736
|
|
|
659
737
|
```typescript
|
|
660
738
|
// Spawn a floaty object that drifts upward
|
|
@@ -662,7 +740,7 @@ scene.spawnObject({
|
|
|
662
740
|
x: 200, y: 300,
|
|
663
741
|
radius: 20,
|
|
664
742
|
fillStyle: '#4a90d9',
|
|
665
|
-
tags: ['
|
|
743
|
+
tags: ['grabable'],
|
|
666
744
|
gravityOverride: { x: 0, y: -0.3 } // floats upward
|
|
667
745
|
});
|
|
668
746
|
|
|
@@ -671,23 +749,76 @@ scene.spawnObject({
|
|
|
671
749
|
x: 400, y: 200,
|
|
672
750
|
radius: 15,
|
|
673
751
|
fillStyle: '#e94560',
|
|
674
|
-
tags: ['falling'],
|
|
675
752
|
gravityOverride: { x: 0, y: 0 }
|
|
676
753
|
});
|
|
677
754
|
|
|
678
755
|
// Change or clear a gravity override at runtime
|
|
679
756
|
scene.setObjectGravityOverride(id, { x: 0.5, y: 0 }); // drift sideways
|
|
680
757
|
scene.setObjectGravityOverride(id, null); // restore scene gravity
|
|
758
|
+
|
|
759
|
+
// removeTag works too — setObjectGravityOverride(id, null) calls it internally
|
|
760
|
+
scene.removeTag(id, 'gravity_override'); // restores scene gravity
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Tags are the source of truth for all behavior. Boolean tags (presence = active, absence = inactive). `gravity_override` additionally carries a Vector2 value managed via `setObjectGravityOverride` or `gravityOverride` in spawn config.
|
|
764
|
+
|
|
765
|
+
| Tag | Behavior |
|
|
766
|
+
|-----|----------|
|
|
767
|
+
| `static` | Static obstacle, not affected by gravity. Absent by default — objects are dynamic unless tagged static. |
|
|
768
|
+
| `grabable` | Can be grabbed and dragged with the mouse |
|
|
769
|
+
| `follow_window` | Always applies a directional force toward its target (in all axes). Gravity determines whether the entity can actually reach targets above/below it. |
|
|
770
|
+
| `gravity_override` | Uses its own gravity vector instead of scene gravity (value set via `setObjectGravityOverride`) |
|
|
771
|
+
| `speed_override` | Multiplies movement speed for `follow_window` and future movement behaviors (value set via `setObjectSpeedOverride`). Negative = runs away from target. Default multiplier: 1 |
|
|
772
|
+
| `mass_override` | Overrides physics mass (value set via `setObjectMassOverride`). Higher mass resists follow forces; lower mass may allow entity to overcome gravity. |
|
|
773
|
+
|
|
774
|
+
### Speed Override
|
|
775
|
+
|
|
776
|
+
Objects with `follow_window` move toward their target at default speed. `speed_override` multiplies this force. Negative values reverse the direction, causing the object to run away.
|
|
777
|
+
|
|
778
|
+
```typescript
|
|
779
|
+
// Spawn a fast follower
|
|
780
|
+
scene.spawnObject({
|
|
781
|
+
tags: ['follow_window'],
|
|
782
|
+
speedOverride: 3, // 3× normal speed — automatically adds 'speed_override' tag
|
|
783
|
+
// ...
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// Spawn a coward that runs away from the mouse
|
|
787
|
+
scene.spawnObject({
|
|
788
|
+
tags: ['follow_window'],
|
|
789
|
+
speedOverride: -2, // flees at 2× speed
|
|
790
|
+
// ...
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Change speed at runtime
|
|
794
|
+
scene.setObjectSpeedOverride(id, 5); // very fast follower
|
|
795
|
+
scene.setObjectSpeedOverride(id, -1); // now runs away at normal speed
|
|
796
|
+
scene.setObjectSpeedOverride(id, null); // remove override, restore default speed
|
|
681
797
|
```
|
|
682
798
|
|
|
683
|
-
|
|
799
|
+
### Mass Override
|
|
800
|
+
|
|
801
|
+
Entities have a default density of `0.005`, which gives typical sizes a mass of ~6–20 units — enough that normal gravity prevents the follow force from lifting them vertically. `mass_override` lets you change this at spawn or runtime.
|
|
802
|
+
|
|
803
|
+
```typescript
|
|
804
|
+
// Light object — follow force can overcome normal gravity, moves freely in all directions
|
|
805
|
+
scene.spawnObject({
|
|
806
|
+
tags: ['follow_window'],
|
|
807
|
+
massOverride: 1,
|
|
808
|
+
// ...
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Very heavy object — barely moves toward target under normal gravity
|
|
812
|
+
scene.spawnObject({
|
|
813
|
+
tags: ['follow_window'],
|
|
814
|
+
massOverride: 100,
|
|
815
|
+
// ...
|
|
816
|
+
});
|
|
684
817
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
| `follow_window` | boolean | Walks toward mouse when grounded |
|
|
690
|
-
| `gravity_override` | Vector2 | Uses own gravity instead of scene gravity |
|
|
818
|
+
// Change or clear mass at runtime
|
|
819
|
+
scene.setObjectMassOverride(id, 50); // now heavy
|
|
820
|
+
scene.setObjectMassOverride(id, null); // restore natural mass
|
|
821
|
+
```
|
|
691
822
|
|
|
692
823
|
## Examples
|
|
693
824
|
|