@vgai/engine 0.3.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/package.json +8 -2
- 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 +67 -0
- package/src/adapter/game-adapter.ts +8 -8
- package/src/adapter/host-context.ts +2 -4
- package/src/adapter/index.ts +3 -1
- package/src/adapter/system-adapter.ts +73 -0
- package/src/adapter/vgai-scene-game-adapter.ts +239 -180
- package/src/animation/xstate-animation-binding.ts +300 -262
- package/src/animation/xstate-animation-meta.ts +50 -13
- package/src/character/cloth-sim.ts +533 -0
- package/src/character/spring-chain.ts +307 -0
- package/src/core/game-loop.ts +41 -3
- package/src/core/seeded-random.ts +161 -0
- package/src/core/system-runner.ts +20 -3
- package/src/core/types.ts +37 -0
- package/src/data/data-asset.ts +1 -1
- 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 +5 -5
- package/src/input/input-manager.ts +701 -22
- package/src/input/input-types.ts +5 -2
- 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 +12 -2
- package/src/runtime/create-runtime.ts +100 -282
- 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-control.ts +13 -13
- package/src/runtime/render-seed.ts +1 -1
- package/src/runtime/state-bridge.ts +24 -10
- package/src/runtime/types.ts +110 -28
- 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/scene-apply.ts +55 -4
- package/src/scene/scene-loader.ts +10 -27
- package/src/scene/scene-types.ts +0 -1
- package/src/scene/schema/index.ts +0 -34
- 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 +14 -1
- package/src/setup/setup-renderer.ts +1 -1
- package/src/tools/define-tool.ts +50 -11
- 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/dist/adapter/authoring.d.ts +0 -404
- package/dist/adapter/authoring.d.ts.map +0 -1
- package/dist/adapter/authoring.js +0 -22
- package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
- package/dist/adapter/colyseus-networking-adapter.js +0 -38
- package/dist/adapter/first-party-systems.d.ts +0 -38
- package/dist/adapter/first-party-systems.d.ts.map +0 -1
- package/dist/adapter/first-party-systems.js +0 -77
- package/dist/adapter/game-adapter.d.ts +0 -133
- package/dist/adapter/game-adapter.d.ts.map +0 -1
- package/dist/adapter/game-adapter.js +0 -10
- package/dist/adapter/host-context.d.ts +0 -73
- package/dist/adapter/host-context.d.ts.map +0 -1
- package/dist/adapter/host-context.js +0 -16
- package/dist/adapter/index.d.ts +0 -22
- package/dist/adapter/index.d.ts.map +0 -1
- package/dist/adapter/index.js +0 -15
- package/dist/adapter/ingest/game-contract.d.ts +0 -53
- package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
- package/dist/adapter/ingest/game-contract.js +0 -30
- package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
- package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-applier.js +0 -132
- package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
- package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-apply.js +0 -101
- package/dist/adapter/ingest/overlay-file.d.ts +0 -79
- package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-file.js +0 -66
- package/dist/adapter/ingest/overlay-report.d.ts +0 -113
- package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
- package/dist/adapter/ingest/overlay-report.js +0 -103
- package/dist/adapter/ingest/scene-capture.d.ts +0 -97
- package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
- package/dist/adapter/ingest/scene-capture.js +0 -248
- package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
- package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
- package/dist/adapter/ingest/upstream-pin.js +0 -50
- package/dist/adapter/loop-gate-report.d.ts +0 -34
- package/dist/adapter/loop-gate-report.d.ts.map +0 -1
- package/dist/adapter/loop-gate-report.js +0 -37
- package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
- package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
- package/dist/adapter/rapier-physics-adapter.js +0 -46
- package/dist/adapter/system-adapter.d.ts +0 -133
- package/dist/adapter/system-adapter.d.ts.map +0 -1
- package/dist/adapter/system-adapter.js +0 -13
- package/dist/adapter/transform.d.ts +0 -18
- package/dist/adapter/transform.d.ts.map +0 -1
- package/dist/adapter/transform.js +0 -1
- package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -116
- package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
- package/dist/adapter/vgai-scene-game-adapter.js +0 -714
- package/dist/adapter/world-kind.d.ts +0 -30
- package/dist/adapter/world-kind.d.ts.map +0 -1
- package/dist/adapter/world-kind.js +0 -18
- package/dist/ai/navigation.d.ts +0 -64
- package/dist/ai/navigation.d.ts.map +0 -1
- package/dist/ai/navigation.js +0 -133
- package/dist/animation/anim-graph-types.d.ts +0 -25
- package/dist/animation/anim-graph-types.d.ts.map +0 -1
- package/dist/animation/anim-graph-types.js +0 -1
- package/dist/animation/animation-clock.d.ts +0 -213
- package/dist/animation/animation-clock.d.ts.map +0 -1
- package/dist/animation/animation-clock.js +0 -301
- package/dist/animation/blend-node.d.ts +0 -22
- package/dist/animation/blend-node.d.ts.map +0 -1
- package/dist/animation/blend-node.js +0 -97
- package/dist/animation/camera-ownership.d.ts +0 -250
- package/dist/animation/camera-ownership.d.ts.map +0 -1
- package/dist/animation/camera-ownership.js +0 -169
- package/dist/animation/cinematic-cues.d.ts +0 -200
- package/dist/animation/cinematic-cues.d.ts.map +0 -1
- package/dist/animation/cinematic-cues.js +0 -213
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -37
- package/dist/animation/gsap-registration.d.ts +0 -119
- package/dist/animation/gsap-registration.d.ts.map +0 -1
- package/dist/animation/gsap-registration.js +0 -92
- package/dist/animation/theatre-clock-binding.d.ts +0 -89
- package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
- package/dist/animation/theatre-clock-binding.js +0 -29
- package/dist/animation/theatre-director.d.ts +0 -242
- package/dist/animation/theatre-director.d.ts.map +0 -1
- package/dist/animation/theatre-director.js +0 -235
- package/dist/animation/theatre-object-binding.d.ts +0 -305
- package/dist/animation/theatre-object-binding.d.ts.map +0 -1
- package/dist/animation/theatre-object-binding.js +0 -351
- package/dist/animation/xstate-animation-binding.d.ts +0 -111
- package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
- package/dist/animation/xstate-animation-binding.js +0 -307
- package/dist/animation/xstate-animation-meta.d.ts +0 -228
- package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
- package/dist/animation/xstate-animation-meta.js +0 -221
- package/dist/assets.d.ts +0 -26
- package/dist/assets.d.ts.map +0 -1
- package/dist/assets.js +0 -55
- package/dist/audio/index.d.ts +0 -4
- package/dist/audio/index.d.ts.map +0 -1
- package/dist/audio/index.js +0 -24
- package/dist/audio/tone-clock-binding.d.ts +0 -75
- package/dist/audio/tone-clock-binding.d.ts.map +0 -1
- package/dist/audio/tone-clock-binding.js +0 -29
- package/dist/audio/tone-context.d.ts +0 -114
- package/dist/audio/tone-context.d.ts.map +0 -1
- package/dist/audio/tone-context.js +0 -39
- package/dist/audio/tone-offline-render.d.ts +0 -90
- package/dist/audio/tone-offline-render.d.ts.map +0 -1
- package/dist/audio/tone-offline-render.js +0 -56
- package/dist/audio/wav-encode.d.ts +0 -38
- package/dist/audio/wav-encode.d.ts.map +0 -1
- package/dist/audio/wav-encode.js +0 -97
- package/dist/core/game-loop.d.ts +0 -21
- package/dist/core/game-loop.d.ts.map +0 -1
- package/dist/core/game-loop.js +0 -130
- package/dist/core/system-runner.d.ts +0 -120
- package/dist/core/system-runner.d.ts.map +0 -1
- package/dist/core/system-runner.js +0 -286
- package/dist/core/types.d.ts +0 -58
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/types.js +0 -25
- package/dist/data/data-asset.d.ts +0 -101
- package/dist/data/data-asset.d.ts.map +0 -1
- package/dist/data/data-asset.js +0 -122
- package/dist/data/data-check-core.d.ts +0 -110
- package/dist/data/data-check-core.d.ts.map +0 -1
- package/dist/data/data-check-core.js +0 -178
- package/dist/data/data-ref.d.ts +0 -79
- package/dist/data/data-ref.d.ts.map +0 -1
- package/dist/data/data-ref.js +0 -130
- package/dist/data/vite-plugin-data.d.ts +0 -79
- package/dist/data/vite-plugin-data.d.ts.map +0 -1
- package/dist/data/vite-plugin-data.js +0 -217
- package/dist/dev/console-bridge.d.ts +0 -22
- package/dist/dev/console-bridge.d.ts.map +0 -1
- package/dist/dev/console-bridge.js +0 -71
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/dev/logger.d.ts +0 -30
- package/dist/dev/logger.d.ts.map +0 -1
- package/dist/dev/logger.js +0 -90
- package/dist/ecs/component-manager.d.ts +0 -189
- package/dist/ecs/component-manager.d.ts.map +0 -1
- package/dist/ecs/component-manager.js +0 -676
- package/dist/ecs/game-component.d.ts +0 -97
- package/dist/ecs/game-component.d.ts.map +0 -1
- package/dist/ecs/game-component.js +0 -80
- package/dist/ecs/hmr-swap-report.d.ts +0 -53
- package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
- package/dist/ecs/hmr-swap-report.js +0 -55
- package/dist/index.d.ts +0 -37
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -30
- package/dist/input/input-manager.d.ts +0 -596
- package/dist/input/input-manager.d.ts.map +0 -1
- package/dist/input/input-manager.js +0 -1564
- package/dist/input/input-types.d.ts +0 -171
- package/dist/input/input-types.d.ts.map +0 -1
- package/dist/input/input-types.js +0 -1
- package/dist/input/prompt-labels.d.ts +0 -21
- package/dist/input/prompt-labels.d.ts.map +0 -1
- package/dist/input/prompt-labels.js +0 -121
- package/dist/input/rebind-controller.d.ts +0 -59
- package/dist/input/rebind-controller.d.ts.map +0 -1
- package/dist/input/rebind-controller.js +0 -85
- package/dist/input/schema.d.ts +0 -16
- package/dist/input/schema.d.ts.map +0 -1
- package/dist/input/schema.js +0 -228
- package/dist/loader.d.ts +0 -43
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -59
- package/dist/manifest/index.d.ts +0 -5
- package/dist/manifest/index.d.ts.map +0 -1
- package/dist/manifest/index.js +0 -8
- package/dist/manifest/load-file.d.ts +0 -4
- package/dist/manifest/load-file.d.ts.map +0 -1
- package/dist/manifest/load-file.js +0 -14
- package/dist/manifest/load.d.ts +0 -117
- package/dist/manifest/load.d.ts.map +0 -1
- package/dist/manifest/load.js +0 -255
- package/dist/manifest/schema.d.ts +0 -173
- package/dist/manifest/schema.d.ts.map +0 -1
- package/dist/manifest/schema.js +0 -302
- package/dist/physics/collision-system.d.ts +0 -41
- package/dist/physics/collision-system.d.ts.map +0 -1
- package/dist/physics/collision-system.js +0 -64
- package/dist/physics/physics-registry.d.ts +0 -45
- package/dist/physics/physics-registry.d.ts.map +0 -1
- package/dist/physics/physics-registry.js +0 -65
- package/dist/physics/transform-writer.d.ts +0 -16
- package/dist/physics/transform-writer.d.ts.map +0 -1
- package/dist/physics/transform-writer.js +0 -38
- package/dist/physics/trigger-dispatch.d.ts +0 -24
- package/dist/physics/trigger-dispatch.d.ts.map +0 -1
- package/dist/physics/trigger-dispatch.js +0 -80
- package/dist/react/game-state.d.ts +0 -105
- package/dist/react/game-state.d.ts.map +0 -1
- package/dist/react/game-state.js +0 -144
- package/dist/react/use-data.d.ts +0 -60
- package/dist/react/use-data.d.ts.map +0 -1
- package/dist/react/use-data.js +0 -96
- package/dist/react/use-selection.d.ts +0 -75
- package/dist/react/use-selection.d.ts.map +0 -1
- package/dist/react/use-selection.js +0 -90
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/render-batch-system.d.ts +0 -33
- package/dist/render/render-batch-system.d.ts.map +0 -1
- package/dist/render/render-batch-system.js +0 -230
- package/dist/render/render-features.d.ts +0 -47
- package/dist/render/render-features.d.ts.map +0 -1
- package/dist/render/render-features.js +0 -105
- package/dist/render/render-settings.d.ts +0 -36
- package/dist/render/render-settings.d.ts.map +0 -1
- package/dist/render/render-settings.js +0 -51
- package/dist/runtime/create-runtime.d.ts +0 -280
- package/dist/runtime/create-runtime.d.ts.map +0 -1
- package/dist/runtime/create-runtime.js +0 -795
- package/dist/runtime/frame-selector-cache.d.ts +0 -41
- package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
- package/dist/runtime/frame-selector-cache.js +0 -65
- package/dist/runtime/game.d.ts +0 -433
- package/dist/runtime/game.d.ts.map +0 -1
- package/dist/runtime/game.js +0 -569
- package/dist/runtime/input-router.d.ts +0 -78
- package/dist/runtime/input-router.d.ts.map +0 -1
- package/dist/runtime/input-router.js +0 -171
- package/dist/runtime/mount-game.d.ts +0 -129
- package/dist/runtime/mount-game.d.ts.map +0 -1
- package/dist/runtime/mount-game.js +0 -152
- package/dist/runtime/mount-manifest.d.ts +0 -119
- package/dist/runtime/mount-manifest.d.ts.map +0 -1
- package/dist/runtime/mount-manifest.js +0 -204
- package/dist/runtime/render-audio-control.d.ts +0 -110
- package/dist/runtime/render-audio-control.d.ts.map +0 -1
- package/dist/runtime/render-audio-control.js +0 -111
- package/dist/runtime/render-control.d.ts +0 -291
- package/dist/runtime/render-control.d.ts.map +0 -1
- package/dist/runtime/render-control.js +0 -264
- package/dist/runtime/render-seed.d.ts +0 -57
- package/dist/runtime/render-seed.d.ts.map +0 -1
- package/dist/runtime/render-seed.js +0 -74
- package/dist/runtime/scene-ui-bridge.d.ts +0 -69
- package/dist/runtime/scene-ui-bridge.d.ts.map +0 -1
- package/dist/runtime/scene-ui-bridge.js +0 -23
- package/dist/runtime/scene-ui-data.d.ts +0 -13
- package/dist/runtime/scene-ui-data.d.ts.map +0 -1
- package/dist/runtime/scene-ui-data.js +0 -92
- package/dist/runtime/state-bridge.d.ts +0 -47
- package/dist/runtime/state-bridge.d.ts.map +0 -1
- package/dist/runtime/state-bridge.js +0 -53
- package/dist/runtime/types.d.ts +0 -179
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/scene/asset-loaders.d.ts +0 -79
- package/dist/scene/asset-loaders.d.ts.map +0 -1
- package/dist/scene/asset-loaders.js +0 -141
- package/dist/scene/asset-paths.d.ts +0 -11
- package/dist/scene/asset-paths.d.ts.map +0 -1
- package/dist/scene/asset-paths.js +0 -108
- package/dist/scene/asset-ref-check.d.ts +0 -88
- package/dist/scene/asset-ref-check.d.ts.map +0 -1
- package/dist/scene/asset-ref-check.js +0 -214
- package/dist/scene/asset-registry.d.ts +0 -30
- package/dist/scene/asset-registry.d.ts.map +0 -1
- package/dist/scene/asset-registry.js +0 -50
- package/dist/scene/collider-dimensions.d.ts +0 -63
- package/dist/scene/collider-dimensions.d.ts.map +0 -1
- package/dist/scene/collider-dimensions.js +0 -98
- package/dist/scene/component-registry.d.ts +0 -17
- package/dist/scene/component-registry.d.ts.map +0 -1
- package/dist/scene/component-registry.js +0 -28
- package/dist/scene/defaults.d.ts +0 -252
- package/dist/scene/defaults.d.ts.map +0 -1
- package/dist/scene/defaults.js +0 -151
- package/dist/scene/geometries/index.d.ts +0 -8
- package/dist/scene/geometries/index.d.ts.map +0 -1
- package/dist/scene/geometries/index.js +0 -7
- package/dist/scene/geometries/terrain.d.ts +0 -2
- package/dist/scene/geometries/terrain.d.ts.map +0 -1
- package/dist/scene/geometries/terrain.js +0 -37
- package/dist/scene/geometry-registry.d.ts +0 -24
- package/dist/scene/geometry-registry.d.ts.map +0 -1
- package/dist/scene/geometry-registry.js +0 -20
- package/dist/scene/instance-registry.d.ts +0 -36
- package/dist/scene/instance-registry.d.ts.map +0 -1
- package/dist/scene/instance-registry.js +0 -55
- package/dist/scene/instancers/grid.d.ts +0 -2
- package/dist/scene/instancers/grid.d.ts.map +0 -1
- package/dist/scene/instancers/grid.js +0 -36
- package/dist/scene/instancers/index.d.ts +0 -8
- package/dist/scene/instancers/index.d.ts.map +0 -1
- package/dist/scene/instancers/index.js +0 -7
- package/dist/scene/light-camera-factory.d.ts +0 -23
- package/dist/scene/light-camera-factory.d.ts.map +0 -1
- package/dist/scene/light-camera-factory.js +0 -68
- package/dist/scene/material-factory.d.ts +0 -17
- package/dist/scene/material-factory.d.ts.map +0 -1
- package/dist/scene/material-factory.js +0 -197
- package/dist/scene/material-registry.d.ts +0 -40
- package/dist/scene/material-registry.d.ts.map +0 -1
- package/dist/scene/material-registry.js +0 -44
- package/dist/scene/materials/index.d.ts +0 -8
- package/dist/scene/materials/index.d.ts.map +0 -1
- package/dist/scene/materials/index.js +0 -7
- package/dist/scene/materials/water.d.ts +0 -2
- package/dist/scene/materials/water.d.ts.map +0 -1
- package/dist/scene/materials/water.js +0 -54
- package/dist/scene/parse.d.ts +0 -23
- package/dist/scene/parse.d.ts.map +0 -1
- package/dist/scene/parse.js +0 -179
- package/dist/scene/particles-factory.d.ts +0 -33
- package/dist/scene/particles-factory.d.ts.map +0 -1
- package/dist/scene/particles-factory.js +0 -282
- package/dist/scene/scene-apply.d.ts +0 -109
- package/dist/scene/scene-apply.d.ts.map +0 -1
- package/dist/scene/scene-apply.js +0 -326
- package/dist/scene/scene-diff-schema.d.ts +0 -285
- package/dist/scene/scene-diff-schema.d.ts.map +0 -1
- package/dist/scene/scene-diff-schema.js +0 -92
- package/dist/scene/scene-diff-types.d.ts +0 -40
- package/dist/scene/scene-diff-types.d.ts.map +0 -1
- package/dist/scene/scene-diff-types.js +0 -14
- package/dist/scene/scene-loader.d.ts +0 -177
- package/dist/scene/scene-loader.d.ts.map +0 -1
- package/dist/scene/scene-loader.js +0 -1213
- package/dist/scene/scene-query.d.ts +0 -11
- package/dist/scene/scene-query.d.ts.map +0 -1
- package/dist/scene/scene-query.js +0 -63
- package/dist/scene/scene-types.d.ts +0 -10
- package/dist/scene/scene-types.d.ts.map +0 -1
- package/dist/scene/scene-types.js +0 -8
- package/dist/scene/scene-version.d.ts +0 -26
- package/dist/scene/scene-version.d.ts.map +0 -1
- package/dist/scene/scene-version.js +0 -34
- package/dist/scene/schema/animation.d.ts +0 -8
- package/dist/scene/schema/animation.d.ts.map +0 -1
- package/dist/scene/schema/animation.js +0 -42
- package/dist/scene/schema/audio.d.ts +0 -13
- package/dist/scene/schema/audio.d.ts.map +0 -1
- package/dist/scene/schema/audio.js +0 -22
- package/dist/scene/schema/camera.d.ts +0 -28
- package/dist/scene/schema/camera.d.ts.map +0 -1
- package/dist/scene/schema/camera.js +0 -17
- package/dist/scene/schema/collider.d.ts +0 -40
- package/dist/scene/schema/collider.d.ts.map +0 -1
- package/dist/scene/schema/collider.js +0 -55
- package/dist/scene/schema/entity-ref.d.ts +0 -31
- package/dist/scene/schema/entity-ref.d.ts.map +0 -1
- package/dist/scene/schema/entity-ref.js +0 -61
- package/dist/scene/schema/entity.d.ts +0 -1036
- package/dist/scene/schema/entity.d.ts.map +0 -1
- package/dist/scene/schema/entity.js +0 -153
- package/dist/scene/schema/environment.d.ts +0 -425
- package/dist/scene/schema/environment.d.ts.map +0 -1
- package/dist/scene/schema/environment.js +0 -357
- package/dist/scene/schema/index.d.ts +0 -39
- package/dist/scene/schema/index.d.ts.map +0 -1
- package/dist/scene/schema/index.js +0 -20
- package/dist/scene/schema/instances.d.ts +0 -14
- package/dist/scene/schema/instances.d.ts.map +0 -1
- package/dist/scene/schema/instances.js +0 -28
- package/dist/scene/schema/joint.d.ts +0 -23
- package/dist/scene/schema/joint.d.ts.map +0 -1
- package/dist/scene/schema/joint.js +0 -23
- package/dist/scene/schema/light.d.ts +0 -38
- package/dist/scene/schema/light.d.ts.map +0 -1
- package/dist/scene/schema/light.js +0 -26
- package/dist/scene/schema/material.d.ts +0 -199
- package/dist/scene/schema/material.d.ts.map +0 -1
- package/dist/scene/schema/material.js +0 -99
- package/dist/scene/schema/mesh.d.ts +0 -48
- package/dist/scene/schema/mesh.d.ts.map +0 -1
- package/dist/scene/schema/mesh.js +0 -82
- package/dist/scene/schema/particles.d.ts +0 -2178
- package/dist/scene/schema/particles.d.ts.map +0 -1
- package/dist/scene/schema/particles.js +0 -321
- package/dist/scene/schema/physics.d.ts +0 -56
- package/dist/scene/schema/physics.d.ts.map +0 -1
- package/dist/scene/schema/physics.js +0 -37
- package/dist/scene/schema/scene-file.d.ts +0 -304
- package/dist/scene/schema/scene-file.d.ts.map +0 -1
- package/dist/scene/schema/scene-file.js +0 -262
- package/dist/scene/schema/shadow.d.ts +0 -28
- package/dist/scene/schema/shadow.d.ts.map +0 -1
- package/dist/scene/schema/shadow.js +0 -20
- package/dist/scene/schema/spline.d.ts +0 -25
- package/dist/scene/schema/spline.d.ts.map +0 -1
- package/dist/scene/schema/spline.js +0 -15
- package/dist/scene/schema/tuples.d.ts +0 -12
- package/dist/scene/schema/tuples.d.ts.map +0 -1
- package/dist/scene/schema/tuples.js +0 -14
- package/dist/scene/schema/ui.d.ts +0 -880
- package/dist/scene/schema/ui.d.ts.map +0 -1
- package/dist/scene/schema/ui.js +0 -506
- package/dist/scene/user-data.d.ts +0 -171
- package/dist/scene/user-data.d.ts.map +0 -1
- package/dist/scene/user-data.js +0 -137
- package/dist/setup/setup-audio.d.ts +0 -20
- package/dist/setup/setup-audio.d.ts.map +0 -1
- package/dist/setup/setup-audio.js +0 -41
- package/dist/setup/setup-particles.d.ts +0 -17
- package/dist/setup/setup-particles.d.ts.map +0 -1
- package/dist/setup/setup-particles.js +0 -16
- package/dist/setup/setup-physics.d.ts +0 -25
- package/dist/setup/setup-physics.d.ts.map +0 -1
- package/dist/setup/setup-physics.js +0 -51
- package/dist/setup/setup-renderer.d.ts +0 -69
- package/dist/setup/setup-renderer.d.ts.map +0 -1
- package/dist/setup/setup-renderer.js +0 -363
- package/dist/tools/define-tool.d.ts +0 -105
- package/dist/tools/define-tool.d.ts.map +0 -1
- package/dist/tools/define-tool.js +0 -82
- package/dist/world2d/authoring-2d.d.ts +0 -76
- package/dist/world2d/authoring-2d.d.ts.map +0 -1
- package/dist/world2d/authoring-2d.js +0 -176
- package/dist/world2d/capture-to-scene2d.d.ts +0 -5
- package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
- package/dist/world2d/capture-to-scene2d.js +0 -49
- package/dist/world2d/collision-2d.d.ts +0 -52
- package/dist/world2d/collision-2d.d.ts.map +0 -1
- package/dist/world2d/collision-2d.js +0 -70
- package/dist/world2d/components-2d.d.ts +0 -47
- package/dist/world2d/components-2d.d.ts.map +0 -1
- package/dist/world2d/components-2d.js +0 -77
- package/dist/world2d/index.d.ts +0 -34
- package/dist/world2d/index.d.ts.map +0 -1
- package/dist/world2d/index.js +0 -32
- package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
- package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
- package/dist/world2d/ingest-iframe-2d.js +0 -166
- package/dist/world2d/ingest2d.d.ts +0 -97
- package/dist/world2d/ingest2d.d.ts.map +0 -1
- package/dist/world2d/ingest2d.js +0 -40
- package/dist/world2d/physics2d-registry.d.ts +0 -24
- package/dist/world2d/physics2d-registry.d.ts.map +0 -1
- package/dist/world2d/physics2d-registry.js +0 -38
- package/dist/world2d/pixi-game-adapter.d.ts +0 -107
- package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
- package/dist/world2d/pixi-game-adapter.js +0 -211
- package/dist/world2d/pixi-surface.d.ts +0 -39
- package/dist/world2d/pixi-surface.d.ts.map +0 -1
- package/dist/world2d/pixi-surface.js +0 -47
- package/dist/world2d/scene-capture-2d.d.ts +0 -43
- package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
- package/dist/world2d/scene-capture-2d.js +0 -92
- package/dist/world2d/scene2d-loader.d.ts +0 -27
- package/dist/world2d/scene2d-loader.d.ts.map +0 -1
- package/dist/world2d/scene2d-loader.js +0 -274
- package/dist/world2d/schema/entity2d.d.ts +0 -102
- package/dist/world2d/schema/entity2d.d.ts.map +0 -1
- package/dist/world2d/schema/entity2d.js +0 -119
- package/dist/world2d/schema/physics2d.d.ts +0 -46
- package/dist/world2d/schema/physics2d.d.ts.map +0 -1
- package/dist/world2d/schema/physics2d.js +0 -45
- package/dist/world2d/schema/sprite.d.ts +0 -42
- package/dist/world2d/schema/sprite.d.ts.map +0 -1
- package/dist/world2d/schema/sprite.js +0 -63
- package/dist/world2d/schema/tilemap.d.ts +0 -15
- package/dist/world2d/schema/tilemap.d.ts.map +0 -1
- package/dist/world2d/schema/tilemap.js +0 -19
- package/dist/world2d/schema/tuples2d.d.ts +0 -20
- package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
- package/dist/world2d/schema/tuples2d.js +0 -19
- package/dist/world2d/system-adapters-2d.d.ts +0 -16
- package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
- package/dist/world2d/system-adapters-2d.js +0 -37
- package/dist/world2d/transform-writer-2d.d.ts +0 -16
- package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
- package/dist/world2d/transform-writer-2d.js +0 -22
- package/dist/world2d/types.d.ts +0 -53
- package/dist/world2d/types.d.ts.map +0 -1
- package/dist/world2d/types.js +0 -1
- 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,307 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Character primitive — spring-bone chains (hair, ponytails, cloth strips)
|
|
3
|
+
* on top of `@pixiv/three-vrm-springbone`'s raw API.
|
|
4
|
+
*
|
|
5
|
+
* This is a THIN COMPOSITION HELPER, not a wrapper (CLAUDE.md "use libraries
|
|
6
|
+
* directly"): every export here is a small function that assembles the
|
|
7
|
+
* library's own objects (`THREE.Bone`, `VRMSpringBoneJoint`,
|
|
8
|
+
* `VRMSpringBoneCollider`, `VRMSpringBoneManager`) — nothing from the
|
|
9
|
+
* library is re-exported, and callers still import `@pixiv/three-vrm-
|
|
10
|
+
* springbone` types directly for anything beyond what's here. Kept
|
|
11
|
+
* deliberately bare: composing a specific character (a caped viking, a
|
|
12
|
+
* ponytailed ranger) is ordinary calling code on top of these pieces, not a
|
|
13
|
+
* costume/preset system in the engine.
|
|
14
|
+
*
|
|
15
|
+
* Sharp edge this module exists partly to contain (see the mission brief /
|
|
16
|
+
* `cloth-hair-bench/spring-rig.ts`'s header for the full discovery):
|
|
17
|
+
* `VRMSpringBoneCollider` overrides `updateWorldMatrix` (the ON-DEMAND,
|
|
18
|
+
* manually-invoked method), NOT `updateMatrixWorld` (the one the renderer
|
|
19
|
+
* calls automatically) — `SpringColliderRig.reposition()` below calls
|
|
20
|
+
* `collider.updateWorldMatrix(true, false)` itself every tick so callers
|
|
21
|
+
* can't forget it.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
VRMSpringBoneCollider,
|
|
26
|
+
type VRMSpringBoneColliderGroup,
|
|
27
|
+
VRMSpringBoneColliderShapeCapsule,
|
|
28
|
+
VRMSpringBoneColliderShapeSphere,
|
|
29
|
+
VRMSpringBoneJoint,
|
|
30
|
+
VRMSpringBoneManager,
|
|
31
|
+
} from '@pixiv/three-vrm-springbone';
|
|
32
|
+
import * as THREE from 'three';
|
|
33
|
+
|
|
34
|
+
const DEFAULT_GRAVITY_DIR = new THREE.Vector3(0, -1, 0);
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Bone chains
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
export interface BoneChain {
|
|
41
|
+
/** Swinging bones, root (nearest the anchor) first. */
|
|
42
|
+
bones: THREE.Bone[];
|
|
43
|
+
/** A non-simulated leaf `Object3D` past the last bone — the spring joint
|
|
44
|
+
* "tail" target for the last bone in the chain. */
|
|
45
|
+
tip: THREE.Object3D;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** World-space offset from `parent`'s CURRENT world position -> the local
|
|
49
|
+
* position a new direct child of `parent` must have to sit there. Requires
|
|
50
|
+
* `parent.matrixWorld` to be current. Sidesteps ever reasoning about a
|
|
51
|
+
* bone's local rest-axis convention (arbitrary per-bone on an imported
|
|
52
|
+
* rig) — the caller only ever thinks in world-space directions. */
|
|
53
|
+
export function worldOffsetToLocalPos(
|
|
54
|
+
parent: THREE.Object3D,
|
|
55
|
+
worldDir: THREE.Vector3,
|
|
56
|
+
length: number,
|
|
57
|
+
): THREE.Vector3 {
|
|
58
|
+
const parentWorld = new THREE.Vector3();
|
|
59
|
+
parent.getWorldPosition(parentWorld);
|
|
60
|
+
const targetWorld = parentWorld.clone().addScaledVector(worldDir, length);
|
|
61
|
+
return parent.worldToLocal(targetWorld.clone());
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Build a straight chain of `count` new `THREE.Bone`s under `parent`, each
|
|
65
|
+
* offset `segmentLength` further along the WORLD-space unit vector
|
|
66
|
+
* `worldDir` — the natural rest shape for hair/cloth hanging under gravity.
|
|
67
|
+
* Ends with a `tip` `Object3D` (one more segment past the last bone) so
|
|
68
|
+
* every bone has an explicit spring-joint child target. Can be anchored to
|
|
69
|
+
* either an existing rig bone (hair off a head) or any plain `Object3D`. */
|
|
70
|
+
export function buildBoneChain(
|
|
71
|
+
parent: THREE.Object3D,
|
|
72
|
+
namePrefix: string,
|
|
73
|
+
count: number,
|
|
74
|
+
segmentLength: number,
|
|
75
|
+
worldDir: THREE.Vector3,
|
|
76
|
+
): BoneChain {
|
|
77
|
+
const bones: THREE.Bone[] = [];
|
|
78
|
+
let cur: THREE.Object3D = parent;
|
|
79
|
+
cur.updateWorldMatrix(true, false);
|
|
80
|
+
for (let i = 0; i < count; i++) {
|
|
81
|
+
const localPos = worldOffsetToLocalPos(cur, worldDir, segmentLength);
|
|
82
|
+
const bone = new THREE.Bone();
|
|
83
|
+
bone.name = `${namePrefix}${i}`;
|
|
84
|
+
bone.position.copy(localPos);
|
|
85
|
+
cur.add(bone);
|
|
86
|
+
bone.updateWorldMatrix(true, false);
|
|
87
|
+
bones.push(bone);
|
|
88
|
+
cur = bone;
|
|
89
|
+
}
|
|
90
|
+
const tip = new THREE.Object3D();
|
|
91
|
+
tip.name = `${namePrefix}Tip`;
|
|
92
|
+
tip.position.copy(worldOffsetToLocalPos(cur, worldDir, segmentLength));
|
|
93
|
+
cur.add(tip);
|
|
94
|
+
return { bones, tip };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Spring joints
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
export interface ChainJointSettings {
|
|
102
|
+
stiffness?: number;
|
|
103
|
+
gravityPower?: number;
|
|
104
|
+
gravityDir?: THREE.Vector3;
|
|
105
|
+
dragForce?: number;
|
|
106
|
+
hitRadius?: number;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface ChainJointHandle {
|
|
110
|
+
joints: VRMSpringBoneJoint[];
|
|
111
|
+
/** Deregisters every joint this chain added from `manager`. Does NOT
|
|
112
|
+
* detach the bones from the scene graph (the caller owns that). */
|
|
113
|
+
dispose(): void;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Register one `VRMSpringBoneJoint` per bone in `chain` on `manager` (each
|
|
117
|
+
* targeting the next bone, the last targeting `chain.tip`). Does not call
|
|
118
|
+
* `manager.setInitState()` — batch that once after all chains for a
|
|
119
|
+
* manager are registered and the scene's world matrices are current (sharp
|
|
120
|
+
* edge: `VRMSpringBoneJoint` sets `bone.matrixAutoUpdate = false`, so
|
|
121
|
+
* `setInitState()` must run AFTER `scene.updateMatrixWorld(true)`). */
|
|
122
|
+
export function registerChainJoints(
|
|
123
|
+
manager: VRMSpringBoneManager,
|
|
124
|
+
chain: BoneChain,
|
|
125
|
+
colliderGroups: VRMSpringBoneColliderGroup[],
|
|
126
|
+
settings: ChainJointSettings = {},
|
|
127
|
+
): ChainJointHandle {
|
|
128
|
+
const joints: VRMSpringBoneJoint[] = [];
|
|
129
|
+
for (let i = 0; i < chain.bones.length; i++) {
|
|
130
|
+
const bone = chain.bones[i]!;
|
|
131
|
+
const child = chain.bones[i + 1] ?? chain.tip;
|
|
132
|
+
const joint = new VRMSpringBoneJoint(
|
|
133
|
+
bone,
|
|
134
|
+
child,
|
|
135
|
+
{
|
|
136
|
+
stiffness: settings.stiffness ?? 1.2,
|
|
137
|
+
gravityPower: settings.gravityPower ?? 0.9,
|
|
138
|
+
gravityDir: settings.gravityDir ?? DEFAULT_GRAVITY_DIR,
|
|
139
|
+
dragForce: settings.dragForce ?? 0.35,
|
|
140
|
+
hitRadius: settings.hitRadius ?? 0.02,
|
|
141
|
+
},
|
|
142
|
+
colliderGroups,
|
|
143
|
+
);
|
|
144
|
+
manager.addJoint(joint);
|
|
145
|
+
joints.push(joint);
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
joints,
|
|
149
|
+
dispose(): void {
|
|
150
|
+
for (const joint of joints) manager.deleteJoint(joint);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
// Colliders
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
export interface SphereColliderDef {
|
|
160
|
+
shape: 'sphere';
|
|
161
|
+
name: string;
|
|
162
|
+
/** The collider tracks this object's world position every `reposition()`. */
|
|
163
|
+
followBone: THREE.Object3D;
|
|
164
|
+
radius: number;
|
|
165
|
+
/** Extra WORLD-space offset added after sampling `followBone`'s position
|
|
166
|
+
* (e.g. nudging a head collider up from the neck joint). */
|
|
167
|
+
worldOffset?: THREE.Vector3;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface CapsuleColliderDef {
|
|
171
|
+
shape: 'capsule';
|
|
172
|
+
name: string;
|
|
173
|
+
followBone: THREE.Object3D;
|
|
174
|
+
radius: number;
|
|
175
|
+
/** Capsule tail, in the collider's own local space (world-axis-aligned —
|
|
176
|
+
* colliders are parented directly to the scene, see `buildSpringColliderRig`). */
|
|
177
|
+
tail: THREE.Vector3;
|
|
178
|
+
worldOffset?: THREE.Vector3;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export type ColliderDef = SphereColliderDef | CapsuleColliderDef;
|
|
182
|
+
|
|
183
|
+
export interface SpringColliderRig {
|
|
184
|
+
group: VRMSpringBoneColliderGroup;
|
|
185
|
+
colliders: Map<string, VRMSpringBoneCollider>;
|
|
186
|
+
/** Reposition every collider from its `followBone`'s CURRENT world
|
|
187
|
+
* transform and refresh its `colliderMatrix` (see module header). Call
|
|
188
|
+
* once per tick, BEFORE `manager.update(dt)`. */
|
|
189
|
+
reposition(): void;
|
|
190
|
+
dispose(): void;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** Build sphere/capsule colliders parented directly to `scene` (world
|
|
194
|
+
* space) — sidesteps reasoning about any bone's local rest-axis convention
|
|
195
|
+
* for the collider shape's offset/tail vectors. */
|
|
196
|
+
export function buildSpringColliderRig(scene: THREE.Scene, defs: ColliderDef[]): SpringColliderRig {
|
|
197
|
+
const colliders = new Map<string, VRMSpringBoneCollider>();
|
|
198
|
+
const followers: Array<{ def: ColliderDef; collider: VRMSpringBoneCollider }> = [];
|
|
199
|
+
|
|
200
|
+
for (const def of defs) {
|
|
201
|
+
const collider =
|
|
202
|
+
def.shape === 'sphere'
|
|
203
|
+
? new VRMSpringBoneCollider(new VRMSpringBoneColliderShapeSphere({ radius: def.radius }))
|
|
204
|
+
: new VRMSpringBoneCollider(
|
|
205
|
+
new VRMSpringBoneColliderShapeCapsule({
|
|
206
|
+
radius: def.radius,
|
|
207
|
+
offset: new THREE.Vector3(0, 0, 0),
|
|
208
|
+
tail: def.tail,
|
|
209
|
+
}),
|
|
210
|
+
);
|
|
211
|
+
scene.add(collider);
|
|
212
|
+
colliders.set(def.name, collider);
|
|
213
|
+
followers.push({ def, collider });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const group: VRMSpringBoneColliderGroup = {
|
|
217
|
+
name: 'body',
|
|
218
|
+
colliders: [...colliders.values()],
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const worldTmp = new THREE.Vector3();
|
|
222
|
+
return {
|
|
223
|
+
group,
|
|
224
|
+
colliders,
|
|
225
|
+
reposition(): void {
|
|
226
|
+
for (const { def, collider } of followers) {
|
|
227
|
+
def.followBone.getWorldPosition(worldTmp);
|
|
228
|
+
collider.position.copy(worldTmp);
|
|
229
|
+
if (def.worldOffset) collider.position.add(def.worldOffset);
|
|
230
|
+
collider.updateWorldMatrix(true, false);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
dispose(): void {
|
|
234
|
+
for (const collider of colliders.values()) scene.remove(collider);
|
|
235
|
+
colliders.clear();
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Composed chain rig (the common case: one chain, its own manager)
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
export interface SpringChainRigOptions {
|
|
245
|
+
parent: THREE.Object3D;
|
|
246
|
+
namePrefix: string;
|
|
247
|
+
count: number;
|
|
248
|
+
segmentLength: number;
|
|
249
|
+
worldDir: THREE.Vector3;
|
|
250
|
+
/** Colliders this chain collides against. Omit for a chain with no
|
|
251
|
+
* collision (e.g. a side strand tucked away from the body). */
|
|
252
|
+
colliderRig?: SpringColliderRig;
|
|
253
|
+
settings?: ChainJointSettings;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export interface SpringChainRig {
|
|
257
|
+
bones: THREE.Bone[];
|
|
258
|
+
tip: THREE.Object3D;
|
|
259
|
+
/** The manager owning this chain's joints — a "manager-compatible handle"
|
|
260
|
+
* a caller can also register additional joints on directly. */
|
|
261
|
+
manager: VRMSpringBoneManager;
|
|
262
|
+
/** Repositions `colliderRig` (if given) and steps the spring simulation.
|
|
263
|
+
* Handles the collider `updateWorldMatrix` footgun internally — callers
|
|
264
|
+
* never need to call `collider.updateWorldMatrix` themselves. */
|
|
265
|
+
tick(dt: number): void;
|
|
266
|
+
dispose(): void;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Compose `buildBoneChain` + `registerChainJoints` behind one call for the
|
|
270
|
+
* common single-chain case: builds the chain, gives it its OWN
|
|
271
|
+
* `VRMSpringBoneManager` (managers are cheap — one per independent chain
|
|
272
|
+
* keeps `dispose()`/`tick()` fully self-contained), registers its joints,
|
|
273
|
+
* and calls `setInitState()` once construction is complete.
|
|
274
|
+
*
|
|
275
|
+
* For MULTIPLE chains sharing one collider rig (e.g. a ponytail + two side
|
|
276
|
+
* strands all colliding against the same head), build each chain with its
|
|
277
|
+
* own `buildSpringChainRig` call (same `colliderRig` passed to each) and
|
|
278
|
+
* tick all of them — `colliderRig.reposition()` is idempotent per frame, so
|
|
279
|
+
* calling it once per chain tick is correct, just slightly redundant work
|
|
280
|
+
* (cheap: it only touches a handful of colliders). */
|
|
281
|
+
export function buildSpringChainRig(options: SpringChainRigOptions): SpringChainRig {
|
|
282
|
+
const chain = buildBoneChain(
|
|
283
|
+
options.parent,
|
|
284
|
+
options.namePrefix,
|
|
285
|
+
options.count,
|
|
286
|
+
options.segmentLength,
|
|
287
|
+
options.worldDir,
|
|
288
|
+
);
|
|
289
|
+
const manager = new VRMSpringBoneManager();
|
|
290
|
+
const colliderGroups = options.colliderRig ? [options.colliderRig.group] : [];
|
|
291
|
+
const jointHandle = registerChainJoints(manager, chain, colliderGroups, options.settings);
|
|
292
|
+
manager.setInitState();
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
bones: chain.bones,
|
|
296
|
+
tip: chain.tip,
|
|
297
|
+
manager,
|
|
298
|
+
tick(dt: number): void {
|
|
299
|
+
options.colliderRig?.reposition();
|
|
300
|
+
manager.update(dt);
|
|
301
|
+
},
|
|
302
|
+
dispose(): void {
|
|
303
|
+
jointHandle.dispose();
|
|
304
|
+
chain.bones[0]?.removeFromParent();
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
package/src/core/game-loop.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GameLoopConfig } from './types';
|
|
1
|
+
import type { GameLoopConfig, GameLoopLiveness } from './types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Fixed-timestep game loop with accumulator pattern.
|
|
@@ -104,12 +104,22 @@ export function createGameLoop(config: GameLoopConfig) {
|
|
|
104
104
|
// listener to fire in the first place.
|
|
105
105
|
if (externalDrive) return;
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
// A play session can be started while the editor tab is ALREADY
|
|
108
|
+
// hidden. In that case no future visibilitychange-to-hidden event will
|
|
109
|
+
// arrive, and arming rAF would leave `running=true` forever while the
|
|
110
|
+
// browser executes zero callbacks. Classify and park the loop before
|
|
111
|
+
// arming it so status is truthful and visibility restore can resume it.
|
|
109
112
|
if (!visibilityListenerAttached && typeof document !== 'undefined') {
|
|
110
113
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
111
114
|
visibilityListenerAttached = true;
|
|
112
115
|
}
|
|
116
|
+
if (typeof document !== 'undefined' && document.hidden) {
|
|
117
|
+
running = false;
|
|
118
|
+
hiddenPaused = true;
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
rafId = requestAnimationFrame(frame);
|
|
113
123
|
},
|
|
114
124
|
|
|
115
125
|
stop() {
|
|
@@ -127,6 +137,22 @@ export function createGameLoop(config: GameLoopConfig) {
|
|
|
127
137
|
return running;
|
|
128
138
|
},
|
|
129
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Truthful liveness (issue #175 — see `GameLoopLiveness`'s doc comment
|
|
142
|
+
* in `core/types.ts` for the full contract). Unlike `isRunning` above,
|
|
143
|
+
* this DISTINGUISHES a hidden-tab pause from an actually-stopped loop:
|
|
144
|
+
* `hiddenPaused` is only ever set by `handleVisibilityChange` (T2.1's
|
|
145
|
+
* idle throttle) and cleared by `start()`/`stop()`/visibility-restore,
|
|
146
|
+
* so it can never be true at the same time `running` is true. An
|
|
147
|
+
* `externalDrive` loop never installs the visibility listener at all
|
|
148
|
+
* (see `start()` above), so `hiddenPaused` stays permanently `false` for
|
|
149
|
+
* it — it only ever reports `'running'` or `'stopped'`.
|
|
150
|
+
*/
|
|
151
|
+
get liveness(): GameLoopLiveness {
|
|
152
|
+
if (hiddenPaused) return 'hidden-paused';
|
|
153
|
+
return running ? 'running' : 'stopped';
|
|
154
|
+
},
|
|
155
|
+
|
|
130
156
|
set timeScale(value: number) {
|
|
131
157
|
const clamped = Math.min(8, Math.max(0, value));
|
|
132
158
|
if (clamped !== value) {
|
|
@@ -140,5 +166,17 @@ export function createGameLoop(config: GameLoopConfig) {
|
|
|
140
166
|
get timeScale() {
|
|
141
167
|
return timeScale;
|
|
142
168
|
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The fixed substep timestep (seconds) this loop's accumulator consumes
|
|
172
|
+
* per `config.update()` call (`config.fixedTimestep ?? 1/60`) — read by
|
|
173
|
+
* `Game.runTicks` (D15, `runtime/game.ts`) so a synchronous fast-forward
|
|
174
|
+
* burst drives `game.runFrame` with the SAME `fixedDt` this loop's own
|
|
175
|
+
* rAF-driven accumulator would have used, without exposing (or
|
|
176
|
+
* `runTicks` needing) any other internal loop state.
|
|
177
|
+
*/
|
|
178
|
+
get fixedDt() {
|
|
179
|
+
return fixedDt;
|
|
180
|
+
},
|
|
143
181
|
};
|
|
144
182
|
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D15 — the seeded-random core (`docs/D15-DETERMINISM-DESIGN.md` §2.a,
|
|
3
|
+
* T-D15.1). `ctx.random` is a game-scoped PRNG with NAMED STREAMS: calling
|
|
4
|
+
* the object itself (`ctx.random()`) draws from the `'gameplay'` stream;
|
|
5
|
+
* `ctx.random.stream('vfx')` (or any other name) derives an INDEPENDENT
|
|
6
|
+
* generator, so a cosmetic/VFX draw can never perturb the gameplay draw
|
|
7
|
+
* order — the classic replay-drift trap option B ("one game-scoped PRNG
|
|
8
|
+
* only") would fall into (see the design doc's §2.a option table).
|
|
9
|
+
*
|
|
10
|
+
* Each stream is seeded from `fnv1a(name) ^ rootSeed` — deterministic given
|
|
11
|
+
* the root seed, independent of draw order across streams (drawing from
|
|
12
|
+
* `'vfx'` never advances `'gameplay'`'s generator, since they are two
|
|
13
|
+
* separate mulberry32 instances). `reseed(seed)` re-derives every stream
|
|
14
|
+
* that has EVER been asked for via `.stream(name)` (including the implicit
|
|
15
|
+
* `'gameplay'` stream `ctx.random()` itself draws from) — future draws only:
|
|
16
|
+
* numbers already returned before a `reseed()` call are not (and cannot be)
|
|
17
|
+
* un-returned; this matches `play.seed.set`'s documented semantics (T-D15.6).
|
|
18
|
+
*
|
|
19
|
+
* Deliberately reuses {@link createMulberry32} FROM `runtime/render-seed.ts`
|
|
20
|
+
* (the render/capture door's own generator) rather than duplicating the
|
|
21
|
+
* algorithm — `render-seed.ts` stays a zero-import module itself (this file
|
|
22
|
+
* imports FROM it, never the reverse), so this is a dependency-clean
|
|
23
|
+
* direction: the render/capture door and the gameplay determinism door share
|
|
24
|
+
* one PRNG implementation without either depending on the other's door
|
|
25
|
+
* logic (§2.a: "Kept as-is for the render/capture door only" — the two doors
|
|
26
|
+
* stay orthogonal; only the generator function itself is shared).
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { createMulberry32 } from '../runtime/render-seed';
|
|
30
|
+
|
|
31
|
+
/** The name `ctx.random()` (called with no `.stream(...)`) draws from. */
|
|
32
|
+
export const GAMEPLAY_STREAM = 'gameplay';
|
|
33
|
+
|
|
34
|
+
/** The fixed fallback seed a `SeededRandom` boots with when no explicit seed
|
|
35
|
+
* is supplied (mirrors `render-seed.ts`'s own `DEFAULT_RENDER_SEED` in
|
|
36
|
+
* spirit — an arbitrary but fixed golden-ratio-derived constant, never
|
|
37
|
+
* wall-clock/`Math.random`-derived, so an UNDECLARED project's `ctx.random`
|
|
38
|
+
* is still perfectly reproducible on its own terms even though nothing in
|
|
39
|
+
* the manifest asked for that — it just isn't a documented CONTRACT until
|
|
40
|
+
* `determinism.seededRandom` is declared, per T4.1's "no dead field" rule). */
|
|
41
|
+
export const DEFAULT_SEEDED_RANDOM_SEED = 0x9e3779b9;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The `ctx.random` surface (`GameContext.random`, `runtime/types.ts`).
|
|
45
|
+
* Callable (draws from the `'gameplay'` stream), plus:
|
|
46
|
+
* - `stream(name)` — an independent named generator, `[0, 1)` floats, same
|
|
47
|
+
* call signature as `Math.random`/the bare `SeededRandom` call itself.
|
|
48
|
+
* - `reseed(seed)` — re-derive every stream ever requested so far (future
|
|
49
|
+
* draws only) from a NEW root seed.
|
|
50
|
+
* - `seed` — the CURRENT root seed (reflects the last `reseed()` call, if
|
|
51
|
+
* any) — a live getter, not a value snapshotted at construction.
|
|
52
|
+
*/
|
|
53
|
+
export interface SeededRandom {
|
|
54
|
+
(): number;
|
|
55
|
+
stream(name: string): () => number;
|
|
56
|
+
reseed(seed: number): void;
|
|
57
|
+
readonly seed: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** fnv1a-32, the standard non-cryptographic string hash — used only to mix a
|
|
61
|
+
* stream NAME into the root seed, never as a determinism primitive on its
|
|
62
|
+
* own (mulberry32 is still what actually produces the `[0,1)` sequence). */
|
|
63
|
+
function fnv1a32(str: string): number {
|
|
64
|
+
let hash = 0x811c9dc5;
|
|
65
|
+
for (let i = 0; i < str.length; i++) {
|
|
66
|
+
hash ^= str.charCodeAt(i);
|
|
67
|
+
hash = Math.imul(hash, 0x01000193);
|
|
68
|
+
}
|
|
69
|
+
return hash >>> 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface StreamBox {
|
|
73
|
+
/** Reassigned wholesale on `reseed()` — the wrapper function `.stream(name)`
|
|
74
|
+
* returns to callers stays the SAME identity forever; only what it reads
|
|
75
|
+
* from changes, which is what makes reseed observable through a handle a
|
|
76
|
+
* caller obtained long before the reseed call. */
|
|
77
|
+
gen: () => number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Construct a fresh, game-scoped seeded-random surface. `initialSeed` is
|
|
82
|
+
* coerced to an unsigned 32-bit integer the same way `render-seed.ts`'s
|
|
83
|
+
* `installDeterministicRandom` does (`>>> 0`), so any finite JS number is
|
|
84
|
+
* accepted.
|
|
85
|
+
*/
|
|
86
|
+
export function createSeededRandom(initialSeed: number): SeededRandom {
|
|
87
|
+
let rootSeed = initialSeed >>> 0;
|
|
88
|
+
const streams = new Map<string, StreamBox>();
|
|
89
|
+
|
|
90
|
+
function deriveStreamSeed(name: string): number {
|
|
91
|
+
return (fnv1a32(name) ^ rootSeed) >>> 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getBox(name: string): StreamBox {
|
|
95
|
+
let box = streams.get(name);
|
|
96
|
+
if (!box) {
|
|
97
|
+
box = { gen: createMulberry32(deriveStreamSeed(name)) };
|
|
98
|
+
streams.set(name, box);
|
|
99
|
+
}
|
|
100
|
+
return box;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function streamFn(name: string): () => number {
|
|
104
|
+
const box = getBox(name);
|
|
105
|
+
return () => box.gen();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const random = (() => getBox(GAMEPLAY_STREAM).gen()) as SeededRandom;
|
|
109
|
+
|
|
110
|
+
Object.defineProperties(random, {
|
|
111
|
+
stream: { value: streamFn, enumerable: true },
|
|
112
|
+
reseed: {
|
|
113
|
+
value: (seed: number) => {
|
|
114
|
+
rootSeed = seed >>> 0;
|
|
115
|
+
// Re-derive every stream ANYONE has ever asked for (including the
|
|
116
|
+
// implicit 'gameplay' stream, once `random()` or
|
|
117
|
+
// `.stream('gameplay')` has been called at least once) — a handle a
|
|
118
|
+
// caller stashed before this call keeps its identity but now reads
|
|
119
|
+
// from the freshly-seeded generator on its NEXT call (future draws
|
|
120
|
+
// only, per the design doc's `play.seed.set` semantics).
|
|
121
|
+
for (const [name, box] of streams) {
|
|
122
|
+
box.gen = createMulberry32(deriveStreamSeed(name));
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
enumerable: true,
|
|
126
|
+
},
|
|
127
|
+
seed: {
|
|
128
|
+
get: () => rootSeed,
|
|
129
|
+
enumerable: true,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return random;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Game-scoped registry — mirrors `runtime/debug-registry.ts`'s
|
|
138
|
+
// `registerDebugRegistry`/`getDebugRegistry` WeakMap pattern exactly, but
|
|
139
|
+
// keyed on a bare `object` (not `Game`) so this module never needs to import
|
|
140
|
+
// `runtime/game.ts` even as a type — `core/` stays independent of `runtime/`
|
|
141
|
+
// except for the one explicit, documented `render-seed.ts` reuse above.
|
|
142
|
+
// `createGame` (`runtime/game.ts`) is the one real registrant, passing itself
|
|
143
|
+
// (the `GameInternal` shell) as the key, exactly like it does for
|
|
144
|
+
// `registerDebugRegistry(gameInternal, debugRegistry)`.
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
const registryByOwner = new WeakMap<object, SeededRandom>();
|
|
148
|
+
|
|
149
|
+
/** Called once by `createGame`, right after both the seeded-random surface
|
|
150
|
+
* and the Game shell object exist — mirrors `registerDebugRegistry`. */
|
|
151
|
+
export function registerSeededRandom(owner: object, random: SeededRandom): void {
|
|
152
|
+
registryByOwner.set(owner, random);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** The game-scoped `SeededRandom` backing every world's `ctx.random` — `null`
|
|
156
|
+
* for an owner built without one (there is always one for every real
|
|
157
|
+
* `createGame` call; `null` only for a hand-built `Game`-shaped stand-in a
|
|
158
|
+
* test constructs without going through `createGame`). */
|
|
159
|
+
export function getSeededRandom(owner: object): SeededRandom | null {
|
|
160
|
+
return registryByOwner.get(owner) ?? null;
|
|
161
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
PHASE_ORDER,
|
|
3
|
+
type SystemDef,
|
|
4
|
+
type SystemFn,
|
|
5
|
+
type SystemOptions,
|
|
6
|
+
type SystemPhaseName,
|
|
7
|
+
type SystemRunObserver,
|
|
8
|
+
} from './types';
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Ordered system execution by named phase.
|
|
@@ -25,10 +32,12 @@ import { PHASE_ORDER, type SystemDef, type SystemFn, type SystemPhaseName } from
|
|
|
25
32
|
*
|
|
26
33
|
* Within each bucket, systems run in registration order.
|
|
27
34
|
*/
|
|
28
|
-
export function createSystemRunner() {
|
|
35
|
+
export function createSystemRunner(observer?: SystemRunObserver, scope = 'world') {
|
|
29
36
|
const systems = new Map<SystemPhaseName, SystemFn[]>();
|
|
30
37
|
const registered: SystemDef[] = [];
|
|
31
38
|
const componentTicks = new Map<SystemPhaseName, SystemFn | null>();
|
|
39
|
+
const labels = new Map<SystemFn, string>();
|
|
40
|
+
let anonymousId = 0;
|
|
32
41
|
|
|
33
42
|
// Initialize all phases with empty arrays / no component tick.
|
|
34
43
|
for (const phase of PHASE_ORDER) {
|
|
@@ -56,11 +65,16 @@ export function createSystemRunner() {
|
|
|
56
65
|
|
|
57
66
|
/** Run one system function, isolating a throw so it never wedges the frame. */
|
|
58
67
|
function runOne(fn: SystemFn, phase: SystemPhaseName, dt: number): void {
|
|
68
|
+
const label = labels.get(fn) ?? fn.name ?? `anonymous-${++anonymousId}`;
|
|
69
|
+
labels.set(fn, label);
|
|
70
|
+
observer?.beginSystem(scope, phase, label);
|
|
59
71
|
try {
|
|
60
72
|
fn(dt);
|
|
61
73
|
} catch (err) {
|
|
62
74
|
const label = fn.name ? `"${fn.name}"` : '(anonymous)';
|
|
63
75
|
console.error(`[system-runner] system ${label} in phase "${phase}" threw:`, err);
|
|
76
|
+
} finally {
|
|
77
|
+
observer?.endSystem(scope, phase, label);
|
|
64
78
|
}
|
|
65
79
|
}
|
|
66
80
|
|
|
@@ -69,12 +83,13 @@ export function createSystemRunner() {
|
|
|
69
83
|
* Register a bare system function in a specific phase.
|
|
70
84
|
* Systems within the same phase run in the order they were added.
|
|
71
85
|
*/
|
|
72
|
-
add(phase: SystemPhaseName, fn: SystemFn) {
|
|
86
|
+
add(phase: SystemPhaseName, fn: SystemFn, options?: SystemOptions) {
|
|
73
87
|
const list = systems.get(phase);
|
|
74
88
|
if (!list) {
|
|
75
89
|
throw new Error(`Unknown phase: ${phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
|
|
76
90
|
}
|
|
77
91
|
list.push(fn);
|
|
92
|
+
labels.set(fn, options?.name ?? fn.name ?? `anonymous-${++anonymousId}`);
|
|
78
93
|
},
|
|
79
94
|
|
|
80
95
|
/**
|
|
@@ -102,6 +117,7 @@ export function createSystemRunner() {
|
|
|
102
117
|
throw new Error(`Unknown phase: ${system.phase}. Valid phases: ${PHASE_ORDER.join(', ')}`);
|
|
103
118
|
}
|
|
104
119
|
list.push(system.update);
|
|
120
|
+
labels.set(system.update, system.name ?? system.update.name ?? `anonymous-${++anonymousId}`);
|
|
105
121
|
registered.push(system);
|
|
106
122
|
},
|
|
107
123
|
|
|
@@ -141,6 +157,7 @@ export function createSystemRunner() {
|
|
|
141
157
|
);
|
|
142
158
|
}
|
|
143
159
|
componentTicks.set(phase, fn);
|
|
160
|
+
labels.set(fn, `components.${phase}`);
|
|
144
161
|
},
|
|
145
162
|
|
|
146
163
|
/**
|
package/src/core/types.ts
CHANGED
|
@@ -30,8 +30,20 @@ export const PHASE_ORDER: SystemPhaseName[] = [
|
|
|
30
30
|
/** A system is just a function that takes delta time */
|
|
31
31
|
export type SystemFn = (dt: number) => void;
|
|
32
32
|
|
|
33
|
+
export interface SystemOptions {
|
|
34
|
+
/** Stable diagnostic label; falls back to the function name. */
|
|
35
|
+
readonly name?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SystemRunObserver {
|
|
39
|
+
beginSystem(scope: string, phase: SystemPhaseName, name: string): void;
|
|
40
|
+
endSystem(scope: string, phase: SystemPhaseName, name: string): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
33
43
|
/** A lifecycle system with init/update/dispose hooks. */
|
|
34
44
|
export interface SystemDef {
|
|
45
|
+
/** Stable diagnostic label; falls back to the update function name. */
|
|
46
|
+
readonly name?: string;
|
|
35
47
|
/** Which phase the update function runs in */
|
|
36
48
|
phase: SystemPhaseName;
|
|
37
49
|
/** One-time setup after all entities are spawned, before the first frame */
|
|
@@ -69,3 +81,28 @@ export interface GameLoopConfig {
|
|
|
69
81
|
*/
|
|
70
82
|
externalDrive?: boolean;
|
|
71
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Truthful liveness classification for `createGameLoop`'s returned loop
|
|
87
|
+
* (issue #175 — "state must never claim health it cannot observe"). See
|
|
88
|
+
* `GameLoop.liveness`'s doc comment in `game-loop.ts` for the full contract;
|
|
89
|
+
* exported here (rather than only inline on the loop's return type) so
|
|
90
|
+
* callers elsewhere in the engine (`runtime/game.ts`, `runtime/
|
|
91
|
+
* debug-registry.ts`) can name the union without importing `game-loop.ts`
|
|
92
|
+
* as a value.
|
|
93
|
+
*
|
|
94
|
+
* - `'running'` — the rAF chain is armed and `config.update` is being
|
|
95
|
+
* driven by real frames (or, for an `externalDrive` loop, `start()` has
|
|
96
|
+
* been called — see that mode's own liveness note below).
|
|
97
|
+
* - `'hidden-paused'` — the T2.1 idle throttle stopped the loop because
|
|
98
|
+
* `document.hidden` is true; this is deliberate and reversible (resumes
|
|
99
|
+
* automatically on visibility), but ZERO ticks are advancing right now.
|
|
100
|
+
* - `'stopped'` — `stop()` was called, or `start()` was never called.
|
|
101
|
+
*
|
|
102
|
+
* `isRunning` collapses `'hidden-paused'` and `'stopped'` into the same
|
|
103
|
+
* `false` — exactly the ambiguity that let a hidden-paused game report as
|
|
104
|
+
* healthy ("playing") through every consumer that only read `isRunning`/
|
|
105
|
+
* store-level play state. Read `liveness` wherever "is this game actually
|
|
106
|
+
* advancing" matters.
|
|
107
|
+
*/
|
|
108
|
+
export type GameLoopLiveness = 'running' | 'hidden-paused' | 'stopped';
|
package/src/data/data-asset.ts
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* Game code reads `tuning.get()` inside its update loop (never cache fields
|
|
26
26
|
* across frames) or `subscribe()`s; an edit to the `.data.json` — from
|
|
27
27
|
* VS Code, an agent, or the future editor Data tab (W2) — flows through Vite
|
|
28
|
-
* HMR into the running game with no remount. React
|
|
28
|
+
* HMR into the running game with no remount. React roots/tools get a
|
|
29
29
|
* `useData` hook with W3's tool-hooks package (deferred with it: the engine
|
|
30
30
|
* core stays React-free).
|
|
31
31
|
*/
|