@vgai/engine 0.2.0 → 0.4.0-canary.20260715.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 +3 -1
- package/package.json +24 -4
- package/schemas/engine-api.json +124 -0
- package/schemas/engine-api.md +53 -0
- package/schemas/engine-capabilities.json +124 -0
- package/schemas/inputmap.schema.json +314 -0
- package/schemas/mat.schema.json +286 -0
- package/schemas/prefab.schema.json +10148 -0
- package/schemas/scn2d.schema.json +475 -0
- package/schemas/vgai-game.schema.json +383 -0
- package/schemas/vscn.schema.json +11007 -0
- package/src/adapter/{world-kind.ts → adapter-surface.ts} +6 -6
- package/src/adapter/authoring.ts +77 -0
- package/src/adapter/first-party-systems.ts +23 -34
- package/src/adapter/game-adapter.ts +8 -8
- package/src/adapter/host-context.ts +2 -4
- package/src/adapter/index.ts +4 -4
- package/src/adapter/system-adapter.ts +88 -22
- package/src/adapter/vgai-scene-game-adapter.ts +244 -194
- package/src/animation/anim-graph-types.ts +12 -43
- package/src/animation/animation-clock.ts +479 -0
- package/src/animation/camera-ownership.ts +467 -0
- package/src/animation/cinematic-cues.ts +451 -0
- package/src/animation/clip-map.ts +41 -0
- package/src/animation/gsap-registration.ts +184 -0
- package/src/animation/theatre-clock-binding.ts +111 -0
- package/src/animation/theatre-director.ts +347 -0
- package/src/animation/theatre-object-binding.ts +661 -0
- package/src/animation/xstate-animation-binding.ts +436 -0
- package/src/animation/xstate-animation-meta.ts +319 -0
- package/src/audio/index.ts +39 -7
- package/src/audio/tone-clock-binding.ts +98 -0
- package/src/audio/tone-context.ts +129 -0
- package/src/audio/tone-offline-render.ts +167 -0
- package/src/audio/wav-encode.ts +119 -0
- package/src/character/cloth-sim.ts +533 -0
- package/src/character/spring-chain.ts +307 -0
- package/src/core/game-loop.ts +57 -2
- package/src/core/seeded-random.ts +161 -0
- package/src/core/system-runner.ts +20 -3
- package/src/core/types.ts +50 -0
- package/src/data/data-asset.ts +167 -0
- package/src/data/data-check-core.ts +242 -0
- package/src/data/data-ref.ts +145 -0
- package/src/data/vite-plugin-data.ts +290 -0
- package/src/dev/performance-profiler.ts +213 -0
- package/src/dev/webgl-gpu-timer.ts +53 -0
- package/src/ecs/component-manager.ts +45 -12
- package/src/ecs/game-component.ts +95 -11
- package/src/humanoid/bake.operation.ts +326 -0
- package/src/humanoid/body.ts +663 -0
- package/src/humanoid/clips.ts +149 -0
- package/src/humanoid/compose.ts +209 -0
- package/src/humanoid/generate.ts +189 -0
- package/src/humanoid/index.ts +36 -0
- package/src/humanoid/schema.ts +108 -0
- package/src/humanoid/skeleton.ts +345 -0
- package/src/index.ts +48 -0
- package/src/input/input-manager.ts +1886 -33
- package/src/input/input-types.ts +158 -3
- package/src/input/prompt-labels.ts +122 -0
- package/src/input/rebind-controller.ts +105 -0
- package/src/input/schema.ts +206 -52
- package/src/manifest/index.ts +5 -5
- package/src/manifest/load.ts +125 -72
- package/src/manifest/schema.ts +362 -255
- package/src/react/game-state.tsx +135 -32
- package/src/react/root-adapter.tsx +49 -0
- package/src/react/unmanaged-root-detector.ts +66 -0
- package/src/react/use-data.ts +124 -0
- package/src/react/use-selection.tsx +135 -0
- package/src/runtime/create-runtime.ts +112 -273
- package/src/runtime/debug-bridge.ts +483 -0
- package/src/runtime/debug-registry.ts +856 -0
- package/src/runtime/game.ts +342 -93
- package/src/runtime/gameplay-rng-trap.ts +134 -0
- package/src/runtime/input-router.ts +7 -7
- package/src/runtime/mount-game.ts +40 -38
- package/src/runtime/mount-manifest.ts +169 -37
- package/src/runtime/render-audio-control.ts +168 -0
- package/src/runtime/render-control.ts +522 -0
- package/src/runtime/render-seed.ts +79 -0
- package/src/runtime/state-bridge.ts +24 -10
- package/src/runtime/types.ts +110 -33
- package/src/scene/asset-loaders.ts +10 -36
- package/src/scene/asset-paths.ts +0 -2
- package/src/scene/asset-ref-check.ts +248 -0
- package/src/scene/asset-registry.ts +22 -0
- package/src/scene/component-registry.ts +14 -3
- package/src/scene/defaults.ts +1 -0
- package/src/scene/light-camera-factory.ts +11 -3
- package/src/scene/parse.ts +133 -0
- package/src/scene/scene-apply.ts +55 -4
- package/src/scene/scene-loader.ts +91 -123
- package/src/scene/scene-types.ts +0 -1
- package/src/scene/schema/animation.ts +30 -79
- package/src/scene/schema/entity.ts +20 -0
- package/src/scene/schema/index.ts +2 -46
- package/src/scene/schema/light.ts +16 -1
- package/src/scene/schema/material.ts +96 -91
- package/src/scene/schema/scene-file.ts +1 -7
- package/src/scene/user-data.ts +22 -10
- package/src/setup/setup-renderer.ts +10 -3
- package/src/tools/define-tool.ts +191 -0
- package/src/world2d/authoring-2d.ts +17 -1
- package/src/world2d/collision-2d.ts +1 -1
- package/src/world2d/pixi-game-adapter.ts +19 -17
- package/src/world2d/scene2d-loader.ts +1 -0
- package/src/world2d/types.ts +8 -2
- package/src/animation/anim-graph.ts +0 -406
- package/src/animation/anim-system.ts +0 -28
- package/src/animation/property-track.ts +0 -178
- package/src/animation/schema.ts +0 -204
- package/src/audio/ambient.ts +0 -300
- package/src/audio/impacts.ts +0 -212
- package/src/audio/movement.ts +0 -140
- package/src/audio/musical.ts +0 -200
- package/src/audio/ui-sounds.ts +0 -171
- package/src/audio/vehicle.ts +0 -235
- package/src/audio/weapons.ts +0 -152
- package/src/runtime/scene-ui-bridge.ts +0 -86
- package/src/runtime/scene-ui-data.ts +0 -119
- package/src/scene/schema/ui.ts +0 -602
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project tools — W4/W3 of `docs/DATA-TOOLS-DESIGN.md` (§3 contract).
|
|
3
|
+
*
|
|
4
|
+
* A project tool is ONE file, `src/tools/<name>.tool.tsx`, discovered by
|
|
5
|
+
* folder scan (same physics as data assets, §3.1: zero registration, delete
|
|
6
|
+
* the file → gone). The file exports exactly two things:
|
|
7
|
+
*
|
|
8
|
+
* ```tsx
|
|
9
|
+
* export const tool = defineTool({
|
|
10
|
+
* id: 'tuning', // stable slug — becomes the tab's test id
|
|
11
|
+
* title: 'Tuning', // the tab label (rendered after a ⚙ marker)
|
|
12
|
+
* placement: 'document', // project-global dashboard (§3.2; 'dock' = deprecated alias)
|
|
13
|
+
* });
|
|
14
|
+
* export default function Tuning() { … } // plain React
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* or, selection-scoped (W3 — §3.2's second placement):
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* export const tool = defineTool({
|
|
21
|
+
* id: 'spin',
|
|
22
|
+
* title: 'Spin',
|
|
23
|
+
* placement: 'inspector', // selection-scoped section
|
|
24
|
+
* match: (node) => node?.kind === 'mesh', // same contract as the
|
|
25
|
+
* }); // inspector-section registry
|
|
26
|
+
* export default function Spin({ node }: InspectorToolProps) { … }
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* The editor renders dock tools as visually distinct tabs after a separator
|
|
30
|
+
* at the end of the bottom dock's tab row (§3.5); inspector tools render as
|
|
31
|
+
* a ⚙-marked section in the Inspector rail whenever `match` accepts the
|
|
32
|
+
* current selection. Both render inside an error boundary — a crashing tool
|
|
33
|
+
* never takes the editor down. Tools are EDITOR-ONLY: no game build bundles
|
|
34
|
+
* `src/tools/` (§4 — nothing in game code imports it), so dev/cheat surfaces
|
|
35
|
+
* can never leak into a tester serve.
|
|
36
|
+
*
|
|
37
|
+
* This module is deliberately react-free metadata (the engine core outside
|
|
38
|
+
* `src/react/` must not import react — `test/react-core-import-ban.test.ts`);
|
|
39
|
+
* the react-facing tool hooks live in `@engine/react/use-data`, and the
|
|
40
|
+
* editor-styled widget kit tools build panels from is `@editor/widgets`.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type { AuthoringAdapter, EditorNode } from '../adapter';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Tool placements. Since W0 of the editor workspace-shell program
|
|
47
|
+
* (docs/EDITOR-WORKSPACE-SHELL-TODO.md §7.2), placement describes MEANING,
|
|
48
|
+
* not a physical container:
|
|
49
|
+
*
|
|
50
|
+
* - `'document'` — a substantial project-global surface (tuning dashboard,
|
|
51
|
+
* table, graph) that belongs in the center workspace;
|
|
52
|
+
* - `'inspector'` — a selection-scoped section in the right Inspector rail;
|
|
53
|
+
* - `'utility'` — a transient output/debug surface for the bottom drawer;
|
|
54
|
+
* - `'dock'` — DEPRECATED physical compatibility alias. Existing dock tools
|
|
55
|
+
* remain in the bottom panel so projects do not lose the ability to tune
|
|
56
|
+
* them while watching the Game/Scene.
|
|
57
|
+
*
|
|
58
|
+
* Only an explicit `document` declaration replaces the center subject.
|
|
59
|
+
* `utility` and legacy `dock` remain in the bottom drawer. Deliberately
|
|
60
|
+
* still nothing else — no floating windows, no in-game overlay
|
|
61
|
+
* host (docs/DATA-TOOLS-DESIGN.md §3.2 "deliberately NOT built").
|
|
62
|
+
*/
|
|
63
|
+
export type ToolPlacement = 'document' | 'inspector' | 'utility' | 'dock';
|
|
64
|
+
|
|
65
|
+
/** The project-global (non-selection-scoped) placements. */
|
|
66
|
+
export type GlobalToolPlacement = 'document' | 'utility' | 'dock';
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Compatibility means preserving behavior: legacy `'dock'` tools remain in
|
|
70
|
+
* the bottom panel. New tools choose `'document'`, `'utility'`, or
|
|
71
|
+
* `'inspector'` deliberately.
|
|
72
|
+
*/
|
|
73
|
+
export function normalizeToolPlacement(
|
|
74
|
+
placement: ToolPlacement,
|
|
75
|
+
): 'document' | 'inspector' | 'utility' {
|
|
76
|
+
return placement === 'dock' ? 'utility' : placement;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Fields every tool declares — see {@link defineTool} for the contract. */
|
|
80
|
+
interface ToolConfigBase {
|
|
81
|
+
/**
|
|
82
|
+
* Stable slug for this tool: letters/digits/`-`/`_` only. It becomes the
|
|
83
|
+
* mount's `data-testid` (`bottom-tab-tool-<id>` for dock tools,
|
|
84
|
+
* `inspector-tool-<id>` for inspector tools), so keep it short and never
|
|
85
|
+
* rename it casually — automation and saved layouts key off it.
|
|
86
|
+
*/
|
|
87
|
+
id: string;
|
|
88
|
+
/** Human label: the dock tab text, or the inspector section's header,
|
|
89
|
+
* rendered after the ⚙ project-tool marker (§3.5). */
|
|
90
|
+
title: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** A project-global tool (§3.2 W4; `'dock'` preserves its historical bottom
|
|
94
|
+
* placement through the `'utility'` compatibility normalization). */
|
|
95
|
+
export interface GlobalToolConfig extends ToolConfigBase {
|
|
96
|
+
placement: GlobalToolPlacement;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** A selection-scoped Inspector section (§3.2, W3). */
|
|
100
|
+
export interface InspectorToolConfig extends ToolConfigBase {
|
|
101
|
+
placement: 'inspector';
|
|
102
|
+
/**
|
|
103
|
+
* When this section appears — the SAME contract as the editor's
|
|
104
|
+
* `inspector-section-registry`: called with the currently selected node
|
|
105
|
+
* (`null` in the no-selection/environment state) and the active authoring
|
|
106
|
+
* adapter; return `true` to render. Keep it cheap and pure — it runs on
|
|
107
|
+
* every inspector render. A `match` that throws is treated as `false`
|
|
108
|
+
* (with one console teaching error), never crashing the inspector.
|
|
109
|
+
*/
|
|
110
|
+
match: (node: EditorNode | null, adapter: AuthoringAdapter) => boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Metadata for a project tool — a discriminated union on `placement`. */
|
|
114
|
+
export type ToolConfig = GlobalToolConfig | InspectorToolConfig;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Props the editor passes to an INSPECTOR tool's default-exported component
|
|
118
|
+
* (dock tools receive no props). Everything richer (live game store, field
|
|
119
|
+
* addresses) arrives through the §3.3 hooks, not through more props.
|
|
120
|
+
*/
|
|
121
|
+
export interface InspectorToolProps {
|
|
122
|
+
/** The selected node your `match` accepted (`null` in the no-selection
|
|
123
|
+
* state, if your `match` chose to accept it). */
|
|
124
|
+
node: EditorNode | null;
|
|
125
|
+
/** The selected node's stable id (`null` in the no-selection state). */
|
|
126
|
+
nodeId: string | null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const TEMPLATE_EXAMPLE = 'the template worked example: src/tools/example.tool.tsx';
|
|
130
|
+
|
|
131
|
+
/** Teaching-error helper (§6.5): every rejection names the fix and cites the
|
|
132
|
+
* template example by path. */
|
|
133
|
+
function toolError(problem: string, fix: string): Error {
|
|
134
|
+
return new Error(`defineTool: ${problem} — ${fix} (see ${TEMPLATE_EXAMPLE}).`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Validate and freeze a project tool's metadata (docs/DATA-TOOLS-DESIGN.md
|
|
139
|
+
* §3.1). Call it once at module scope of a `src/tools/<name>.tool.tsx` file
|
|
140
|
+
* and export the result as `tool`, next to a default-exported React
|
|
141
|
+
* component — the editor's folder scan picks the file up automatically.
|
|
142
|
+
*
|
|
143
|
+
* Fails loud (§6.5 "errors teach") on anything malformed rather than letting
|
|
144
|
+
* a half-shaped tool render confusingly: bad `id`/`title`, an unknown
|
|
145
|
+
* placement, an inspector tool without a callable `match`.
|
|
146
|
+
*
|
|
147
|
+
* The returned config is frozen: tool metadata is a definition, not state
|
|
148
|
+
* (the same immutability rule data assets follow, §2.1).
|
|
149
|
+
*/
|
|
150
|
+
export function defineTool(config: ToolConfig): Readonly<ToolConfig> {
|
|
151
|
+
if (config === null || typeof config !== 'object') {
|
|
152
|
+
throw toolError(
|
|
153
|
+
'expected a config object',
|
|
154
|
+
"call it as defineTool({ id, title, placement: 'document' })",
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
const { id, title, placement } = config;
|
|
158
|
+
if (
|
|
159
|
+
placement !== 'document' &&
|
|
160
|
+
placement !== 'utility' &&
|
|
161
|
+
placement !== 'dock' &&
|
|
162
|
+
placement !== 'inspector'
|
|
163
|
+
) {
|
|
164
|
+
throw toolError(
|
|
165
|
+
`unknown placement ${JSON.stringify(placement)}`,
|
|
166
|
+
"the placements are 'document' (project-global center document), 'inspector' " +
|
|
167
|
+
"(selection-scoped section) and 'utility' (transient bottom-drawer output); " +
|
|
168
|
+
"'dock' remains accepted as a deprecated bottom-panel alias " +
|
|
169
|
+
'(docs/EDITOR-WORKSPACE-SHELL-TODO.md §7.2)',
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(id)) {
|
|
173
|
+
throw toolError(
|
|
174
|
+
`invalid id ${JSON.stringify(id)}`,
|
|
175
|
+
'pass a non-empty slug of letters, digits, "-" or "_" — it becomes the tool\'s data-testid (bottom-tab-tool-<id> / inspector-tool-<id>)',
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (typeof title !== 'string' || title.trim() === '') {
|
|
179
|
+
throw toolError(`invalid title ${JSON.stringify(title)}`, 'pass a short human-readable label');
|
|
180
|
+
}
|
|
181
|
+
if (placement === 'inspector') {
|
|
182
|
+
if (typeof config.match !== 'function') {
|
|
183
|
+
throw toolError(
|
|
184
|
+
`placement 'inspector' requires a match function (got ${JSON.stringify(config.match)})`,
|
|
185
|
+
"pass match: (node) => boolean — e.g. match: (node) => node?.kind === 'mesh' — deciding which selections show this section",
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return Object.freeze({ id, title, placement, match: config.match });
|
|
189
|
+
}
|
|
190
|
+
return Object.freeze({ id, title, placement });
|
|
191
|
+
}
|
|
@@ -31,6 +31,8 @@ export interface Property2D {
|
|
|
31
31
|
|
|
32
32
|
/** A persisted edit overlay keyed by structural-path id (the 2D analog of IngestOverlay). */
|
|
33
33
|
export interface Override2D {
|
|
34
|
+
label?: string;
|
|
35
|
+
alpha?: number;
|
|
34
36
|
position?: [number, number];
|
|
35
37
|
rotation?: number;
|
|
36
38
|
scale?: [number, number];
|
|
@@ -110,7 +112,11 @@ export class AuthoringAdapter2D {
|
|
|
110
112
|
// --- Transforms ---
|
|
111
113
|
getTransform(id: string): Transform2DValue | null {
|
|
112
114
|
const o = this.byId.get(id);
|
|
113
|
-
|
|
115
|
+
// A tracked node isn't guaranteed to be a real PixiJS display object with
|
|
116
|
+
// a transform (e.g. test doubles / non-positional fixture nodes walked
|
|
117
|
+
// via the composite adapter's overlay path) — treat a missing position
|
|
118
|
+
// the same as a missing node: no transform to report, not a crash.
|
|
119
|
+
if (!o || !o.position || !o.scale) return null;
|
|
114
120
|
return {
|
|
115
121
|
position: [o.position.x, o.position.y],
|
|
116
122
|
rotation: o.rotation,
|
|
@@ -156,6 +162,8 @@ export class AuthoringAdapter2D {
|
|
|
156
162
|
if (!o) return;
|
|
157
163
|
o[path] = value;
|
|
158
164
|
this.overlay[id] ??= {};
|
|
165
|
+
if (path === 'label') this.overlay[id].label = value as string;
|
|
166
|
+
if (path === 'alpha') this.overlay[id].alpha = value as number;
|
|
159
167
|
if (path === 'tint') this.overlay[id].tint = value as number;
|
|
160
168
|
if (path === 'visible') this.overlay[id].visible = value as boolean;
|
|
161
169
|
this.dirty = true;
|
|
@@ -164,6 +172,8 @@ export class AuthoringAdapter2D {
|
|
|
164
172
|
private applyOverride(id: string, ov: Override2D): void {
|
|
165
173
|
const o = this.byId.get(id);
|
|
166
174
|
if (!o) return;
|
|
175
|
+
if (ov.label !== undefined) o.label = ov.label;
|
|
176
|
+
if (ov.alpha !== undefined) o.alpha = ov.alpha;
|
|
167
177
|
if (ov.position) o.position.set(ov.position[0], ov.position[1]);
|
|
168
178
|
if (ov.rotation !== undefined) o.rotation = ov.rotation;
|
|
169
179
|
if (ov.scale) o.scale.set(ov.scale[0], ov.scale[1]);
|
|
@@ -201,6 +211,12 @@ export class AuthoringAdapter2D {
|
|
|
201
211
|
serializeOverlay(): Overlay2D {
|
|
202
212
|
return this.overlay;
|
|
203
213
|
}
|
|
214
|
+
/** Replace, rather than merge, overlay state after an exact history restore. */
|
|
215
|
+
replaceOverlay(overlay: Overlay2D): void {
|
|
216
|
+
this.overlay = structuredClone(overlay);
|
|
217
|
+
for (const [id, ov] of Object.entries(this.overlay)) this.applyOverride(id, ov);
|
|
218
|
+
this.dirty = true;
|
|
219
|
+
}
|
|
204
220
|
/** Clear the dirty flag after a successful save (mirrors IngestAuthoringAdapter). */
|
|
205
221
|
markClean(): void {
|
|
206
222
|
this.dirty = false;
|
|
@@ -47,7 +47,7 @@ export type Collision2DSystem = ReturnType<typeof createCollision2DSystem>;
|
|
|
47
47
|
* structural slice: the unified `ComponentManager` (`ecs/component-manager.ts`,
|
|
48
48
|
* what production wires up since T7.3 slice 1, and the only manager left —
|
|
49
49
|
* the world2d silo's manager is deleted, T7.3 slice 2) satisfies it — this
|
|
50
|
-
* dispatcher has no dependency on which `
|
|
50
|
+
* dispatcher has no dependency on which `AdapterSurface` a caller's manager was
|
|
51
51
|
* constructed for.
|
|
52
52
|
*/
|
|
53
53
|
export interface TriggerDispatchComponents {
|
|
@@ -25,10 +25,10 @@ export interface World2DHost {
|
|
|
25
25
|
canvas: HTMLCanvasElement;
|
|
26
26
|
width: number;
|
|
27
27
|
height: number;
|
|
28
|
-
/**
|
|
29
|
-
|
|
28
|
+
/** Shared Game root when mounted by the universal multi-world host. */
|
|
29
|
+
game?: GameInternal;
|
|
30
30
|
/** DPR override (T6.1 slice 1, COMPOSITION-DESIGN.md D5 §3 — "one DPR
|
|
31
|
-
* everywhere"): the
|
|
31
|
+
* everywhere"): the roots-path host computes ONE dpr for every stacked
|
|
32
32
|
* surface and passes it here; omitted -> `createPixiSurface`'s own
|
|
33
33
|
* default (`window.devicePixelRatio`, uncapped) — today's behavior. */
|
|
34
34
|
dpr?: number;
|
|
@@ -109,7 +109,7 @@ export class PixiSceneGameAdapter {
|
|
|
109
109
|
width: host.width,
|
|
110
110
|
height: host.height,
|
|
111
111
|
...(sceneData?.background ? { background: sceneData.background } : {}),
|
|
112
|
-
// T6.1 slice 1 (
|
|
112
|
+
// T6.1 slice 1 (roots-path host hints — see `World2DHost`'s fields):
|
|
113
113
|
// all optional, all no-ops when the host is the legacy
|
|
114
114
|
// `createWorld2DRuntime` entry (which never sets them), so behavior
|
|
115
115
|
// there is unchanged.
|
|
@@ -128,15 +128,17 @@ export class PixiSceneGameAdapter {
|
|
|
128
128
|
const writeTransforms2D = createTransformWriter2D(physics2d);
|
|
129
129
|
const collisions2d = createCollision2DSystem(rapier2dWorld, eventQueue);
|
|
130
130
|
|
|
131
|
-
const systems = createSystemRunner();
|
|
132
|
-
systems.add('physics', () => rapier2dWorld.step(eventQueue));
|
|
133
|
-
systems.add(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
131
|
+
const systems = createSystemRunner(host.game?.profiler.systemObserver, 'pixijs');
|
|
132
|
+
systems.add('physics', () => rapier2dWorld.step(eventQueue), { name: 'physics2d.step' });
|
|
133
|
+
systems.add(
|
|
134
|
+
'postPhysics',
|
|
135
|
+
() => {
|
|
136
|
+
collisions2d.drain();
|
|
137
|
+
writeTransforms2D();
|
|
138
|
+
},
|
|
139
|
+
{ name: 'physics2d.sync' },
|
|
140
|
+
);
|
|
141
|
+
systems.add('render', () => surface.render(), { name: 'pixi.render' });
|
|
140
142
|
|
|
141
143
|
const ctx: World2DContext = {
|
|
142
144
|
app: surface.app,
|
|
@@ -147,7 +149,7 @@ export class PixiSceneGameAdapter {
|
|
|
147
149
|
collisions2d,
|
|
148
150
|
components: null!,
|
|
149
151
|
systems,
|
|
150
|
-
|
|
152
|
+
...(host.game ? { game: host.game } : {}),
|
|
151
153
|
};
|
|
152
154
|
|
|
153
155
|
// T7.3 slice 1/2: the pixi world's components attach onto the SAME
|
|
@@ -235,16 +237,15 @@ export interface World2DSession {
|
|
|
235
237
|
* surface-host-backend-neutral allows). Owns the canvas and constructs a real `Game`
|
|
236
238
|
* (T7.3 slice 1) with exactly one `'pixijs'`-kind `WorldInstance`, driven each fixed
|
|
237
239
|
* substep through `Game.runFrame` — the world2d analog of `createGameRuntime`'s
|
|
238
|
-
* `
|
|
240
|
+
* `registerThreeWorld` (`runtime/create-runtime.ts`). Before T7.3 this ran its
|
|
239
241
|
* own standalone `createGameLoop` calling `mounted.update` directly; that loop is
|
|
240
242
|
* retired here in favor of one shared frame executor per game root.
|
|
241
243
|
*/
|
|
242
244
|
export async function createWorld2DRuntime(
|
|
243
245
|
config: PixiSceneConfig & World2DHost,
|
|
244
246
|
): Promise<World2DSession> {
|
|
245
|
-
const { canvas, width, height,
|
|
247
|
+
const { canvas, width, height, ...sceneConfig } = config;
|
|
246
248
|
const adapter = new PixiSceneGameAdapter(sceneConfig);
|
|
247
|
-
const mounted = await adapter.mount({ canvas, width, height, ...(ui ? { ui } : {}) });
|
|
248
249
|
|
|
249
250
|
const assets = createAssetCache();
|
|
250
251
|
// `game` is referenced here before its `const` below only textually — this
|
|
@@ -258,6 +259,7 @@ export async function createWorld2DRuntime(
|
|
|
258
259
|
},
|
|
259
260
|
});
|
|
260
261
|
const game: GameInternal = createGame({ loop, assets });
|
|
262
|
+
const mounted = await adapter.mount({ canvas, width, height, game });
|
|
261
263
|
|
|
262
264
|
// T7.5: `mounted` (a `MountedGame2D`, extending `MountedPixiWorld`) and
|
|
263
265
|
// `adapter` (this `PixiSceneGameAdapter`, satisfying `WorldInstance.adapter`'s
|
|
@@ -183,6 +183,7 @@ function createPhysics2D(ctx: World2DContext, entity: Entity2D, display: Contain
|
|
|
183
183
|
if (p.restitution !== undefined) colDesc.setRestitution(p.restitution);
|
|
184
184
|
if (p.sensor) {
|
|
185
185
|
colDesc.setSensor(true);
|
|
186
|
+
colDesc.setActiveCollisionTypes(rapier2d.ActiveCollisionTypes.ALL);
|
|
186
187
|
colDesc.setActiveEvents(rapier2d.ActiveEvents.COLLISION_EVENTS);
|
|
187
188
|
} else {
|
|
188
189
|
colDesc.setActiveEvents(rapier2d.ActiveEvents.COLLISION_EVENTS);
|
package/src/world2d/types.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { createSystemRunner } from '../core/system-runner';
|
|
|
5
5
|
import type { SystemPhaseName } from '../core/types';
|
|
6
6
|
import type { ComponentManager } from '../ecs/component-manager';
|
|
7
7
|
import type { GameComponent } from '../ecs/game-component';
|
|
8
|
+
import type { Game } from '../runtime/game';
|
|
8
9
|
import type { Collision2DSystem } from './collision-2d';
|
|
9
10
|
import type { Physics2DRegistry } from './physics2d-registry';
|
|
10
11
|
|
|
@@ -38,8 +39,13 @@ export interface World2DContext {
|
|
|
38
39
|
components: ComponentManager;
|
|
39
40
|
/** The phase-ordered system runner (shared with the 3D path). */
|
|
40
41
|
systems: ReturnType<typeof createSystemRunner>;
|
|
41
|
-
/**
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The shared Game root when this world is mounted by a first-party host.
|
|
44
|
+
* This is the cross-world data path: a Pixi component can query gameplay
|
|
45
|
+
* components owned by a sibling Three.js world without a mirror store or a
|
|
46
|
+
* bespoke synchronization bridge.
|
|
47
|
+
*/
|
|
48
|
+
game?: Game | undefined;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
/** Constructor type for `GameComponent<'pixijs'>` subclasses used in a world2d
|