@babylonjsmarket/arcade 0.3.7 → 0.3.8
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/package.json +2 -2
- package/src/Components/ArcCamera/meta.json +4 -0
- package/src/Components/Bullet/meta.json +15 -3
- package/src/Components/BulletPool/meta.json +15 -3
- package/src/Components/Bumper/meta.json +14 -3
- package/src/Components/CameraFollow/CameraFollow.contract.test.ts +2 -1
- package/src/Components/CameraFollow/CameraFollow.test.ts +4 -3
- package/src/Components/Enemy/meta.json +21 -3
- package/src/Components/EnemySpawner/meta.json +15 -3
- package/src/Components/EntityPool/meta.json +4 -0
- package/src/Components/Flipper/meta.json +17 -3
- package/src/Components/Health/meta.json +4 -0
- package/src/Components/KeyboardMover/KeyboardMover.test.ts +4 -3
- package/src/Components/LineOfSight/meta.json +4 -0
- package/src/Components/Mesh/meta.json +4 -0
- package/src/Components/MeshPrimitive/meta.json +5 -1
- package/src/Components/MouseHole/meta.json +13 -3
- package/src/Components/Obstacle/meta.json +4 -0
- package/src/Components/Physics/meta.json +4 -0
- package/src/Components/PinballBuilder/meta.json +19 -3
- package/src/Components/PinballBuilderInput/PinballBuilderInput.test.ts +1 -1
- package/src/Components/PinballBuilderInput/meta.json +14 -3
- package/src/Components/PinballCamera/PinballCamera.test.ts +7 -0
- package/src/Components/PinballCamera/meta.json +14 -3
- package/src/Components/PinballLayout/meta.json +14 -3
- package/src/Components/PinballTable/meta.json +13 -3
- package/src/Components/Plunger/meta.json +15 -3
- package/src/Components/Score/meta.json +5 -1
- package/src/Components/ShooterCamera/ShooterCamera.test.ts +7 -0
- package/src/Components/ShooterCamera/meta.json +14 -3
- package/src/Components/SkeletonAnimator/meta.json +4 -0
- package/src/Components/Spinner/meta.json +14 -3
- package/src/Components/TwinStickShooter/TwinStickShooter.test.ts +7 -0
- package/src/Components/TwinStickShooter/meta.json +17 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babylonjsmarket/arcade",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Reusable arcade-style components (mesh, camera, lights, input, physics, scoring, animation) for the @babylonjsmarket/ecs framework",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"prepublishOnly": "npm run build && npm run test"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@babylonjsmarket/ecs": "
|
|
92
|
+
"@babylonjsmarket/ecs": "^0.5.3",
|
|
93
93
|
"@clack/prompts": "^0.10.0",
|
|
94
94
|
"mri": "^1.2.0",
|
|
95
95
|
"picocolors": "^1.1.1"
|
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
"name": "Bullet",
|
|
3
3
|
"description": "Drives pooled bullet entities (MeshPrimitive + Bullet). Each frame the system advances every live bullet along its direction, expires it on lifetime, on leaving the arena, or when its travel segment crosses a bullet-blocking Obstacle wall, and tests it against live enemies. On a hit it emits `bullet.hit` (for observers like SFX/flashes, carrying the shot direction) then routes damage through HealthSystem via `health.damage`, and releases the bullet back to its pool with `world.removeEntity` rather than destroying it.",
|
|
4
4
|
"category": "shooter",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"BulletComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"BulletSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"Health",
|
|
14
|
+
"Obstacle"
|
|
15
|
+
],
|
|
16
|
+
"tags": [
|
|
17
|
+
"core",
|
|
18
|
+
"combat"
|
|
19
|
+
]
|
|
8
20
|
}
|
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
"name": "BulletPool",
|
|
3
3
|
"description": "Registers a framework entity pool of reusable bullet entities (MeshPrimitive + Bullet) so firing never allocates an entity or mesh. On `bulletPool.fire` it acquires a parked slot and resets its direction/speed/lifetime/damage + muzzle position (with an optional per-shot color), then BulletSystem flies it and parks it again on expiry or hit. Emits `bulletPool.ready` once the pool is pre-built. Parking is the World `active` flag; the park-aware MeshPrimitiveSystem keeps each bullet mesh alive (hidden) for reuse.",
|
|
4
4
|
"category": "shooter",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"BulletPoolComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"BulletPoolSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"Bullet"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"core",
|
|
17
|
+
"combat",
|
|
18
|
+
"spawning"
|
|
19
|
+
]
|
|
8
20
|
}
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "Bumper",
|
|
3
3
|
"description": "Proximity-based scoring trigger. Each bumper owns a hitRadius and a point value; the system runs an XZ distance check against every ball-tagged entity and, on entry, emits bumper.hit plus score.add to award points to ownerEntity. A per-bumper-per-ball debounce keeps a slow rolling ball from re-triggering every frame — the flag only clears once the ball moves beyond releaseRadius. Renderer-agnostic: it reads MeshPrimitive positions only and never touches Babylon or Havok, leaving the physical deflection to a Physics component on the same entity.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"BumperComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"BumperSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"Score"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball",
|
|
17
|
+
"physics"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CameraFollowEvents,
|
|
6
6
|
} from './CameraFollow';
|
|
7
7
|
import { ArcCameraComponent } from '../ArcCamera/ArcCamera';
|
|
8
|
+
import { getArcCameraRuntime } from '../ArcCamera/ArcCamera.core';
|
|
8
9
|
|
|
9
10
|
runMechanicContract({
|
|
10
11
|
name: 'CameraFollow',
|
|
@@ -30,7 +31,7 @@ runMechanicContract({
|
|
|
30
31
|
const arcEntity = world.createEntity();
|
|
31
32
|
const arc = new ArcCameraComponent();
|
|
32
33
|
arc.handle = renderer.createArcCamera(arcEntity.id, arc.toSpec());
|
|
33
|
-
arc.initialized = true;
|
|
34
|
+
getArcCameraRuntime(arc).initialized = true;
|
|
34
35
|
arcEntity.add(arc);
|
|
35
36
|
|
|
36
37
|
// Stub a world position the mesh handle reports through the adapter.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
2
|
import { CameraFollowComponent, CameraFollowSystem } from './CameraFollow';
|
|
3
3
|
import { ArcCameraComponent } from '../ArcCamera/ArcCamera';
|
|
4
|
+
import { getArcCameraRuntime } from '../ArcCamera/ArcCamera.core';
|
|
4
5
|
import { EventBus, World } from '@babylonjsmarket/ecs';
|
|
5
6
|
import { MockRendererAdapter } from '@babylonjsmarket/ecs';
|
|
6
7
|
import type { MeshHandle } from '@babylonjsmarket/ecs/renderer-types';
|
|
@@ -60,7 +61,7 @@ describe('CameraFollowSystem', () => {
|
|
|
60
61
|
const arcEntity = world.createEntity('Camera');
|
|
61
62
|
const arcComp = new ArcCameraComponent();
|
|
62
63
|
arcComp.handle = renderer.createArcCamera(arcEntity.id, arcComp.toSpec());
|
|
63
|
-
arcComp.initialized = true;
|
|
64
|
+
getArcCameraRuntime(arcComp).initialized = true;
|
|
64
65
|
arcEntity.add(arcComp);
|
|
65
66
|
|
|
66
67
|
const followEntity = world.createEntity('Follow');
|
|
@@ -76,7 +77,7 @@ describe('CameraFollowSystem', () => {
|
|
|
76
77
|
const arcEntity = world.createEntity('Camera');
|
|
77
78
|
const arcComp = new ArcCameraComponent();
|
|
78
79
|
arcComp.handle = renderer.createArcCamera(arcEntity.id, arcComp.toSpec());
|
|
79
|
-
arcComp.initialized = true;
|
|
80
|
+
getArcCameraRuntime(arcComp).initialized = true;
|
|
80
81
|
arcEntity.add(arcComp);
|
|
81
82
|
|
|
82
83
|
// Fake a Player mesh and emit the creation event the system listens for.
|
|
@@ -106,7 +107,7 @@ describe('CameraFollowSystem', () => {
|
|
|
106
107
|
const arcEntity = world.createEntity('Camera');
|
|
107
108
|
const arcComp = new ArcCameraComponent();
|
|
108
109
|
arcComp.handle = renderer.createArcCamera(arcEntity.id, arcComp.toSpec());
|
|
109
|
-
arcComp.initialized = true;
|
|
110
|
+
getArcCameraRuntime(arcComp).initialized = true;
|
|
110
111
|
arcEntity.add(arcComp);
|
|
111
112
|
|
|
112
113
|
const fakeHandle = renderer.createMesh('Player', { kind: 'capsule' });
|
|
@@ -2,7 +2,25 @@
|
|
|
2
2
|
"name": "Enemy",
|
|
3
3
|
"description": "Chases the entity tagged `player`, separates from other enemies so they don't stack, and deals contact damage via `health.damage`. Takes bullet hits as `bullet.hit`; on death (HealthSystem's `health.died`) it emits `enemy.killed` and `score.add`. Pooled — instead of being destroyed it asks EntityPool to re-park its slot (`entityPool.release`). Optional SkeletonAnimator support drives walk/attack/death clips, directional fall animations, and a knockback slide that barrels the corpse into the swarm; chase vs wander is driven by LineOfSight's acquired/lost events.",
|
|
4
4
|
"category": "shooter",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"EnemyComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"EnemySystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"EntityPool",
|
|
14
|
+
"SkeletonAnimator",
|
|
15
|
+
"Health",
|
|
16
|
+
"Score",
|
|
17
|
+
"Bullet",
|
|
18
|
+
"Obstacle",
|
|
19
|
+
"LineOfSight"
|
|
20
|
+
],
|
|
21
|
+
"tags": [
|
|
22
|
+
"core",
|
|
23
|
+
"ai",
|
|
24
|
+
"combat"
|
|
25
|
+
]
|
|
8
26
|
}
|
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
"name": "EnemySpawner",
|
|
3
3
|
"description": "Pulses enemies out from its own world position on a timer, up to a per-spawner live cap. It never creates entities itself — it emits `entityPool.spawn` naming its pool, and EntityPool hands out a reused enemy. The cap stays a true live count by tracking the pool's `entityPool.spawned` / `entityPool.released` events (a release covers both a kill and the pool recycling under pressure). Per-spawn hp/speed/color ride along for games that apply overrides on acquire.",
|
|
4
4
|
"category": "shooter",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"EnemySpawnerComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"EnemySpawnerSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"EntityPool"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"core",
|
|
17
|
+
"spawning",
|
|
18
|
+
"ai"
|
|
19
|
+
]
|
|
8
20
|
}
|
|
@@ -2,7 +2,21 @@
|
|
|
2
2
|
"name": "Flipper",
|
|
3
3
|
"description": "Keyboard-driven kinematic flipper bat. Holding the trigger key (default KeyZ) ramps the bat's Y rotation toward flipAngle at flipSpeed and releasing returns it to restAngle, driving the MeshPrimitive's pose each frame so Havok computes the contact impulse that pings the ball. Opt into taperedGeometry to rebuild the mesh as a trapezoidal prism hinged at the pivot end and inflate the collider against tunneling; pair with Physics autoCreate:false. Emits flipper.colliderRebuilt and listens for flipper.rebuildColliders so debug panels can refit collider extents live. Ships with FlipperColliderDebugger, a Babylon wireframe overlay of the inflated collider toggled by a hotkey.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"FlipperComponent",
|
|
7
|
+
"FlipperColliderDebuggerComponent"
|
|
8
|
+
],
|
|
9
|
+
"systems": [
|
|
10
|
+
"FlipperSystem",
|
|
11
|
+
"FlipperColliderDebuggerSystem"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": [
|
|
14
|
+
"MeshPrimitive",
|
|
15
|
+
"Physics"
|
|
16
|
+
],
|
|
17
|
+
"tags": [
|
|
18
|
+
"pinball",
|
|
19
|
+
"controller",
|
|
20
|
+
"input"
|
|
21
|
+
]
|
|
8
22
|
}
|
|
@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
|
|
2
2
|
import { KeyboardMoverComponent, KeyboardMoverSystem } from './KeyboardMover';
|
|
3
3
|
import { MeshPrimitiveComponent } from '../MeshPrimitive/MeshPrimitive.core';
|
|
4
4
|
import { ArcCameraComponent } from '../ArcCamera/ArcCamera';
|
|
5
|
+
import { getArcCameraRuntime } from '../ArcCamera/ArcCamera.core';
|
|
5
6
|
import { EventBus, World } from '@babylonjsmarket/ecs';
|
|
6
7
|
import { MockRendererAdapter } from '@babylonjsmarket/ecs';
|
|
7
8
|
|
|
@@ -29,7 +30,7 @@ describe('KeyboardMover', () => {
|
|
|
29
30
|
const camEntity = world.createEntity('Camera');
|
|
30
31
|
const arc = new ArcCameraComponent({ alpha: 0 });
|
|
31
32
|
arc.handle = renderer.createArcCamera(camEntity.id, arc.toSpec());
|
|
32
|
-
arc.initialized = true;
|
|
33
|
+
getArcCameraRuntime(arc).initialized = true;
|
|
33
34
|
camEntity.add(arc);
|
|
34
35
|
|
|
35
36
|
const playerEntity = world.createEntity('Player');
|
|
@@ -54,7 +55,7 @@ describe('KeyboardMover', () => {
|
|
|
54
55
|
const camEntity = world.createEntity('Camera');
|
|
55
56
|
const arc = new ArcCameraComponent({ alpha: 0 });
|
|
56
57
|
arc.handle = renderer.createArcCamera(camEntity.id, arc.toSpec());
|
|
57
|
-
arc.initialized = true;
|
|
58
|
+
getArcCameraRuntime(arc).initialized = true;
|
|
58
59
|
camEntity.add(arc);
|
|
59
60
|
|
|
60
61
|
const playerEntity = world.createEntity('Player');
|
|
@@ -79,7 +80,7 @@ describe('KeyboardMover', () => {
|
|
|
79
80
|
const camEntity = world.createEntity('Camera');
|
|
80
81
|
const arc = new ArcCameraComponent({ alpha: 0 });
|
|
81
82
|
arc.handle = renderer.createArcCamera(camEntity.id, arc.toSpec());
|
|
82
|
-
arc.initialized = true;
|
|
83
|
+
getArcCameraRuntime(arc).initialized = true;
|
|
83
84
|
camEntity.add(arc);
|
|
84
85
|
|
|
85
86
|
const playerEntity = world.createEntity('Player');
|
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
"name": "MouseHole",
|
|
3
3
|
"description": "Saucer / scoop that captures the ball, holds it briefly, then kicks it back into play. A proximity check against the ball's XZ position triggers capture: the system emits mouseHole.captured and score.add (points to ownerEntity), pins the ball to the hole with its velocity zeroed for holdSeconds, then on release applies kickoutVelocity via the physics body and emits mouseHole.released. A short post-kickout cooldown stops the hole from immediately re-capturing the ball it just launched.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"MouseHoleComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"MouseHoleSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"Score"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball"
|
|
17
|
+
]
|
|
8
18
|
}
|
|
@@ -2,7 +2,23 @@
|
|
|
2
2
|
"name": "PinballBuilder",
|
|
3
3
|
"description": "Data-driven, in-game placer for pinball elements. Listens for `pinballBuilder.addBumper`/`addWall`/`addRail`/`addRamp`/`addSpinner`/`addMouseHole` and spawns a fully-wired entity (MeshPrimitive + Shadow + Physics, plus the matching Bumper/Spinner/MouseHole behavior) at the requested table coordinates, emitting `pinballBuilder.entityAdded`. Also handles freehand draw mode: `pinballBuilder.setDrawMode` toggles drawing (echoed via `pinballBuilder.drawModeChanged`) and `pinballBuilder.drawPath` smooths a raw pointer polyline with a Catmull-Rom spline, resamples it at uniform arc-length, and chains one rail/wall segment per interval to turn a freehand line into a clean curved track.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PinballBuilderComponent",
|
|
7
|
+
"PinballBuilderDebuggerComponent"
|
|
8
|
+
],
|
|
9
|
+
"systems": [
|
|
10
|
+
"PinballBuilderSystem",
|
|
11
|
+
"PinballBuilderDebuggerSystem"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": [
|
|
14
|
+
"MeshPrimitive",
|
|
15
|
+
"Bumper",
|
|
16
|
+
"Spinner",
|
|
17
|
+
"MouseHole",
|
|
18
|
+
"PinballTable"
|
|
19
|
+
],
|
|
20
|
+
"tags": [
|
|
21
|
+
"pinball",
|
|
22
|
+
"builder"
|
|
23
|
+
]
|
|
8
24
|
}
|
|
@@ -42,7 +42,7 @@ describe('PinballBuilderInputSystem', () => {
|
|
|
42
42
|
const defaultPick = (x: number, y: number): PickResult =>
|
|
43
43
|
({ x: x / 10, y: 0, z: y / 10, meshId: 'mesh_Playfield' }) as unknown as PickResult;
|
|
44
44
|
|
|
45
|
-
function setup(pick = defaultPick) {
|
|
45
|
+
function setup(pick: (x: number, y: number) => PickResult | null = defaultPick) {
|
|
46
46
|
eventBus = new EventBus();
|
|
47
47
|
canvas = document.createElement('canvas');
|
|
48
48
|
canvas.getBoundingClientRect = () => ({ left: 0, top: 0, width: 800, height: 600 }) as DOMRect;
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "PinballBuilderInput",
|
|
3
3
|
"description": "Canvas-side counterpart to PinballBuilder's freehand drawing. On `pinballBuilder.setDrawMode` { enabled, kind } it attaches (or detaches) pointer listeners to the rendering canvas, projects each sample onto the playfield mesh via the renderer's screen-point pick (filtered by pickMeshPrefix), accumulates a jitter-filtered XZ polyline, and emits `pinballBuilder.drawPath` on pointer release for PinballBuilder to turn into a chained rail or wall. Parameter-free singleton; declare one on any entity to activate the system.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PinballBuilderInputComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"PinballBuilderInputSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"PinballBuilder"
|
|
13
|
+
],
|
|
14
|
+
"tags": [
|
|
15
|
+
"pinball",
|
|
16
|
+
"builder",
|
|
17
|
+
"input"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -33,7 +33,14 @@ describe('PinballCameraSystem', () => {
|
|
|
33
33
|
alpha: Math.PI / 2,
|
|
34
34
|
beta: Math.PI / 3,
|
|
35
35
|
radius: 15,
|
|
36
|
+
minRadius: 5,
|
|
37
|
+
maxRadius: 50,
|
|
38
|
+
minBeta: 0.1,
|
|
39
|
+
maxBeta: Math.PI / 2 - 0.1,
|
|
36
40
|
target: [0, 0, 0],
|
|
41
|
+
inertia: 0.9,
|
|
42
|
+
wheelPrecision: 50,
|
|
43
|
+
angularSensibility: 1000,
|
|
37
44
|
});
|
|
38
45
|
e.add(arc);
|
|
39
46
|
return { entity: e, arc };
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "PinballCamera",
|
|
3
3
|
"description": "Locks a fixed top-down pinball view on top of a sibling ArcCamera. Detaches the ArcRotateCamera's pointer controls and overrides its FOV (retrying each frame until the camera handle exists), then listens for `pinballTable.resized` and recomputes the fit distance so the whole table frames neatly at any dimensions, padded by marginFactor. Drives ArcCamera's `distance` (not the raw radius) so a tuning panel can still override zoom. Attach next to ArcCamera on the same entity.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PinballCameraComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"PinballCameraSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"ArcCamera",
|
|
13
|
+
"PinballTable"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball",
|
|
17
|
+
"camera"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "PinballLayout",
|
|
3
3
|
"description": "Declarative proportional placement for table elements. Each entity expresses its position as `x = xCoeff * tableWidth + xOffset` and `z = zCoeff * tableDepth + zOffset`, plus optional X/Z mesh scaling tied to the same dims (scaleWith: both/width/depth/none). The system listens for `pinballTable.resized` and, in one pass, rewrites every layout entity's MeshPrimitive position and scale through the renderer — so resizing the table from the viz panel keeps walls, bumpers, flippers and the playfield correctly arranged.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PinballLayoutComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"PinballLayoutSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"PinballTable"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball",
|
|
17
|
+
"builder"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -2,7 +2,17 @@
|
|
|
2
2
|
"name": "PinballTable",
|
|
3
3
|
"description": "Single source of truth for the playfield's width (X) and depth (Z). One PinballTable entity per scene; the system broadcasts the seed dimensions once as `pinballTable.resized` on init, and on every `pinballTable.resize` request (from the viz panel) it updates the dims and re-broadcasts `pinballTable.resized` so dependent entities (walls, slingshots, flippers, inlanes) reposition and the camera reframes. Defaults match a regulation 20.25\"×42\" Williams/Bally playfield.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PinballTableComponent",
|
|
7
|
+
"PinballTableDebuggerComponent"
|
|
8
|
+
],
|
|
9
|
+
"systems": [
|
|
10
|
+
"PinballTableSystem",
|
|
11
|
+
"PinballTableDebuggerSystem"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": [],
|
|
14
|
+
"tags": [
|
|
15
|
+
"pinball",
|
|
16
|
+
"builder"
|
|
17
|
+
]
|
|
8
18
|
}
|
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
"name": "Plunger",
|
|
3
3
|
"description": "Owns the pinball's ball lifecycle. Auto-launches the ball the moment its Havok body appears, re-launches on drain (when the ball falls below drainY or rolls past the bottom-edge drain Z), and fires on a trigger key (default Space) for a manual re-plunge — each launch teleports the ball to spawnPosition and kicks it down-table with launchVelocityZ plus sideways jitter. Also installs the table's tilted scene gravity and Havok's 240 Hz sub-time-step once physics is alive, and listens to pinballTable.resized to keep the drain threshold lined up with the table's bottom edge. Optionally swaps the ball's material for a chrome-mirror PBR.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"PlungerComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"PlungerSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"PinballTable"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball",
|
|
17
|
+
"controller",
|
|
18
|
+
"input"
|
|
19
|
+
]
|
|
8
20
|
}
|
|
@@ -61,7 +61,14 @@ describe('ShooterCameraSystem', () => {
|
|
|
61
61
|
alpha: Math.PI / 2,
|
|
62
62
|
beta: Math.PI / 3,
|
|
63
63
|
radius: 15,
|
|
64
|
+
minRadius: 5,
|
|
65
|
+
maxRadius: 50,
|
|
66
|
+
minBeta: 0.1,
|
|
67
|
+
maxBeta: Math.PI / 2 - 0.1,
|
|
64
68
|
target: [0, 0, 0],
|
|
69
|
+
inertia: 0.9,
|
|
70
|
+
wheelPrecision: 50,
|
|
71
|
+
angularSensibility: 1000,
|
|
65
72
|
});
|
|
66
73
|
e.add(arc);
|
|
67
74
|
return { entity: e, arc };
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "ShooterCamera",
|
|
3
3
|
"description": "Top-down follow camera companion for twin-stick / top-down shooters. Configures a sibling ArcCamera rather than creating its own: it resolves the live ArcCameraComponent and writes its data — a steep look-down beta + fixed distance, the follow target (so ArcCameraSystem, the single target writer, tracks the player) — then once detaches the orbit controls so the player can't unhinge the framing and overrides the FOV. It no longer reads the player mesh or writes the camera target itself, so it never races ArcCameraSystem. Attach it next to ArcCamera on the same entity.",
|
|
4
4
|
"category": "shooter",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"ShooterCameraComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"ShooterCameraSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"ArcCamera"
|
|
13
|
+
],
|
|
14
|
+
"tags": [
|
|
15
|
+
"shooter",
|
|
16
|
+
"twin-stick-shooter",
|
|
17
|
+
"camera"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -2,7 +2,18 @@
|
|
|
2
2
|
"name": "Spinner",
|
|
3
3
|
"description": "A freely-spinning bar mounted to the playfield. When a ball passes within hitRadius the system imparts angular velocity to the bar (direction set by which side the ball crossed), then integrates that spin each frame with drag decaying it toward zero. Purely kinematic — it rotates the MeshPrimitive directly rather than using a Havok hinge. Every completed revolution emits spinner.spun and score.add for pointsPerSpin to ownerEntity, so one strong hit rewards multiple points as the bar coasts.",
|
|
4
4
|
"category": "pinball",
|
|
5
|
-
"components": [
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
"components": [
|
|
6
|
+
"SpinnerComponent"
|
|
7
|
+
],
|
|
8
|
+
"systems": [
|
|
9
|
+
"SpinnerSystem"
|
|
10
|
+
],
|
|
11
|
+
"dependencies": [
|
|
12
|
+
"MeshPrimitive",
|
|
13
|
+
"Score"
|
|
14
|
+
],
|
|
15
|
+
"tags": [
|
|
16
|
+
"pinball",
|
|
17
|
+
"physics"
|
|
18
|
+
]
|
|
8
19
|
}
|
|
@@ -45,7 +45,14 @@ describe('TwinStickShooterSystem', () => {
|
|
|
45
45
|
alpha: -Math.PI / 2,
|
|
46
46
|
beta: Math.PI / 3,
|
|
47
47
|
radius: 15,
|
|
48
|
+
minRadius: 5,
|
|
49
|
+
maxRadius: 50,
|
|
50
|
+
minBeta: 0.1,
|
|
51
|
+
maxBeta: Math.PI / 2 - 0.1,
|
|
48
52
|
target: [0, 0, 0],
|
|
53
|
+
inertia: 0.9,
|
|
54
|
+
wheelPrecision: 50,
|
|
55
|
+
angularSensibility: 1000,
|
|
49
56
|
});
|
|
50
57
|
e.add(arc);
|
|
51
58
|
return e;
|
|
@@ -3,7 +3,21 @@
|
|
|
3
3
|
"description": "SmashTV-style player controller: WASD moves, the arrow keys fire in 8 directions, both camera-relative (W is always up the screen). The body eases to face where it shoots or heads, and movement is clamped to the arena and pushed out of Obstacle barriers. Firing is pooled — instead of spawning entities it emits `bulletPool.fire` at the muzzle, and reuses a single muzzle-flash mesh per shooter (no per-shot mesh churn).",
|
|
4
4
|
"category": "shooter",
|
|
5
5
|
"subgenre": "twin-stick-shooter",
|
|
6
|
-
"components": [
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
"components": [
|
|
7
|
+
"TwinStickShooterComponent"
|
|
8
|
+
],
|
|
9
|
+
"systems": [
|
|
10
|
+
"TwinStickShooterSystem"
|
|
11
|
+
],
|
|
12
|
+
"dependencies": [
|
|
13
|
+
"MeshPrimitive",
|
|
14
|
+
"BulletPool",
|
|
15
|
+
"Obstacle",
|
|
16
|
+
"ArcCamera"
|
|
17
|
+
],
|
|
18
|
+
"tags": [
|
|
19
|
+
"shooter",
|
|
20
|
+
"twin-stick-shooter",
|
|
21
|
+
"controller"
|
|
22
|
+
]
|
|
9
23
|
}
|