@vgai/editor 0.2.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/LICENSE +661 -0
- package/README.md +35 -0
- package/dist/a/b/c/test-nested.json +1 -0
- package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
- package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
- package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
- package/dist/assets/browserAll-CEkWYlFk.js +1 -0
- package/dist/assets/collision-world-DniMB0qq.glb +0 -0
- package/dist/assets/create-runtime-DYIWr3va.js +1 -0
- package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
- package/dist/assets/esbuild-BHljloGq.wasm +0 -0
- package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
- package/dist/assets/game-8SDZPPgH.js +1 -0
- package/dist/assets/game-BI111o5f.js +10 -0
- package/dist/assets/game-CevorxyV.js +1 -0
- package/dist/assets/game-Di1H0gFj.js +1 -0
- package/dist/assets/game-Di7XlywD.js +1 -0
- package/dist/assets/game-DjnCaw8R.js +1 -0
- package/dist/assets/game-Q9A0aXSV.js +1 -0
- package/dist/assets/game-pt_1lQLv.js +1 -0
- package/dist/assets/game-source-CvidN7AB.js +1 -0
- package/dist/assets/game-xsOho_wg.js +83 -0
- package/dist/assets/index-Bp5cecD6.js +12 -0
- package/dist/assets/index-CPVV6pjw.js +1 -0
- package/dist/assets/index-CpOnYyeD.js +213 -0
- package/dist/assets/index-Cs8xwv3e.js +1 -0
- package/dist/assets/index-DL7RcQ0n.js +27 -0
- package/dist/assets/index-Dr8H_6Rl.js +1 -0
- package/dist/assets/index-_bCaipUk.js +13650 -0
- package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
- package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
- package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
- package/dist/assets/scene-query-BivAHhJu.js +1 -0
- package/dist/assets/stats.module--VATS4Kh.js +1 -0
- package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
- package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
- package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
- package/dist/index.html +37 -0
- package/dist/ingest/racing-game/UPSTREAM.md +68 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
- package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
- package/dist/ingest/racing-game/a/index.css +1 -0
- package/dist/ingest/racing-game/images/bronze.png +0 -0
- package/dist/ingest/racing-game/images/gold.png +0 -0
- package/dist/ingest/racing-game/images/silver.png +0 -0
- package/dist/ingest/racing-game/index.html +15 -0
- package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
- package/dist/ingest/racing-game/models/track-draco.glb +0 -0
- package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
- package/dist/ingest/racing-game/rg-entry.js +1 -0
- package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
- package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
- package/dist/ingest/racing-game/textures/cursor.svg +3 -0
- package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
- package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
- package/dist/ingest/racing-game/textures/mask.svg +11 -0
- package/dist/ingest/racing-game/textures/no_sound.png +0 -0
- package/dist/ingest/racing-game/three-r139.module.js +50617 -0
- package/dist/ingest/racing-game/vendor.js +179 -0
- package/dist/ingest/racing-game/vgai.game.json +20 -0
- package/dist/ingest/simcity/UPSTREAM.md +65 -0
- package/dist/ingest/simcity/favicon.ico +0 -0
- package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
- package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
- package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
- package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
- package/dist/ingest/simcity/icons/bulldozer.png +0 -0
- package/dist/ingest/simcity/icons/calendar.png +0 -0
- package/dist/ingest/simcity/icons/factory-color.png +0 -0
- package/dist/ingest/simcity/icons/factory.png +0 -0
- package/dist/ingest/simcity/icons/house-color.png +0 -0
- package/dist/ingest/simcity/icons/house.png +0 -0
- package/dist/ingest/simcity/icons/job.png +0 -0
- package/dist/ingest/simcity/icons/office.png +0 -0
- package/dist/ingest/simcity/icons/pause-color.png +0 -0
- package/dist/ingest/simcity/icons/pause.png +0 -0
- package/dist/ingest/simcity/icons/person.png +0 -0
- package/dist/ingest/simcity/icons/play-color.png +0 -0
- package/dist/ingest/simcity/icons/play.png +0 -0
- package/dist/ingest/simcity/icons/power-color.png +0 -0
- package/dist/ingest/simcity/icons/power-line-color.png +0 -0
- package/dist/ingest/simcity/icons/power.png +0 -0
- package/dist/ingest/simcity/icons/road-color.png +0 -0
- package/dist/ingest/simcity/icons/road.png +0 -0
- package/dist/ingest/simcity/icons/select-color.png +0 -0
- package/dist/ingest/simcity/icons/select.png +0 -0
- package/dist/ingest/simcity/icons/store-color.png +0 -0
- package/dist/ingest/simcity/index.html +94 -0
- package/dist/ingest/simcity/index.js +58 -0
- package/dist/ingest/simcity/main.css +233 -0
- package/dist/ingest/simcity/models/armored-truck.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
- package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
- package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
- package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
- package/dist/ingest/simcity/models/building-cafe.glb +0 -0
- package/dist/ingest/simcity/models/building-casino.glb +0 -0
- package/dist/ingest/simcity/models/building-cinema.glb +0 -0
- package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
- package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
- package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
- package/dist/ingest/simcity/models/building-office-big.glb +0 -0
- package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
- package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
- package/dist/ingest/simcity/models/building-office.glb +0 -0
- package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
- package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
- package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
- package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
- package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
- package/dist/ingest/simcity/models/car-passenger.glb +0 -0
- package/dist/ingest/simcity/models/car-police.glb +0 -0
- package/dist/ingest/simcity/models/car-taxi.glb +0 -0
- package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
- package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
- package/dist/ingest/simcity/models/car-veteran.glb +0 -0
- package/dist/ingest/simcity/models/construction-small.glb +0 -0
- package/dist/ingest/simcity/models/data-center.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
- package/dist/ingest/simcity/models/industry-factory.glb +0 -0
- package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
- package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
- package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
- package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
- package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
- package/dist/ingest/simcity/models/truck.glb +0 -0
- package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
- package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
- package/dist/ingest/simcity/textures/base.png +0 -0
- package/dist/ingest/simcity/textures/grid.png +0 -0
- package/dist/ingest/simcity/textures/specular.png +0 -0
- package/dist/ingest/simcity/three-r155.module.js +51466 -0
- package/dist/ingest/simcity/vgai.game.json +20 -0
- package/dist/ingest/tanks/UPSTREAM.md +69 -0
- package/dist/ingest/tanks/index.html +214 -0
- package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
- package/dist/ingest/tanks/tanks-entry.js +92 -0
- package/dist/ingest/tanks/three-r170.module.js +54571 -0
- package/dist/ingest/tanks/vgai.game.json +20 -0
- package/dist/inputmaps/default.inputmap.json +41 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/dist/scenes/untitled.vscn.json +29 -0
- package/dist/textures/waternormals.jpg +0 -0
- package/dist-server/packaged.mjs +18155 -0
- package/package.json +36 -0
- package/server/asset-library-routes.ts +833 -0
- package/server/canonical-path.ts +50 -0
- package/server/cloud-asset-catalog.ts +101 -0
- package/server/dev.ts +350 -0
- package/server/editor-server.ts +1537 -0
- package/server/editor-sse.ts +73 -0
- package/server/game-globals-shadow.ts +58 -0
- package/server/local-asset-catalog.ts +163 -0
- package/server/packaged.ts +571 -0
- package/server/prod.ts +75 -0
- package/server/project-optimize-deps-entries.ts +149 -0
- package/server/project-view.ts +65 -0
- package/server/server-utils.ts +430 -0
- package/server/session-registry.ts +93 -0
- package/src/EditorContext.tsx +87 -0
- package/src/achieved-tier.ts +126 -0
- package/src/action-registry.ts +268 -0
- package/src/adapter-resolver.ts +1858 -0
- package/src/asset-events.ts +100 -0
- package/src/authoring/active-adapter.ts +98 -0
- package/src/authoring/active-systems.ts +50 -0
- package/src/authoring/boundary-authoring-adapter.ts +178 -0
- package/src/authoring/composite-authoring-adapter.ts +891 -0
- package/src/authoring/design-time-layers.ts +776 -0
- package/src/authoring/edit-mode-authoring.ts +643 -0
- package/src/authoring/embed-only-adapter.ts +57 -0
- package/src/authoring/ephemeral-persistence.ts +26 -0
- package/src/authoring/eyedropper-session.ts +69 -0
- package/src/authoring/first-party-hierarchy-menu.ts +74 -0
- package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
- package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
- package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
- package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
- package/src/authoring/first-party-inspector/register.tsx +108 -0
- package/src/authoring/ingest-authoring-adapter.ts +475 -0
- package/src/authoring/ingest-capture.ts +94 -0
- package/src/authoring/ingest-game-adapter.ts +291 -0
- package/src/authoring/ingest-iframe-adapter.ts +38 -0
- package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
- package/src/authoring/ingest-overlay.ts +193 -0
- package/src/authoring/layered-pick.ts +87 -0
- package/src/authoring/mount-failure-report.ts +112 -0
- package/src/authoring/overlay-report.ts +52 -0
- package/src/authoring/project-scenes.ts +111 -0
- package/src/authoring/react-dom-authoring-adapter.ts +795 -0
- package/src/authoring/react-inspector-section.tsx +1041 -0
- package/src/authoring/react-world-authoring-adapter.ts +2400 -0
- package/src/authoring/root-activation.ts +61 -0
- package/src/authoring/scene-ui-layer.ts +485 -0
- package/src/authoring/story-overlay.ts +48 -0
- package/src/authoring/ui-authoring-adapter.ts +325 -0
- package/src/authoring/ui-hierarchy-menu.ts +40 -0
- package/src/authoring/ui-inspector-sections.tsx +156 -0
- package/src/authoring/undo-timeline.ts +145 -0
- package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
- package/src/authoring/viewport-pick-context.ts +31 -0
- package/src/authoring/viewport-raycast.ts +80 -0
- package/src/authoring/world-overlay.ts +76 -0
- package/src/authoring/world-pan-state.ts +115 -0
- package/src/authoring/world-session-state.ts +102 -0
- package/src/authoring/world2d-authoring-adapter.ts +313 -0
- package/src/browser-external-watch.ts +98 -0
- package/src/browser-play-entry.ts +112 -0
- package/src/browser-project-scripts.ts +112 -0
- package/src/browser-transpile.ts +246 -0
- package/src/browser-watch-reload.ts +100 -0
- package/src/command-listener.ts +326 -0
- package/src/components/AlignToolbar.tsx +189 -0
- package/src/components/AppRoot.tsx +73 -0
- package/src/components/AssetBrowser.tsx +502 -0
- package/src/components/BottomPanel.tsx +326 -0
- package/src/components/BuildPanel.tsx +455 -0
- package/src/components/CameraInfo.tsx +56 -0
- package/src/components/CommandPalette.tsx +265 -0
- package/src/components/ConsolePanel.tsx +272 -0
- package/src/components/DataPanel.tsx +1268 -0
- package/src/components/DefaultEditorLayout.tsx +509 -0
- package/src/components/FolderTree.tsx +179 -0
- package/src/components/GameHierarchy.tsx +1349 -0
- package/src/components/Inspector.tsx +671 -0
- package/src/components/InspectorToolSection.tsx +74 -0
- package/src/components/MountFailureBanner.tsx +110 -0
- package/src/components/NetworkSection.tsx +109 -0
- package/src/components/OnlineAssetBrowser.tsx +448 -0
- package/src/components/OverlayReportBanner.tsx +122 -0
- package/src/components/PlayBar.tsx +356 -0
- package/src/components/PrefabSaveDialog.tsx +201 -0
- package/src/components/ProjectHeader.tsx +174 -0
- package/src/components/ProjectScreen.tsx +1004 -0
- package/src/components/RightPanel.tsx +166 -0
- package/src/components/RootActivateDialog.tsx +105 -0
- package/src/components/SaveDialog.tsx +197 -0
- package/src/components/SaveStatus.tsx +69 -0
- package/src/components/StatsOverlay.tsx +35 -0
- package/src/components/ToolHost.tsx +80 -0
- package/src/components/Toolbar.tsx +307 -0
- package/src/components/ViewportOverlay.tsx +234 -0
- package/src/components/ViewportPanel.tsx +369 -0
- package/src/components/WorldContextMenu.tsx +257 -0
- package/src/components/WorldSelectionOverlay.tsx +2003 -0
- package/src/components/WorldTextEditor.tsx +86 -0
- package/src/components/asset-viewers/AssetViewer.tsx +38 -0
- package/src/components/asset-viewers/AudioViewer.tsx +149 -0
- package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
- package/src/components/asset-viewers/ImageViewer.tsx +65 -0
- package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
- package/src/components/asset-viewers/ModelViewer.tsx +180 -0
- package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
- package/src/components/data-csv.ts +301 -0
- package/src/components/entity-name-commit.ts +18 -0
- package/src/components/inspector-property-grouping.ts +65 -0
- package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
- package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
- package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
- package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
- package/src/components/inspector-widgets/FontPicker.tsx +196 -0
- package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
- package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
- package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
- package/src/components/inspector-widgets/color-utils.ts +202 -0
- package/src/components/inspector-widgets/gallery-main.tsx +167 -0
- package/src/components/inspector-widgets/index.ts +22 -0
- package/src/components/inspector-widgets/shared.tsx +135 -0
- package/src/components/inspectors/AnimationSection.tsx +143 -0
- package/src/components/inspectors/AudioSection.tsx +215 -0
- package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
- package/src/components/inspectors/CameraSection.tsx +105 -0
- package/src/components/inspectors/ComponentsSection.tsx +358 -0
- package/src/components/inspectors/JointsSection.tsx +255 -0
- package/src/components/inspectors/LightSection.tsx +116 -0
- package/src/components/inspectors/MaterialSection.tsx +1189 -0
- package/src/components/inspectors/MeshSection.tsx +99 -0
- package/src/components/inspectors/NavigationSection.tsx +58 -0
- package/src/components/inspectors/ParticleSection.tsx +681 -0
- package/src/components/inspectors/PhysicsSection.tsx +326 -0
- package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
- package/src/components/inspectors/RenderingSection.tsx +109 -0
- package/src/components/inspectors/ShadowSection.tsx +84 -0
- package/src/components/inspectors/SplineSection.tsx +191 -0
- package/src/components/inspectors/TransformSection.tsx +191 -0
- package/src/components/inspectors/json-schema-fields.ts +281 -0
- package/src/components/inspectors/multi-edit-utils.ts +49 -0
- package/src/components/inspectors/zod-fields.ts +184 -0
- package/src/components/primitives/ColorInput.tsx +109 -0
- package/src/components/primitives/JsonInput.tsx +98 -0
- package/src/components/primitives/NumberInput.tsx +164 -0
- package/src/components/primitives/Panel.tsx +68 -0
- package/src/components/primitives/SectionHeader.tsx +45 -0
- package/src/components/primitives/Tooltip.tsx +82 -0
- package/src/components/primitives/Vec3Input.tsx +62 -0
- package/src/components/world-overlay-gestures.ts +1438 -0
- package/src/doctor/data-checks.ts +285 -0
- package/src/doctor/report.ts +386 -0
- package/src/editor-api.ts +700 -0
- package/src/editor-console.ts +194 -0
- package/src/editor-hotkeys.ts +441 -0
- package/src/editor-layers.ts +2 -0
- package/src/editor-mode.ts +28 -0
- package/src/editor-store.ts +2225 -0
- package/src/editor-viewport.ts +1924 -0
- package/src/entity-factory.ts +221 -0
- package/src/game-globals-prelude.ts +27 -0
- package/src/gated-globals.ts +131 -0
- package/src/generated/setup-by-example.generated.ts +53 -0
- package/src/gizmo-registry.ts +608 -0
- package/src/handle-system.ts +221 -0
- package/src/hierarchy-menu-registry.ts +70 -0
- package/src/hierarchy-rows.ts +73 -0
- package/src/hosted-example.ts +160 -0
- package/src/hotkeys.ts +142 -0
- package/src/ingest/discovery-react.ts +132 -0
- package/src/ingest/discovery.ts +205 -0
- package/src/ingest/discovery2d.ts +204 -0
- package/src/ingest/esm-entry-resolution.ts +102 -0
- package/src/ingest/games/bloom-composer/game.js +99 -0
- package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
- package/src/ingest/games/embed-bundle/embed.html +10 -0
- package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
- package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
- package/src/ingest/games/games-fps/game.js +479 -0
- package/src/ingest/games/games-fps/vgai.game.json +24 -0
- package/src/ingest/games/iframe-reachable/game-source.js +58 -0
- package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
- package/src/ingest/games/interactive-cubes/game.js +139 -0
- package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
- package/src/ingest/games/mismatched-three/fake-three.js +38 -0
- package/src/ingest/games/mismatched-three/game.js +10 -0
- package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
- package/src/ingest/games/own-three-bundled/game.js +43 -0
- package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
- package/src/ingest/games/pointerlock-maze/game.js +288 -0
- package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
- package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
- package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
- package/src/ingest/games-2d/bunnymark/game.ts +88 -0
- package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy/game.ts +148 -0
- package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
- package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
- package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
- package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
- package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
- package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
- package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
- package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
- package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
- package/src/ingest/host-namespace-registry.ts +196 -0
- package/src/ingest/types.ts +127 -0
- package/src/ingest-hierarchy-rows.ts +207 -0
- package/src/ingest-mode.ts +2092 -0
- package/src/ingest-siblings.ts +466 -0
- package/src/initial-scene.ts +194 -0
- package/src/inspector-section-registry.ts +117 -0
- package/src/interactive-edit-scope.ts +34 -0
- package/src/main.tsx +23 -0
- package/src/manifest-project.ts +43 -0
- package/src/model-thumbnail.ts +156 -0
- package/src/module-mode.ts +513 -0
- package/src/navmesh-handler.ts +94 -0
- package/src/packaged-runtime.ts +49 -0
- package/src/play-mode.ts +1148 -0
- package/src/playground/main.ts +206 -0
- package/src/project-manager.ts +173 -0
- package/src/scene-diff.ts +112 -0
- package/src/scene-serializer.ts +144 -0
- package/src/scene-sync.ts +587 -0
- package/src/scene-ui-mode/active-scene-ui.ts +58 -0
- package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
- package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
- package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
- package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
- package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
- package/src/script-loader.ts +143 -0
- package/src/storage/fsa-storage.ts +55 -0
- package/src/storage/handle-storage.ts +150 -0
- package/src/storage/handle-store.ts +116 -0
- package/src/storage/http-storage.ts +137 -0
- package/src/storage/index.ts +147 -0
- package/src/storage/mem-storage.ts +135 -0
- package/src/storage/opfs-storage.ts +25 -0
- package/src/storage/paths.ts +26 -0
- package/src/storage/seed.ts +106 -0
- package/src/storage/types.ts +110 -0
- package/src/tool-loader.ts +291 -0
- package/src/ui-editor/inspector.tsx +563 -0
- package/src/ui-editor/node-ops.ts +266 -0
- package/src/ui-editor/overlay.tsx +380 -0
- package/src/ui-editor/palette.tsx +114 -0
- package/src/ui-editor/react-store.ts +78 -0
- package/src/ui-editor/timeline-panel.tsx +443 -0
- package/src/ui-editor/ui-edit-store.ts +552 -0
- package/src/ui-editor/ui-prefab.ts +95 -0
- package/src/ui-source/inspect.ts +544 -0
- package/src/ui-source/oid-transform.ts +133 -0
- package/src/ui-source/source-write-backend.ts +203 -0
- package/src/ui-source/tailwind-map.ts +174 -0
- package/src/ui-source/writer.ts +1175 -0
- package/src/widgets/index.ts +73 -0
- package/template/.claude/skills/editor/SKILL.md +252 -0
- package/template/CLAUDE.md +512 -0
- package/template/LICENSE +30 -0
- package/template/emit-schemas.ts +42 -0
- package/template/index.html +17 -0
- package/template/package.json +53 -0
- package/template/project.json +9 -0
- package/template/public/audio/ambient-arena.wav +0 -0
- package/template/public/audio/ambient-music.wav +0 -0
- package/template/public/audio/ambient-nature.wav +0 -0
- package/template/public/audio/ambient-wind.wav +0 -0
- package/template/public/inputmaps/default.inputmap.json +41 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
- package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
- package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
- package/template/public/scenes/default.vscn.json +43 -0
- package/template/public/scenes/untitled.vscn.json +29 -0
- package/template/public/textures/waternormals.jpg +0 -0
- package/template/server/colyseus-setup.ts +85 -0
- package/template/server/main.ts +34 -0
- package/template/server/rooms/arena-room.ts +420 -0
- package/template/server/rooms/game-room.ts +162 -0
- package/template/server/rooms.ts +15 -0
- package/template/server/tsconfig.json +19 -0
- package/template/src/data/README.md +40 -0
- package/template/src/data/assets.ts +31 -0
- package/template/src/data/tuning.data.json +5 -0
- package/template/src/data/tuning.schema.json +21 -0
- package/template/src/data/tuning.schema.ts +34 -0
- package/template/src/data/tuning.ts +24 -0
- package/template/src/main.ts +157 -0
- package/template/src/scripts/components/data-spinner.ts +45 -0
- package/template/src/scripts/components/scene-camera.ts +40 -0
- package/template/src/scripts/main.ts +94 -0
- package/template/src/scripts/registry.ts +46 -0
- package/template/src/tools/README.md +14 -0
- package/template/src/tools/example.tool.tsx +91 -0
- package/template/src/tools/spin.tool.tsx +101 -0
- package/template/src/ui/Button.tsx +27 -0
- package/template/src/ui/DefaultHud.tsx +40 -0
- package/template/src/ui/KeyHint.tsx +25 -0
- package/template/src/ui/Panel.tsx +52 -0
- package/template/src/ui/RebindPanel.tsx +129 -0
- package/template/src/ui/Slider.tsx +28 -0
- package/template/src/ui/StatusDot.tsx +22 -0
- package/template/src/ui/Toggle.tsx +19 -0
- package/template/src/ui/game-state.tsx +37 -0
- package/template/src/ui/index.ts +7 -0
- package/template/src/ui/mount-default-hud.tsx +19 -0
- package/template/src/ui/mount-ui.ts +15 -0
- package/template/tsconfig.json +36 -0
- package/template/validate-asset-content.ts +78 -0
- package/template/validate-manifest.ts +55 -0
- package/template/validate-scenes.ts +211 -0
- package/template/vgai.game.json +21 -0
- package/template/vite.config.ts +128 -0
- package/vite-plugin-example-assets.ts +60 -0
- package/vite-plugin-game-globals.ts +46 -0
- package/vite-plugin-game-static.ts +71 -0
- package/vite-plugin-project-game-static.ts +84 -0
- package/vite-plugin-react-game-static.ts +98 -0
- package/vite-plugin-react-world-provider.ts +81 -0
- package/vite-plugin-react-world-runtime.ts +76 -0
- package/vite-plugin-ui-oid.ts +561 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
# CLAUDE.md — VGAI Game Project
|
|
2
|
+
|
|
3
|
+
## The acceptance bar: feature-complete, bot-proven, great-looking
|
|
4
|
+
|
|
5
|
+
This project is not done at a vertical slice. It is done when the **whole
|
|
6
|
+
game** — every feature in the game's maximal spec/ROADMAP, enumerated up
|
|
7
|
+
front — ships, and every one of those features has a **bot-played
|
|
8
|
+
acceptance criterion** proving it works on the real client (see "Autoplay
|
|
9
|
+
bots" below). Two bars, non-negotiable:
|
|
10
|
+
|
|
11
|
+
- **Completeness.** Write the maximal spec/ROADMAP before or alongside the
|
|
12
|
+
first line of code — every feature the game needs, not just the ones
|
|
13
|
+
that are easy or exciting. **No descopes, no "for now," no
|
|
14
|
+
vertical-slice-and-ship.** A core-only build with the rest deferred to
|
|
15
|
+
"later" is not done; it's a partial delivery wearing a done label. If
|
|
16
|
+
scope turns out to be wrong, revise the spec explicitly and say so — do
|
|
17
|
+
not quietly narrow it by shipping less.
|
|
18
|
+
- **Visual quality.** "Great-looking" is a claim you DEMONSTRATE, never one
|
|
19
|
+
you assert. Every visual milestone ships a real, inspectable artifact —
|
|
20
|
+
screenshots and a gameplay video captured from the actual running client
|
|
21
|
+
(`npm run game`, not a mockup) — judged against this game's own
|
|
22
|
+
reference/style bar. "It renders" or "looks good" in prose is not proof;
|
|
23
|
+
if you can't point at the recording, it isn't shown.
|
|
24
|
+
|
|
25
|
+
Both bars are proven the same way the autoplay-bot doctrine below proves
|
|
26
|
+
everything else: **the bot plays the real game and the run is the
|
|
27
|
+
evidence.** A skill/feature isn't complete until a bot has played it
|
|
28
|
+
end-to-end and the logs/recording show it working — that's what turns
|
|
29
|
+
"I built X" and "X looks great" from a claim into a demonstrated fact on
|
|
30
|
+
the real product surface.
|
|
31
|
+
|
|
32
|
+
## Before Writing Any Code
|
|
33
|
+
|
|
34
|
+
0. **Launch the editor and keep it running.** `vgai edit` (run it in the
|
|
35
|
+
background) — the editor is the authoring surface AND the verification
|
|
36
|
+
loop: `vgai status` / `vgai play` / `vgai select` / `vgai apply-diff` and
|
|
37
|
+
the `/editor` skill all drive it. Tell the user the URL `vgai edit` prints
|
|
38
|
+
(default `http://localhost:5173`, but NOT always — see the port caveat
|
|
39
|
+
below) so they can watch the game take shape. This step applies to agents
|
|
40
|
+
too, headless or not; if part of the editor loop genuinely doesn't fit your
|
|
41
|
+
game (e.g. fully procedural world, no scene to author), say so explicitly
|
|
42
|
+
instead of silently skipping the editor.
|
|
43
|
+
**Port cross-talk caveat:** the default port (5173) is shared across every
|
|
44
|
+
vgai project on this machine. `vgai edit` will not silently retarget
|
|
45
|
+
another project's already-running editor — if a DIFFERENT project's
|
|
46
|
+
editor already holds 5173, this one starts a second instance on a fresh
|
|
47
|
+
free port instead and prints THAT url. If you (or the user) already have
|
|
48
|
+
another vgai project's editor tab open in the browser, don't assume it's
|
|
49
|
+
this project — read the printed URL/port, or pass `vgai edit --port <n>`
|
|
50
|
+
to pin a specific port for this project, so an old tab left open from a
|
|
51
|
+
different project doesn't get mistaken for this session (see the
|
|
52
|
+
`/editor` skill's CLI Reference for `--port` and `vgai sessions`).
|
|
53
|
+
1. **Read an example.** A plain scaffold ships only a minimal default scene — no example content. The worked implementations (FPS, vehicle, third-person, RTS, multiplayer arena, IK, one-concept tutorials — see the Examples Catalog below) are standalone projects in the vgai monorepo's `examples/<id>/`. Scaffold one directly to read/copy its code: `npx create-vgai-project <name> --example <id>` (e.g. `--example third-person`) makes a correctly-rewritten standalone copy of that example, `vgai.game.json` and all. You can also open any of them live in the hosted editor at `https://vgai-editor.pages.dev/?project=<id>` (read-only — scaffold to keep edits). Otherwise, build gameplay from the Components section below.
|
|
54
|
+
2. **Read the schema.** Scene format is defined by the engine's Zod schemas; the generated JSON Schema is `schemas/vscn.schema.json` inside the engine package (your tsconfig `paths` / vite alias points at the engine — the schema sits next to its `src/`). Read it before writing or editing any `.vscn.json` file. Wrong field names (e.g. `type` instead of `bodyType` in physics) silently break things. Do **not** author fields the runtime doesn't read: `gamepad_axis_pair`/`mouse_move` input bindings — these (like dead `.vscn` fields) **throw at parse** — the engine ships Zod schemas for every fetched asset format (`inputmap.schema.json` sits beside `vscn.schema.json` in the engine package). Discrete character animation (idle/walk/run/jump) is an XState machine + `bindXStateAnimation` in your own component code, not a `.vscn` field — see the engine's `packages/engine/src/animation/xstate-animation-binding.ts` and a scaffolded `--example third-person`'s `src/components.ts`.
|
|
55
|
+
3. **Read `src/scripts/registry.ts`.** Every component used in a scene must be registered here. Unregistered components fail with "Component not found in registry".
|
|
56
|
+
|
|
57
|
+
## Commands
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
vgai edit # Launch the editor for this project (opens browser)
|
|
61
|
+
vgai play # Enter play mode
|
|
62
|
+
# Play-mode (and ingest-mode) edit policy:
|
|
63
|
+
# - LIVE-BUT-DISCARDED: gizmo transforms, rename,
|
|
64
|
+
# visibility, environment tuning — applied to the
|
|
65
|
+
# running scene, thrown away on stop.
|
|
66
|
+
# - BLOCKED (console warning): saving (Cmd/Ctrl+S),
|
|
67
|
+
# structural ops (add/delete/cut/copy/paste/reparent),
|
|
68
|
+
# inspector edits that rebuild the object (mesh,
|
|
69
|
+
# material, physics, ...), undo/redo, opening files.
|
|
70
|
+
# Nothing you change during play can leak into your
|
|
71
|
+
# .vscn. Stop play mode to persist edits.
|
|
72
|
+
vgai status # Check editor state
|
|
73
|
+
vgai apply-diff <scene.vscn.json> <patch.json> [--report]
|
|
74
|
+
# The canonical programmatic edit op: apply a
|
|
75
|
+
# JSON SceneDiff patch (add/remove/update/move ops +
|
|
76
|
+
# environment/name) to a scene file. Validates the
|
|
77
|
+
# scene, the patch, AND the result through the engine's
|
|
78
|
+
# Zod schemas, and dereferences every asset reference
|
|
79
|
+
# the diff introduces (existence for all; schema parse
|
|
80
|
+
# for .mat/.prefab refs) — nothing is
|
|
81
|
+
# written on any failure.
|
|
82
|
+
# --report: validate + summarize only, never write.
|
|
83
|
+
# Prefer this over hand-editing .vscn JSON in scripts.
|
|
84
|
+
npm run dev # Open the EDITOR on this project (delegates to `vgai edit .`) —
|
|
85
|
+
# the authoring surface; press Play (or `vgai play`) to run the game
|
|
86
|
+
npm run game # Vite dev server for the STANDALONE game page (no editor) — the
|
|
87
|
+
# ship-shaped form, for player-view testing/bots/CI, not authoring
|
|
88
|
+
npm run build # tsc + vite build
|
|
89
|
+
npm run typecheck # Type-check
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Project Structure
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
public/
|
|
96
|
+
scenes/ .vscn.json scene files (entities, transforms, components)
|
|
97
|
+
audio/ Audio assets
|
|
98
|
+
src/
|
|
99
|
+
main.ts Entry point — creates game runtime
|
|
100
|
+
data/ Data assets: <name>.schema.ts + emitted .schema.json + .data.json
|
|
101
|
+
(tuning.* ships as the worked example; see src/data/README.md)
|
|
102
|
+
tools/ Project tools: <name>.tool.tsx editor panels (dock tab or
|
|
103
|
+
inspector section) — editor-only, never bundled
|
|
104
|
+
(example.tool.tsx + spin.tool.tsx ship; see src/tools/README.md)
|
|
105
|
+
scripts/
|
|
106
|
+
main.ts Game setup — loads scenes, registers components, mounts the default HUD (keep .ts — its path is pinned in vgai.game.json + src/main.ts ENTRY_MODULES; see the React rule)
|
|
107
|
+
registry.ts Component registry (GameComponent classes)
|
|
108
|
+
components/ Custom GameComponent classes
|
|
109
|
+
ui/ React HUD kit (Panel/Button/Toggle/Slider/...) + DefaultHud.tsx, mount-ui.ts
|
|
110
|
+
server/
|
|
111
|
+
rooms/ Colyseus multiplayer rooms
|
|
112
|
+
project.json Project metadata (name, resolution, default scene)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Key Concepts
|
|
116
|
+
|
|
117
|
+
The engine has six fundamental primitives — everything you build is one of
|
|
118
|
+
these, and each has exactly one home:
|
|
119
|
+
|
|
120
|
+
- **Worlds** — the composition units (`vgai.game.json`): `threejs` / `react` /
|
|
121
|
+
`pixijs`, each with an `entry`; a game is one or more worlds composed by the
|
|
122
|
+
runtime and the editor alike.
|
|
123
|
+
- **Scenes** — *where things are*: `.vscn.json` files in `public/scenes/`
|
|
124
|
+
defining entity hierarchies with transforms, meshes, materials, lights,
|
|
125
|
+
cameras, physics, and components. **Prefabs** (`.prefab.json`) are reusable
|
|
126
|
+
entity templates.
|
|
127
|
+
- **GameComponents** — *how things behave*: per-entity classes with
|
|
128
|
+
`init`/`update`/`dispose` and a `static schema` (Zod) for per-entity config
|
|
129
|
+
(see Components below).
|
|
130
|
+
- **Loop hooks** — *when things run*: per-frame system phases `input` →
|
|
131
|
+
`prePhysics` → `physics` → `postPhysics` → `gameLogic` → `animation` →
|
|
132
|
+
`preRender` → `render`, via `GameComponent.phase` or `ctx.systems.add`.
|
|
133
|
+
**Every `setup()` must return a `dispose()`** that cleans up all objects,
|
|
134
|
+
bodies, listeners, and UI.
|
|
135
|
+
- **Data** — *what things are*: schema'd JSON in `src/data/`, the layer the
|
|
136
|
+
inspector (and designers, and VS Code) edit — shared/global definitions and
|
|
137
|
+
tuning, as opposed to per-entity component config. See "Tunable values → a
|
|
138
|
+
data asset" under One Canonical Way.
|
|
139
|
+
- **Tools** — *how you tune it*: project-authored React panels
|
|
140
|
+
(`src/tools/*.tool.tsx`) the editor discovers automatically. Two
|
|
141
|
+
placements: `placement: 'dock'` (a ⚙ tab after the built-in bottom tabs —
|
|
142
|
+
project-global dashboards) and `placement: 'inspector'` with a
|
|
143
|
+
`match: (node, adapter) => boolean` predicate (a ⚙ section in the Inspector
|
|
144
|
+
for matching selections). Each tool file exports `tool = defineTool({...})`
|
|
145
|
+
(from `@engine/tools/define-tool`) plus a default React component; build
|
|
146
|
+
the panel from `@editor/widgets` (the editor's own widget kit + THEME
|
|
147
|
+
tokens) so it looks native. `src/tools/example.tool.tsx` (dock) and
|
|
148
|
+
`spin.tool.tsx` (inspector) ship as worked examples — both live-tune
|
|
149
|
+
`src/data/tuning.data.json` through `useData`/`writeData`
|
|
150
|
+
(`@engine/react/use-data`) and the running game follows via HMR. An
|
|
151
|
+
inspector tool can also read the selected entity's OWN component config —
|
|
152
|
+
`useSelection()` (`@engine/react/use-selection`) returns the selected node,
|
|
153
|
+
its attached components, and each component's authored config, live.
|
|
154
|
+
Tools are editor-only dev surfaces, stripped from shipped games by
|
|
155
|
+
construction: `npm run build` bundles only what `index.html` →
|
|
156
|
+
`src/main.ts` imports, and nothing in game code imports `src/tools/`.
|
|
157
|
+
|
|
158
|
+
## Components
|
|
159
|
+
|
|
160
|
+
All per-entity behavior is a **GameComponent class** — an OOP class with an `init`/`update`/`dispose` lifecycle. The Object3D *is* the entity (there is no ECS / no `eid`): inside a component, `this.object3D`, `this.rigidBody`, and `this.collider` are wired up for you. **Every component referenced by a scene must be registered in `src/scripts/registry.ts`** — unregistered components cause "Component not found in registry" at play time.
|
|
161
|
+
|
|
162
|
+
| | GameComponent Class |
|
|
163
|
+
|---|---|
|
|
164
|
+
| **What** | OOP class extending `GameComponent` with lifecycle (`init`/`update`/`dispose`, optional `onTriggerEnter`/`onTriggerExit`) |
|
|
165
|
+
| **Use for** | All gameplay behavior — controllers, cameras, AI, pickups, tags |
|
|
166
|
+
| **Access** | `this.object3D` / `this.rigidBody` / `this.collider`; config fields from `static schema` (Zod) |
|
|
167
|
+
| **Editable fields** | Declare a `static schema = z.object({...})` with `.describe()` + `.default()` — these become typed, documented widgets in the editor inspector and are validated on load |
|
|
168
|
+
| **HMR** | Yes (prototype swap preserves instance state on live edit) |
|
|
169
|
+
| **Reference** | `src/scripts/components/scene-camera.ts` (`SceneCamera` — declares `static phase` + `static schema`) in this project; for richer patterns, scaffold a worked example (`npx create-vgai-project <name> --example fps`, `--example vehicle`, `--example third-person`, ...) and read its `src/components.ts`; base class at `packages/engine/src/ecs/game-component.ts` |
|
|
170
|
+
|
|
171
|
+
**Before creating a component:** read the reference above (or a scaffolded example's, per the "Read an example" step) and copy the pattern (`static phase`, `static schema`, `init`/`update`). Don't author ad-hoc `ctx.systems.add` closures for gameplay — they're neither editable nor hot-reloadable; reserve `systems.add` for non-gameplay infra (audio mixers, network send, etc.).
|
|
172
|
+
|
|
173
|
+
**Procedural games (no scene file):** components also attach at runtime to
|
|
174
|
+
code-created objects — construct the instance yourself and call
|
|
175
|
+
`ctx.components.attach(object3D, instance)`; the component manager runs `init`
|
|
176
|
+
and the per-frame lifecycle exactly as for scene-authored components. A fully
|
|
177
|
+
procedural world (voxel terrain, roguelike floors, …) legitimately has a
|
|
178
|
+
manifest world with an `entry` and **no `scene`**, and skips `.vscn` authoring
|
|
179
|
+
entirely — registry registration is only *required* for components referenced
|
|
180
|
+
by scene files. The editor still works on such games: play mode adopts the
|
|
181
|
+
live scene graph into the hierarchy/inspector.
|
|
182
|
+
|
|
183
|
+
**Registration:** `src/scripts/registry.ts` exports two maps:
|
|
184
|
+
- `componentRegistry` — **all** GameComponent classes referenced by scene files (used by the loader + scene validator)
|
|
185
|
+
- `gameComponents` — the same classes, for HMR
|
|
186
|
+
|
|
187
|
+
**In scene JSON**, components are an object keyed by name: `"components": { "Checkpoint": {}, "MarbleController": { "forceStrength": 12 } }`. Names must match `componentRegistry` keys; field names must match the component's `static schema`. Empty `{}` for tag components (no schema).
|
|
188
|
+
|
|
189
|
+
**System phases** (for `ctx.systems.add` and `GameComponent.phase`): `input` → `prePhysics` → `physics` → `postPhysics` → `gameLogic` (default) → `animation` → `preRender` → `render`
|
|
190
|
+
|
|
191
|
+
## One Canonical Way Per Task
|
|
192
|
+
|
|
193
|
+
For each common job there is exactly ONE recommended approach. Use it — competing paths cause ambiguity and non-determinism.
|
|
194
|
+
|
|
195
|
+
- **Camera → a camera GameComponent.** Attach a component that positions/aims `ctx.camera` (the shipped patterns to copy: `FPSCamera` in a `--example fps` scaffold's `src/components.ts`, `ChaseCamera` in `--example vehicle`, `CameraTarget` in `--example third-person` — all schema-declared and driven in `preRender`). This project also registers a minimal `SceneCamera` (`src/scripts/components/scene-camera.ts` — schema-declared position + look-at target). Precedence is fixed and intentional:
|
|
196
|
+
|
|
197
|
+
`editor viewport camera < scene-defined camera < camera GameComponent`
|
|
198
|
+
|
|
199
|
+
The editor's viewport transform is only the *initial* viewpoint, so a scene-defined or component-driven camera always wins (this is why play mode uses your scene's camera, not the editor's orbit camera). Do **not** also write an ad-hoc `ctx.systems.add('preRender', ...)` camera closure — put the camera logic in the component.
|
|
200
|
+
|
|
201
|
+
- **Gameplay behavior → a GameComponent.** Never an ad-hoc `ctx.systems.add` closure (those are neither editable nor hot-reloadable). Reserve `systems.add` for non-gameplay infra only (audio mixers, network send, debug overlays). See the Components section above.
|
|
202
|
+
|
|
203
|
+
- **Tunable values → a data asset, never a constant.** If a designer (or you,
|
|
204
|
+
while tuning) should be able to change a value without editing game code, it
|
|
205
|
+
lives in `src/data/` as JSON governed by a Zod schema — not as a constant in
|
|
206
|
+
a component or `setup()`. Per-entity config stays in the component's
|
|
207
|
+
`static schema` (see Components); data assets are the shared/global layer:
|
|
208
|
+
feel tuning, economy numbers, tables of game definitions.
|
|
209
|
+
|
|
210
|
+
Two blessed shapes, and only these (they're what the editor's DATA tab can
|
|
211
|
+
render as real widgets): a **singleton** — one `z.object` config whose
|
|
212
|
+
fields are scalars (number/string/boolean/enum), optionally grouped one or
|
|
213
|
+
two levels deep in nested `z.object`s (the tab renders groups as sub-forms)
|
|
214
|
+
— or a **table** — `z.record(z.string(), rowSchema)` with a flat row schema,
|
|
215
|
+
rendered as a grid (this Zod version's `z.record` takes a key schema AND a
|
|
216
|
+
value schema — the single-arg `z.record(rowSchema)` shorthand from Zod 3
|
|
217
|
+
throws here). Anything the tab can't type (arrays, deep nesting, unions)
|
|
218
|
+
degrades to a raw-JSON cell that refuses invalid commits — prefer
|
|
219
|
+
flattening, or a table, when a designer should tune it. Tables also get a
|
|
220
|
+
spreadsheet-friendly bulk-edit path: the DATA tab's **Export CSV** /
|
|
221
|
+
**Import CSV** buttons round-trip the whole table (header `key,<row
|
|
222
|
+
fields>`). Import is a WHOLE-TABLE REPLACE (a row the sheet doesn't mention
|
|
223
|
+
is deleted) and ATOMIC (any cell that fails to coerce to its field's kind —
|
|
224
|
+
e.g. non-numeric text in a `number` column — writes NOTHING; fix the sheet
|
|
225
|
+
and re-import). `"$schema"` is never a CSV column; it's carried over from
|
|
226
|
+
the file automatically.
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
// src/data/tuning.schema.ts — engineer-owned: the contract
|
|
230
|
+
import { z } from 'zod'
|
|
231
|
+
export const tuningSchema = z.object({
|
|
232
|
+
gravity: z.number().min(0).max(50).default(9.8).describe('m/s², downward'),
|
|
233
|
+
})
|
|
234
|
+
```
|
|
235
|
+
```jsonc
|
|
236
|
+
// src/data/tuning.data.json — designer-owned: the values
|
|
237
|
+
{ "$schema": "./tuning.schema.json", "gravity": 9.8 }
|
|
238
|
+
```
|
|
239
|
+
```ts
|
|
240
|
+
// src/data/tuning.ts — the live handle (engine defineData, W1)
|
|
241
|
+
import { defineData } from '@engine/data/data-asset'
|
|
242
|
+
import raw from './tuning.data.json'
|
|
243
|
+
import { tuningSchema } from './tuning.schema'
|
|
244
|
+
export const tuning = defineData(tuningSchema, raw, 'src/data/tuning.data.json')
|
|
245
|
+
// ['default'] not .default: the template tsconfig's
|
|
246
|
+
// noPropertyAccessFromIndexSignature applies to Vite's ModuleNamespace
|
|
247
|
+
import.meta.hot?.accept('./tuning.data.json', (m) => tuning.hotSwap(m?.['default']))
|
|
248
|
+
```
|
|
249
|
+
Read `tuning.get()` live where the value is used (e.g. each frame) — don't
|
|
250
|
+
copy fields out at init, or HMR edits won't reach them. `subscribe(fn)` fires
|
|
251
|
+
on every hot edit. **This project ships the pattern working**: the trio in
|
|
252
|
+
`src/data/` (`tuning.schema.ts` / emitted `tuning.schema.json` /
|
|
253
|
+
`tuning.data.json`), the handle (`src/data/tuning.ts`), and a consumer
|
|
254
|
+
(`DataSpinner` on the default scene's Box — press ▶, edit the JSON, watch it
|
|
255
|
+
change live). Add a data asset by copying that trio AND registering it in
|
|
256
|
+
`src/data/assets.ts` (the one shared list); after every schema change run
|
|
257
|
+
`npm run emit-schemas` (commits the emitted `.schema.json` so VS Code
|
|
258
|
+
validates the data file with no dev server). Rows in table-shaped assets
|
|
259
|
+
are referenced as `"file#key"` strings — declare a cross-asset field with
|
|
260
|
+
`dataRef('items')` (`@engine/data/data-ref`) instead of a bare `z.string()`:
|
|
261
|
+
the emitted schema gets a real `pattern` AND the editor's Data tab renders
|
|
262
|
+
it as a `stem#key` PICKER (a plain string still works, but has no picker
|
|
263
|
+
and doesn't survive a target-file rename in `vgai doctor`'s checks). Read
|
|
264
|
+
the referenced row with `getRef(itemsHandle, enemy.drop)` — pass the
|
|
265
|
+
TARGET's own handle, not a stem string; it resolves live off `.get()`, same
|
|
266
|
+
HMR doctrine as everything else in this file. Data assets are immutable at
|
|
267
|
+
runtime — runtime state lives in your game's own state, never written back
|
|
268
|
+
into data files. Enforcement is automatic: `npm run build` fails loud on a
|
|
269
|
+
data file that no longer matches its schema, a dangling `"file#key"` ref (or,
|
|
270
|
+
for a declared `dataRef` field, a target file that no longer exists at all),
|
|
271
|
+
or tool code reaching the shipped bundle, and `vgai doctor <folder>` runs
|
|
272
|
+
the same data checks terminal-side.
|
|
273
|
+
(The editor's Data dock tab edits these files in place, and project
|
|
274
|
+
tools (`src/tools/`, see Key Concepts) tune them live through the same
|
|
275
|
+
file-write path. WSL note: if this project lives on `/mnt/c` (drvfs — no
|
|
276
|
+
inotify), set `VGAI_WATCH_POLL=1` in the environment; the editor dev server
|
|
277
|
+
AND this project's standalone `npm run game` both honor it, and without it
|
|
278
|
+
external edits to `.data.json` never hot-reload into the running game.)
|
|
279
|
+
|
|
280
|
+
- **Material → inline `material` OR `materialRef`, never both.** An entity uses one or the other: inline `material` for a one-off look, or `materialRef` pointing at a shared `.mat.json` when several entities share a material. Setting both is a hard load-time validation error (enforced by a Zod refine in the engine schema) — there is no silent precedence to memorize.
|
|
281
|
+
|
|
282
|
+
## Rules
|
|
283
|
+
|
|
284
|
+
- **The engine is source-linked, not copied.** `@vgai/engine` resolves as a
|
|
285
|
+
symlink back into the vgai checkout you scaffolded from (a `file:`
|
|
286
|
+
dependency plus `@engine/*` aliases into `<checkout>/packages/engine/src`).
|
|
287
|
+
The engine source is yours to read — but **editing it edits the shared
|
|
288
|
+
checkout**, instantly affecting every project scaffolded from it. A game
|
|
289
|
+
that truly needs a divergent engine forks the checkout. Your manifest's
|
|
290
|
+
`engine.version` records which engine version this project was scaffolded
|
|
291
|
+
or last upgraded against; after updating the checkout, run `vgai upgrade`
|
|
292
|
+
(report first; `--report` for report-only) to re-sync untouched template
|
|
293
|
+
files and re-pin — your own edits are never overwritten.
|
|
294
|
+
**drvfs caveat (WSL, project on /mnt/c):** npm materializes the `file:`
|
|
295
|
+
dependency as a COPY there, not a symlink — after engine changes, code that
|
|
296
|
+
resolves through `node_modules/@vgai/engine` (root scripts like
|
|
297
|
+
`emit-schemas.ts`/`validate-scenes.ts`) sees a STALE engine until you
|
|
298
|
+
refresh it: `rm -rf node_modules/@vgai/engine && npm install`. The
|
|
299
|
+
`@engine/*` tsc/vite aliases always see the live checkout and are
|
|
300
|
+
unaffected.
|
|
301
|
+
- **Use engine/library APIs directly.** No wrappers around Three.js or Rapier.
|
|
302
|
+
- **GameComponents are for gameplay entities only.** Static scenery, lights, and UI are plain Three.js objects.
|
|
303
|
+
- **React: HUD overlay via `mountUI(ctx.uiContainer, <Component />)` (inline styles, no CSS files), OR a full react world — declare `kind: "react"` + `entry` in `vgai.game.json` and default-export a component (JSX is truth, no scene file; GameComponents can't attach to react world nodes). The UI container is reset between scene mounts — a react WORLD is the persistent-UI (title-screen shell) pattern: it lives at world scope, not scene scope.** This project ships one HUD overlay out of the box — `src/ui/DefaultHud.tsx` (ordinary props/`useState`, normal inline styling, no Scene UI `UINode`), mounted by `src/ui/mount-default-hud.tsx` and wired into `src/scripts/main.ts`'s `setup()`/`dispose()`. Edit or delete it freely; nothing else in the template depends on it. Do **not** author a `.vscn` top-level `ui` field or import `@vgai/scene-ui` for new gameplay UI — that data-language path is being retired (spec `docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md` §9 C1/C5); use ordinary React instead.
|
|
304
|
+
|
|
305
|
+
To bridge live game state (a singleton gameplay component's score/lives/wave)
|
|
306
|
+
into the HUD, don't poll — wrap the HUD in `<GameProvider game={ctx.game!}>`
|
|
307
|
+
and read with `useGameState((g) => g.queryByComponent(MyComponent)[0]?.field)`;
|
|
308
|
+
both ship in this project's `src/ui/game-state.tsx` (a re-export of
|
|
309
|
+
`@engine/react/game-state`), and the selector re-runs at most once per frame.
|
|
310
|
+
Worked reference: the `hp-hud` example's `src/hud.tsx`.
|
|
311
|
+
|
|
312
|
+
Mounting that HUD from `src/scripts/main.ts`: keep it a `.ts` file. Its exact
|
|
313
|
+
path string is load-bearing in TWO places — `vgai.game.json`'s `entry` and the
|
|
314
|
+
`ENTRY_MODULES` key in `src/main.ts` — and only the standalone build
|
|
315
|
+
(`npm run game`) throws on a mismatch; the editor resolves the entry itself,
|
|
316
|
+
so a `.tsx` rename looks fine in `vgai edit` and breaks later. Since a `.ts`
|
|
317
|
+
file can't contain JSX, keep the JSX in a sibling `.tsx` (like hp-hud's
|
|
318
|
+
`hud.tsx`) and mount with
|
|
319
|
+
`mountUI(ctx.uiContainer, React.createElement(GameProvider, { game: ctx.game! }, React.createElement(MyHud)))`.
|
|
320
|
+
- **Multiplayer → Colyseus directly.** vgai deliberately has no engine-level networking wrapper. Typed `joinOrCreate<T>` + the v0.17 Callbacks API (`onAdd`/`onChange`/`onRemove`; per-field `listen` — but see the P2P caveat below before using it); dynamic-import `@colyseus/sdk` so single-player bundles stay clean. Read live schema properties inside `onChange`; never destructure schema fields into local copies. The worked reference is `network.ts` in a `--example third-person-arena` scaffold (raw Callbacks + an ambient type mirror of the server schema; the engine's old `net/` wrapper is deleted).
|
|
321
|
+
- **P2P mode (browser-hosted rooms) → write dual-target code.** Your room can
|
|
322
|
+
also run browser-hosted over WebRTC via `@vgai/p2p-colyseus`: build/serve
|
|
323
|
+
with `VGAI_P2P=1` (this project's `vite.config.ts` aliases the Colyseus
|
|
324
|
+
packages onto browser-safe compat shims only then) and launch with
|
|
325
|
+
`?net=p2p-host` / `?net=p2p-join&roomId=…`. The same room class still runs
|
|
326
|
+
on a real server (`npm run server`), so keep room + client code inside the
|
|
327
|
+
two runtimes' intersection — full rules with rationale in the engine
|
|
328
|
+
checkout's `docs/P2P-COLYSEUS-MIGRATION.md` ("Dual-Target Portability
|
|
329
|
+
Rules"); the short list:
|
|
330
|
+
- No `$.listen` in the compat layer — read root-state fields inside
|
|
331
|
+
`room.onStateChange` (use `.once(...)`, not its return value: the real
|
|
332
|
+
SDK returns an EventEmitter, the compat an unsubscribe function).
|
|
333
|
+
- No `this.clock` on the compat `Room` — drive all timing from
|
|
334
|
+
`setSimulationInterval` (tick counters for delays).
|
|
335
|
+
- Never send messages from `onJoin` (dropped under P2P) — catch-up is
|
|
336
|
+
client-initiated: register handlers, then send a `sync_req`; reply in
|
|
337
|
+
bounded batches.
|
|
338
|
+
- Keep bulky world data OUT of room state (the P2P runtime diffs the whole
|
|
339
|
+
state per client per tick) — state carries players/actors + small
|
|
340
|
+
scalars; the world is a seed + an event log sent as messages.
|
|
341
|
+
- Relay budget ~30 messages/peer/sec, 16 KB max envelope — throttle moves
|
|
342
|
+
(~10 Hz) and batch bulk sync under the cap.
|
|
343
|
+
|
|
344
|
+
## Editor Control
|
|
345
|
+
|
|
346
|
+
Use the `/editor` skill to control the visual editor (select entities, focus camera, play/stop, toggle display modes). The skill documents the `vgai` CLI and `@vgai/editor-sdk`.
|
|
347
|
+
|
|
348
|
+
### Quick examples:
|
|
349
|
+
```bash
|
|
350
|
+
# Select and focus an entity after editing its scene file
|
|
351
|
+
vgai select <entity-id> && vgai focus <entity-id>
|
|
352
|
+
|
|
353
|
+
# Test gameplay
|
|
354
|
+
vgai play
|
|
355
|
+
|
|
356
|
+
# Check editor state
|
|
357
|
+
vgai status
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Autoplay bots — testing your game in cycles
|
|
361
|
+
|
|
362
|
+
The highest-leverage test harness for a vgai game is a **synthetic player**:
|
|
363
|
+
a bot that plays the game the way a human does. The full doctrine and the
|
|
364
|
+
evidence behind it live in the engine checkout's
|
|
365
|
+
`docs/SYNTHETIC-PLAYER-DESIGN.md`; this section is the practical playbook.
|
|
366
|
+
|
|
367
|
+
**The principle: synthesized input in, observable state out, no backdoors.**
|
|
368
|
+
The bot presses the same virtual keys and reads the same game state a player
|
|
369
|
+
sees. It never teleports the player, calls gameplay methods directly, or
|
|
370
|
+
writes inventory outside a declared test fixture. To the extent the bot
|
|
371
|
+
cannot cheat, every *gameplay* failure it hits is a real player-facing
|
|
372
|
+
defect — a physics gap, a missing line-of-sight check, a UI-sequencing bug.
|
|
373
|
+
(The bot has its own bug class too — planner/executor defects. A red run is
|
|
374
|
+
a lead, not a verdict; the logs below are how you tell the buckets apart.)
|
|
375
|
+
If some capability forces a compromise (e.g. headless pages have no pointer
|
|
376
|
+
lock, so mouse-look may need a direct write), DECLARE it in your docs as a
|
|
377
|
+
known violation — never let backdoors accumulate silently.
|
|
378
|
+
|
|
379
|
+
**Actuation** — the engine's `InputManager` already ships the seam:
|
|
380
|
+
- `input.setVirtualKey(code, pressed)` — synthesizes key state through the
|
|
381
|
+
real action-binding path.
|
|
382
|
+
- `input.setLookStick(x, y)` — virtual look input, injected as mouse delta
|
|
383
|
+
during `poll()` (the headless-friendly alternative to pointer lock).
|
|
384
|
+
Run the bot against `npm run game` (the standalone player-view page — that's
|
|
385
|
+
what it's for), gated behind a query param like `?bot=1&goal=<skill>&reps=N`.
|
|
386
|
+
|
|
387
|
+
**Perception** — read the game's own observable state (the same objects and
|
|
388
|
+
state your HUD reads), plus a debug handle you expose on `window` for the
|
|
389
|
+
harness (e.g. `__myGame = { gameState, bot }`). World queries that scan
|
|
390
|
+
through terrain/content the player has never seen are X-ray — assisted
|
|
391
|
+
perception. Sometimes pragmatic, but declare it.
|
|
392
|
+
|
|
393
|
+
**Test in layers — never debug a pure-logic bug through the browser:**
|
|
394
|
+
1. **L1 — headless logic tests.** Extract pure models (movement/physics
|
|
395
|
+
stepping, pathfinding, world rules) into functions a Node script can call
|
|
396
|
+
with no browser, no renderer. Seconds per run; this is where most bugs
|
|
397
|
+
should be found and fixed.
|
|
398
|
+
2. **L2 — goal isolation.** One skill, from a fresh fixture, N repetitions,
|
|
399
|
+
under a time cap (e.g. a `bot-probe` script that boots the game page
|
|
400
|
+
headless, runs `?bot=1&goal=chop-wood&reps=3`, and greps the event log).
|
|
401
|
+
A skill isn't done until it passes ~3×3 across ≥3 world seeds/fixtures.
|
|
402
|
+
3. **L3 — full mission.** The bot plays start-to-goal. Only run this when
|
|
403
|
+
the isolated skills underneath are green — otherwise every failure is
|
|
404
|
+
ambiguous.
|
|
405
|
+
|
|
406
|
+
**Logs and analytics from day one — they ARE the iteration loop:**
|
|
407
|
+
- **`lastFail` on every skill**: whenever a bot skill gives up, it records a
|
|
408
|
+
one-line reason with coordinates/state (`"mineBlock 4,61,-1: crosshair on
|
|
409
|
+
4,62,-1"`). A probe that ends `goal-failed` with no `lastFail` is a
|
|
410
|
+
harness bug.
|
|
411
|
+
- **A trace ring buffer** (last ~60 bot decisions: step taken, expected vs
|
|
412
|
+
actual result) so a failure comes with its recent history attached.
|
|
413
|
+
- **A structured event log** (`goal-passed`/`goal-failed`/`mission-complete`
|
|
414
|
+
+ timestamp + position/hp), printed to console so a headless harness can
|
|
415
|
+
grep it, plus periodic screenshots if you drive via Playwright.
|
|
416
|
+
- The iteration cycle is: red run → read `lastFail` + trace → reproduce at
|
|
417
|
+
the LOWEST layer that exhibits it → fix → re-run the isolation → re-run
|
|
418
|
+
the full matrix. Without the logs, step two is guesswork.
|
|
419
|
+
|
|
420
|
+
**Two hard-won invariants** (each cost a debugging session to learn):
|
|
421
|
+
- If your bot is a generator/coroutine stepped once per frame, every loop
|
|
422
|
+
iteration must `yield`. A loop that can re-enter without yielding (e.g.
|
|
423
|
+
"plan failed → retry") is a synchronous freeze that pins the renderer.
|
|
424
|
+
- Never edit source files watched by a running dev server mid-probe — the
|
|
425
|
+
hot reload kills the run and the result is garbage either way.
|
|
426
|
+
- If you drive either dev server (`vgai edit`/`npm run dev`, or `npm run
|
|
427
|
+
game`) with Playwright, use `page.goto(url, { waitUntil: 'load' })`, never
|
|
428
|
+
`'networkidle'` — the dev server keeps a live SSE/websocket/HMR
|
|
429
|
+
connection open, so the network never goes idle and `'networkidle'`
|
|
430
|
+
hangs forever.
|
|
431
|
+
|
|
432
|
+
**Multiplayer without multiplayer**: N synthetic players in N headless pages
|
|
433
|
+
against your Colyseus room is a real multiplayer session — same input paths,
|
|
434
|
+
same netcode, no humans needed. This is the cheapest way to soak-test rooms.
|
|
435
|
+
|
|
436
|
+
## Examples Catalog
|
|
437
|
+
|
|
438
|
+
Nothing example-shaped ships inside this project — every example is a
|
|
439
|
+
standalone project in the vgai monorepo's `examples/<id>/`, with its own
|
|
440
|
+
`src/`, scenes, `vgai.game.json`, and (where multiplayer) `server/`. Get one's
|
|
441
|
+
code by scaffolding it: `npx create-vgai-project <name> --example <id>` (e.g.
|
|
442
|
+
`--example third-person`) — a correctly-rewritten standalone copy. Or open it
|
|
443
|
+
live in the hosted editor at `https://vgai-editor.pages.dev/?project=<id>`
|
|
444
|
+
(read-only; scaffold to keep edits).
|
|
445
|
+
|
|
446
|
+
**Gameplay examples:**
|
|
447
|
+
- `third-person` — orbit camera, WASD movement, XState animation machine, AI chase
|
|
448
|
+
- `fps` — pointer lock, throwable projectiles, dynamic physics
|
|
449
|
+
- `vehicle` — Rapier raycast-vehicle suspension, steering, chase camera
|
|
450
|
+
- `rts` — top-down camera, box selection, navmesh crowd pathfinding
|
|
451
|
+
- `third-person-arena` — multiplayer tag with AI bots: client-authoritative movement, server-authoritative game state (tag/phases); ships its own `server/`
|
|
452
|
+
- `starfighter` — flight controller with schema-declared tuning fields
|
|
453
|
+
- `ik` — skeletal IK (CCDIKSolver) on a code-built rig
|
|
454
|
+
|
|
455
|
+
**Scene & technique showcases:**
|
|
456
|
+
- `editor-tutorial` — feature-showcase scene: meshes, materials, lights, physics, particles, audio, bloom
|
|
457
|
+
- `feature-scenes` — six technique scenes in one project: instancing, terrain, ocean, particles, custom water shader, HDR skybox
|
|
458
|
+
- `littlest-tokyo` — GLTF model scene (Draco-compressed; ships its decoder assets)
|
|
459
|
+
- `learn-joints`, `learn-matfiles`, `learn-triggers`, `learn-splines` — one-concept tutorials (physics joints, shared `.mat.json` materials, sensor triggers, spline motion)
|
|
460
|
+
|
|
461
|
+
**UI / multi-world examples:**
|
|
462
|
+
- `hp-hud` — React HUD reading live game state via `<GameProvider>` + `useGameState` (selectors call `g.queryByComponent(...)`) (the HUD-overlay pattern on a threejs world — for a `kind: "react"` WORLD example, see `tri-world`)
|
|
463
|
+
- `tri-world` — one manifest, three world kinds side by side (threejs + pixijs + react)
|
|
464
|
+
|
|
465
|
+
**Multiplayer note:** this project already ships a generic `server/` (a
|
|
466
|
+
`GameRoom` in `server/rooms/`, registered in `server/rooms.ts`), but the
|
|
467
|
+
Colyseus server is **not** started by `npm run dev`/`npm run game`. Run `npm run server` to
|
|
468
|
+
boot it standalone — it starts `server/main.ts` on `ws://localhost:2567` and
|
|
469
|
+
shuts down cleanly on Ctrl-C. Start it before connecting, or the client will
|
|
470
|
+
fail to connect. (The script runs tsx against `server/tsconfig.json`, not the
|
|
471
|
+
app tsconfig — `@colyseus/schema`'s legacy `@type` decorators need
|
|
472
|
+
`experimentalDecorators`, which only the server tsconfig sets.) The
|
|
473
|
+
`third-person-arena` example (`--example third-person-arena`) ships its own
|
|
474
|
+
richer `server/` (an `ArenaRoom` with tag/phase state) in place of this
|
|
475
|
+
generic one.
|
|
476
|
+
|
|
477
|
+
## Scene File Format
|
|
478
|
+
|
|
479
|
+
Entities in `.vscn.json` have these optional fields:
|
|
480
|
+
- `id` — UUID. The **editor** generates one when it creates an entity, and the
|
|
481
|
+
runtime loader auto-generates a random one for id-less entities at load (so
|
|
482
|
+
every loaded object carries an entity descriptor and appears in the play-mode
|
|
483
|
+
hierarchy). But a random id can't be *referenced*: anything targeted from
|
|
484
|
+
elsewhere (joints, god-rays, etc.) still needs an AUTHORED id.
|
|
485
|
+
- `name` — display name
|
|
486
|
+
- `transform` — `{ position: [x,y,z], rotation: [x,y,z,w], scale: [x,y,z] }`
|
|
487
|
+
- `mesh` — `{ type: 'box'|'sphere'|'plane'|'cylinder'|... }`
|
|
488
|
+
- `material` — `{ type: 'standard'|'physical', color, metalness, roughness, ... }` (inline material)
|
|
489
|
+
- `materialRef` — path to a shared `.mat.json` asset. **Mutually exclusive with `material`** — set one or the other, never both (load-time error).
|
|
490
|
+
- `light` — `{ type: 'directional'|'point'|'spot'|'ambient', ... }`
|
|
491
|
+
- `camera` — `{ type: 'perspective'|'orthographic', ... }`
|
|
492
|
+
- `physics` — `{ bodyType: 'dynamic'|'fixed'|'kinematic', collider: { type: 'cuboid'|'ball'|'capsule', halfExtents?, radius?, friction?, restitution?, density? }, mass? }`
|
|
493
|
+
|
|
494
|
+
**Physics contracts (current):**
|
|
495
|
+
- Physics entities may be nested under transformed parents: the loader
|
|
496
|
+
composes the full parent chain and spawns the body at the WORLD
|
|
497
|
+
transform. One exception, enforced loudly: a `dynamic` body under an
|
|
498
|
+
ancestor that itself has a `dynamic`/`kinematic` body is a load error
|
|
499
|
+
(Rapier has no parent/child body relationship).
|
|
500
|
+
- Collider dimensions (`halfExtents`, `radius`) are authored in local units
|
|
501
|
+
and **scale with the entity's composed world scale** (per-axis for
|
|
502
|
+
cuboids; round colliders require uniform scale on their round axes —
|
|
503
|
+
non-uniform is a loud load error). The editor's collider gizmo renders
|
|
504
|
+
the same computed dimensions the runtime uses.
|
|
505
|
+
- Collision events fire for all scene-loaded colliders: solid-solid
|
|
506
|
+
contacts reach `ctx.collisions.onCollision`; **sensor** colliders
|
|
507
|
+
additionally dispatch `onTriggerEnter/Exit` on components.
|
|
508
|
+
- `components` — `{ "ComponentName": { prop: value, ... }, ... }` (see Components section above)
|
|
509
|
+
- `children` — nested entity array
|
|
510
|
+
|
|
511
|
+
Scene files also carry a top-level `ui` field (React UI roots authored via the
|
|
512
|
+
editor's UI edit mode); it survives undo/redo and asset moves.
|
package/template/LICENSE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Volter AI, Inc.
|
|
4
|
+
|
|
5
|
+
This MIT license covers the vgai game-project template (this directory) — the
|
|
6
|
+
starter code scaffolded into your project. It is intentionally permissive so that
|
|
7
|
+
the game you build with vgai, and anything you generate from this template, is
|
|
8
|
+
YOURS, free of any obligations to us. You may relicense your generated game
|
|
9
|
+
under any terms you choose.
|
|
10
|
+
|
|
11
|
+
(The vgai engine and tooling — everything outside this directory — are licensed
|
|
12
|
+
per component: Apache-2.0 core, AGPL-3.0 editor. See the repository LICENSE.md.)
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emits `src/data/*.schema.json` from the Zod schemas — the schema-interchange
|
|
3
|
+
* step of docs/DATA-TOOLS-DESIGN.md §2.1 / D4 (in the engine checkout).
|
|
4
|
+
* The emitted artifact is committed so VS Code validates/autocompletes every
|
|
5
|
+
* `*.data.json` (via its `"$schema"` first line) with no dev server running.
|
|
6
|
+
*
|
|
7
|
+
* Also VALIDATES each asset's current `.data.json` against its schema and
|
|
8
|
+
* fails loud (the terminal half of the §6 doctor check, usable today) — a
|
|
9
|
+
* schema change that orphans existing data is caught here, not at runtime.
|
|
10
|
+
* The same list drives the BUILD-time check (`vite.config.ts` →
|
|
11
|
+
* `vgaiDataCheck`), and `vgai doctor <folder>` re-runs schema + ref
|
|
12
|
+
* validation from outside the project.
|
|
13
|
+
*
|
|
14
|
+
* Run after every schema change: `npm run emit-schemas`
|
|
15
|
+
* Register new data assets in `src/data/assets.ts` (the one shared list).
|
|
16
|
+
*/
|
|
17
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { fileURLToPath } from 'node:url';
|
|
20
|
+
import { parseDataJson, toDataJsonSchema } from '@vgai/engine/data/data-asset';
|
|
21
|
+
import { dataAssets } from './src/data/assets';
|
|
22
|
+
|
|
23
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
|
|
25
|
+
let failed = false;
|
|
26
|
+
for (const { name, schema } of dataAssets) {
|
|
27
|
+
const out = `src/data/${name}.schema.json`;
|
|
28
|
+
const data = `src/data/${name}.data.json`;
|
|
29
|
+
writeFileSync(path.join(here, out), `${JSON.stringify(toDataJsonSchema(schema), null, 2)}\n`);
|
|
30
|
+
console.log(`emit-schemas: wrote ${out}`);
|
|
31
|
+
|
|
32
|
+
// parseDataJson = the exact parse defineData runs at load (incl. the
|
|
33
|
+
// "$schema"-line strip), so this check can never disagree with runtime.
|
|
34
|
+
try {
|
|
35
|
+
parseDataJson(schema, JSON.parse(readFileSync(path.join(here, data), 'utf8')), data);
|
|
36
|
+
console.log(`emit-schemas: ${data} validates`);
|
|
37
|
+
} catch (err) {
|
|
38
|
+
failed = true;
|
|
39
|
+
console.error(String(err));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (failed) process.exit(1);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
|
6
|
+
<title>My Game</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
9
|
+
html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
|
|
10
|
+
#game-canvas { width: 100%; height: 100%; position: relative; }
|
|
11
|
+
</style>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="game-canvas"></div>
|
|
15
|
+
<script type="module" src="/src/main.ts"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|