@inneranimalmedia/agentsam-sdk 2.6.5 → 2.6.7
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/AGENTSAM.md +29 -0
- package/apps/README.md +5 -0
- package/apps/cad-creator/README.md +4 -4
- package/apps/cad-creator/shared/cad/package.json +1 -1
- package/apps/client-cms-editor/AGENTS.md +12 -7
- package/apps/client-cms-editor/README.md +76 -48
- package/apps/client-cms-editor/agentsam.app.json +26 -9
- package/apps/client-cms-editor/backend/package.json +20 -3
- package/apps/client-cms-editor/backend/src/api/client.ts +134 -101
- package/apps/client-cms-editor/backend/src/index.ts +1 -1
- package/apps/client-cms-editor/backend/src/model.ts +11 -115
- package/apps/client-cms-editor/backend/src/preview/bridge.ts +1 -1
- package/apps/client-cms-editor/backend/tsconfig.json +6 -1
- package/apps/client-cms-editor/bin/agentsam-cms.js +262 -0
- package/apps/client-cms-editor/bin/agentsam-cms.mjs +2 -228
- package/apps/client-cms-editor/frontend/package.json +16 -7
- package/apps/client-cms-editor/frontend/src/AgentSamDrawer.tsx +9 -41
- package/apps/client-cms-editor/frontend/src/CmsAgentSurface.tsx +19 -174
- package/apps/client-cms-editor/frontend/src/adapters/heuristic.ts +29 -0
- package/apps/client-cms-editor/frontend/src/adapters/http.ts +628 -0
- package/apps/client-cms-editor/frontend/src/adapters/memory.ts +510 -0
- package/apps/client-cms-editor/frontend/src/editor/CmsEditor.tsx +213 -0
- package/apps/client-cms-editor/frontend/src/editor/CmsEditorProvider.tsx +20 -0
- package/apps/client-cms-editor/frontend/src/editor/canvas/PreviewCanvas.tsx +53 -0
- package/apps/client-cms-editor/frontend/src/{CmsEditor.tsx → editor/legacy/CmsEditorPrototype.tsx} +52 -23
- package/apps/client-cms-editor/frontend/src/editor/useCmsEditorController.ts +218 -0
- package/apps/client-cms-editor/frontend/src/index.ts +19 -5
- package/apps/client-cms-editor/frontend/src/lib/agent-host.ts +24 -0
- package/apps/client-cms-editor/frontend/src/main.tsx +82 -8
- package/apps/client-cms-editor/frontend/src/mount.tsx +92 -30
- package/apps/client-cms-editor/frontend/src/styles/agentsam-drawer.css +1 -1
- package/apps/client-cms-editor/frontend/src/styles/cmsShell.css +8 -8
- package/apps/client-cms-editor/frontend/src/styles/studio.css +55 -3
- package/apps/client-cms-editor/frontend/tsconfig.json +12 -1
- package/apps/client-cms-editor/frontend/vite.config.ts +12 -0
- package/apps/client-cms-editor/package-lock.json +973 -134
- package/apps/client-cms-editor/package.json +118 -6
- package/apps/client-cms-editor/reference/harvest/HARVEST_RECEIPT.json +201 -0
- package/apps/client-cms-editor/reference/harvest/PROVENANCE_HISTORY.txt +10 -0
- package/apps/client-cms-editor/reference/harvest/README.md +8 -0
- package/apps/client-cms-editor/reference/harvest/plans/ARCHIVE-INTAKE.md +135 -0
- package/apps/client-cms-editor/reference/harvest/plans/CHATGPT-IDENTITY-HARVEST.md +83 -0
- package/apps/client-cms-editor/reference/harvest/plans/CMS-EDITOR-HARVEST.md +166 -0
- package/apps/client-cms-editor/reference/harvest/plans/HARVEST-INDEX.md +196 -0
- package/apps/client-cms-editor/reference/harvest/plans/INNERANIMALS-CONTENT-001.md +137 -0
- package/apps/client-cms-editor/reference/harvest/plans/NO-IMPLICIT-SIDE-EFFECTS.md +87 -0
- package/apps/client-cms-editor/reference/harvest/plans/STOREFRONT-SURFACE-MAP.md +140 -0
- package/apps/client-cms-editor/reference/harvest/plans/THEME-INNERANIMALS-HARVEST.md +137 -0
- package/apps/client-cms-editor/reference/harvest/plans/TOOLING-LAB.md +63 -0
- package/apps/client-cms-editor/reference/harvest/plans/harvest-manifest.json +97 -0
- package/apps/client-cms-editor/reference/harvest/plans/local-cms-harvest-schema.sql +328 -0
- package/apps/client-cms-editor/shared/cms/package.json +17 -7
- package/apps/client-cms-editor/shared/cms/src/adapter.ts +204 -0
- package/apps/client-cms-editor/shared/cms/src/agent-context.ts +7 -5
- package/apps/client-cms-editor/shared/cms/src/editor-types.ts +32 -0
- package/apps/client-cms-editor/shared/cms/src/host.ts +82 -0
- package/apps/client-cms-editor/shared/cms/src/index.ts +4 -0
- package/apps/client-cms-editor/shared/cms/src/map.ts +115 -0
- package/apps/client-cms-editor/shared/cms/src/starter-pack.ts +158 -0
- package/contracts/source-types.v1.json +353 -0
- package/docs/CMS_CONTEXTUAL_COMPOSER.md +1 -1
- package/docs/PRODUCT_LIFECYCLE.md +566 -0
- package/docs/THEME_REFINERY.md +2 -2
- package/docs/architecture/AGENTSAM_RUNTIME_PROTOCOL.md +40 -20
- package/docs/content-studio/REVISION_GATE_2026-09-27.md +1 -1
- package/docs/plans/LOCAL_STUDIO_DESKTOP_RELEASE_2026-09-29.md +150 -0
- package/migrations/d1/0017_identity_native_oauth.sql +21 -0
- package/native/agentsam-machine/Cargo.lock +426 -0
- package/native/agentsam-machine/Cargo.toml +9 -0
- package/native/agentsam-machine/cli/Cargo.toml +14 -0
- package/native/agentsam-machine/cli/src/main.rs +77 -0
- package/native/agentsam-machine/core/Cargo.toml +15 -0
- package/native/agentsam-machine/core/src/assets.rs +790 -0
- package/native/agentsam-machine/core/src/bound.rs +298 -0
- package/native/agentsam-machine/core/src/enrich.rs +706 -0
- package/native/agentsam-machine/core/src/frontend.rs +442 -0
- package/native/agentsam-machine/core/src/lib.rs +1086 -0
- package/native/agentsam-machine/core/src/liquid.rs +528 -0
- package/native/agentsam-machine/core/src/source_types.rs +78 -0
- package/package.json +51 -21
- package/packages/agentsam-brand/README.md +1 -1
- package/packages/agentsam-brand/package.json +4 -4
- package/packages/agentsam-brand/src/index.js +1 -1
- package/packages/agentsam-contracts/package.json +13 -3
- package/packages/agentsam-contracts/src/tools.ts +22 -0
- package/packages/agentsam-errors/package.json +13 -3
- package/packages/agentsam-knowledge/package.json +18 -4
- package/packages/agentsam-knowledge/src/autorag/index.js +10 -3
- package/packages/agentsam-knowledge/src/backends/index.js +33 -1
- package/packages/agentsam-knowledge/test/autorag.test.mjs +60 -1
- package/packages/agentsam-nav/package.json +24 -6
- package/packages/agentsam-queue-control/README.md +1 -1
- package/packages/agentsam-queue-control/package.json +7 -2
- package/packages/agentsam-rapid-rust/src/main.rs +1 -1
- package/packages/agentsam-repository/package.json +13 -3
- package/packages/agentsam-repository/src/source-types.js +8 -0
- package/packages/agentsam-vault/package.json +14 -4
- package/packages/agentsam-workbench/package.json +15 -4
- package/packages/agentsam-workbench/src/agent/ToolPermissionRequest.tsx +94 -0
- package/packages/agentsam-workbench/src/agent/index.ts +2 -0
- package/packages/agentsam-workbench/src/agent/tool-permission.css +134 -0
- package/packages/connectors/cloudflare/package.json +13 -3
- package/packages/connectors/cloudflare/src/api-client.js +3 -2
- package/packages/connectors/cloudflare/src/families/vectorize.js +127 -0
- package/packages/connectors/cloudflare/src/index.js +1 -0
- package/packages/heuristic-theme/README.md +2 -2
- package/packages/heuristic-theme/package.json +8 -3
- package/packages/heuristic-theme/src/registry.js +2 -2
- package/packages/heuristic-theme/src/theme.js +1 -1
- package/packages/heuristic-theme/test/theme.test.mjs +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/agentsam.feature.json +1 -1
- package/packages/identity/.agentsam/features/oauth-login-portal/providers.json +27 -7
- package/packages/identity/README.md +2 -2
- package/packages/identity/migrations/D1_SCHEMA_MAPPING.md +18 -15
- package/packages/identity/migrations/apply-portable.js +109 -0
- package/packages/identity/migrations/sqlite/004_identity_oauth_consent_catalog.sql +1 -1
- package/packages/identity/migrations/sqlite/005_identity_company_native.sql +52 -0
- package/packages/identity/package.json +27 -6
- package/packages/identity/schema/agentsam.identity/manifest.json +101 -0
- package/packages/identity/scripts/local-identity-bridge.mjs +13 -7
- package/packages/identity/scripts/preview-auth-portal.mjs +6 -6
- package/packages/identity/src/adapters/cloudflare-d1/index.js +8 -401
- package/packages/identity/src/adapters/iam-compat/index.js +540 -0
- package/packages/identity/src/adapters/portable-d1/index.js +18 -0
- package/packages/identity/src/adapters/portable-store.js +479 -0
- package/packages/identity/src/adapters/sqlite/index.js +13 -312
- package/packages/identity/src/contracts/auth-config.js +0 -1
- package/packages/identity/src/contracts/identity-store.js +56 -5
- package/packages/identity/src/contracts/provider.js +1 -0
- package/packages/identity/src/contracts/sign-in-options.js +104 -0
- package/packages/identity/src/core/constants.js +1 -1
- package/packages/identity/src/core/session-policy.js +31 -0
- package/packages/identity/src/frontend/auth-portal/pages/login.html +6 -6
- package/packages/identity/src/frontend/auth-portal/pages/reset.html +5 -5
- package/packages/identity/src/frontend/auth-portal/pages/signup.html +5 -5
- package/packages/identity/src/frontend/auth-portal/shared/company-branding.js +1 -1
- package/packages/identity/src/frontend/auth-portal/shared/desktop-identity-bridge.js +6 -37
- package/packages/identity/src/index.js +28 -1
- package/packages/identity/src/migrations/README.md +23 -23
- package/packages/identity/src/oauth/README.md +1 -1
- package/packages/identity/src/oauth/credentials.js +10 -11
- package/packages/identity/src/oauth/google-desktop-exchange.js +195 -27
- package/packages/identity/src/oauth/iam-platform.js +21 -9
- package/packages/identity/src/oauth/native-handoff.js +136 -0
- package/packages/identity/src/providers/chatgpt/headers.js +65 -0
- package/packages/identity/src/providers/chatgpt/index.js +86 -0
- package/packages/identity/src/providers/chatgpt/mapper.js +43 -0
- package/packages/identity/src/providers/chatgpt/paths.js +45 -0
- package/packages/identity/src/providers/github/oauth.js +1 -2
- package/packages/identity/src/providers/github/profile.js +16 -2
- package/packages/identity/src/providers/index.js +11 -3
- package/packages/identity/src/server/identity-service.js +2 -1
- package/packages/identity/src/server/worker-router.js +71 -18
- package/packages/identity/tests/chatgpt-provider.test.mjs +75 -0
- package/packages/identity/tests/google-desktop-identity-login.test.mjs +210 -0
- package/packages/identity/tests/identity-init-scaffold.test.mjs +15 -4
- package/packages/identity/tests/lane1-identity-store-parity.test.mjs +527 -0
- package/packages/identity/tests/lane1-packed-consumer.test.mjs +175 -0
- package/packages/identity/tests/native-oauth-handoff.test.mjs +292 -0
- package/packages/identity/tests/oauth-credentials.test.mjs +4 -4
- package/packages/identity/tests/preview-auth-portal.test.mjs +4 -2
- package/packages/identity/tests/session-routes-policy.test.mjs +13 -1
- package/packages/identity/tests/worker-runtime-boundary.test.mjs +11 -0
- package/packages/providers/completeful/package.json +7 -2
- package/packages/theme-church-site/package.json +17 -6
- package/packages/theme-church-site/src/harvest.json +4 -2
- package/packages/theme-church-site/src/index.js +7 -3
- package/packages/theme-church-site/test/theme.test.mjs +4 -2
- package/packages/theme-companions-site/package.json +17 -6
- package/packages/theme-companions-site/src/harvest.json +4 -2
- package/packages/theme-companions-site/src/index.js +7 -3
- package/packages/theme-companions-site/test/theme.test.mjs +4 -2
- package/packages/theme-floors-site/package.json +17 -6
- package/packages/theme-floors-site/src/harvest.json +4 -2
- package/packages/theme-floors-site/src/index.js +7 -3
- package/packages/theme-floors-site/test/theme.test.mjs +4 -2
- package/packages/theme-fuelnfree-site/package.json +17 -6
- package/packages/theme-fuelnfree-site/src/harvest.json +4 -2
- package/packages/theme-fuelnfree-site/src/index.js +7 -3
- package/packages/theme-fuelnfree-site/test/theme.test.mjs +4 -2
- package/packages/theme-handyman-site/package.json +17 -6
- package/packages/theme-handyman-site/src/harvest.json +4 -2
- package/packages/theme-handyman-site/src/index.js +7 -3
- package/packages/theme-handyman-site/test/theme.test.mjs +4 -2
- package/packages/theme-insurance-site/package.json +17 -6
- package/packages/theme-insurance-site/src/harvest.json +4 -2
- package/packages/theme-insurance-site/src/index.js +7 -3
- package/packages/theme-insurance-site/test/theme.test.mjs +4 -2
- package/packages/theme-scenes/README.md +1 -1
- package/packages/theme-scenes/package.json +4 -3
- package/packages/theme-scenes/src/registry.js +187 -0
- package/packages/theme-shinshu-site/package.json +17 -6
- package/packages/theme-shinshu-site/src/harvest.json +4 -2
- package/packages/theme-shinshu-site/src/index.js +7 -3
- package/packages/theme-shinshu-site/test/theme.test.mjs +4 -2
- package/packages/work-graph/package.json +14 -4
- package/protocol/FILEMETA_V1.md +2 -2
- package/protocol/capabilities/manifest.json +1 -1
- package/protocol/database/agentsam.schema-pack.v1.schema.json +72 -0
- package/protocol/presets/catalog.json +2 -2
- package/protocol/theme-refinery/lineage-registry.json +105 -0
- package/protocol/theme-refinery/product-metadata.schema.json +21 -1
- package/skills/grok-auth/SKILL.md +110 -0
- package/skills/grok-auth/agentsam.skill.json +25 -0
- package/skills/grok-auth/references/grok-identity.md +64 -0
- package/skills/grok-auth/references/per-user-data.md +49 -0
- package/skills/grok-auth/references/prewired-and-env.md +54 -0
- package/skills/grok-auth/references/session-ui.md +133 -0
- package/skills/grok-auth/references/sign-in-methods.md +55 -0
- package/skills/grok-auth/references/wiring.md +71 -0
- package/skills/grok-building-games/SKILL.md +213 -0
- package/skills/grok-building-games/agentsam.skill.json +25 -0
- package/skills/grok-building-games/references/3d-libs.md +64 -0
- package/skills/grok-building-games/references/ai-pathfinding.md +122 -0
- package/skills/grok-building-games/references/audio.md +129 -0
- package/skills/grok-building-games/references/babylon.md +202 -0
- package/skills/grok-building-games/references/collision-physics.md +122 -0
- package/skills/grok-building-games/references/ecs-architecture.md +111 -0
- package/skills/grok-building-games/references/game-feel-juice.md +110 -0
- package/skills/grok-building-games/references/genres/board-card-chess.md +89 -0
- package/skills/grok-building-games/references/genres/endless-runner.md +83 -0
- package/skills/grok-building-games/references/genres/fps.md +105 -0
- package/skills/grok-building-games/references/genres/platformer-2d.md +97 -0
- package/skills/grok-building-games/references/genres/puzzle-match3-tetris.md +131 -0
- package/skills/grok-building-games/references/genres/racing-kart.md +106 -0
- package/skills/grok-building-games/references/genres/topdown-twin-stick.md +97 -0
- package/skills/grok-building-games/references/genres/tower-defense.md +84 -0
- package/skills/grok-building-games/references/genres/voxel-minecraft.md +89 -0
- package/skills/grok-building-games/references/input.md +139 -0
- package/skills/grok-building-games/references/phaser.md +277 -0
- package/skills/grok-building-games/references/procedural-generation.md +141 -0
- package/skills/grok-building-games/references/save-persistence.md +108 -0
- package/skills/grok-building-games/references/threejs-foundational.md +148 -0
- package/skills/grok-controls/SKILL.md +274 -0
- package/skills/grok-controls/agentsam.skill.json +25 -0
- package/skills/grok-design-ui/SKILL.md +122 -0
- package/skills/grok-design-ui/agentsam.skill.json +25 -0
- package/skills/grok-design-ui/references/animations.md +379 -0
- package/skills/grok-design-ui/references/performance.md +88 -0
- package/skills/grok-design-ui/references/refined-ui.md +384 -0
- package/skills/grok-design-ui/references/surfaces.md +247 -0
- package/skills/grok-design-ui/references/typography.md +123 -0
- package/skills/grok-game-animation-frames/SKILL.md +84 -0
- package/skills/grok-game-animation-frames/agentsam.skill.json +25 -0
- package/skills/grok-game-asset-core/SKILL.md +69 -0
- package/skills/grok-game-asset-core/agentsam.skill.json +25 -0
- package/skills/grok-game-character-consistency/SKILL.md +69 -0
- package/skills/grok-game-character-consistency/agentsam.skill.json +25 -0
- package/skills/grok-game-tilesets/SKILL.md +65 -0
- package/skills/grok-game-tilesets/agentsam.skill.json +25 -0
- package/skills/grok-game-ui-icons/SKILL.md +56 -0
- package/skills/grok-game-ui-icons/agentsam.skill.json +25 -0
- package/skills/grok-generate2dmap/LICENSE +21 -0
- package/skills/grok-generate2dmap/SKILL.md +120 -0
- package/skills/grok-generate2dmap/SOURCE.md +6 -0
- package/skills/grok-generate2dmap/agentsam.skill.json +25 -0
- package/skills/grok-generate2dmap/references/deliverables.md +101 -0
- package/skills/grok-generate2dmap/references/layered-map-contract.md +209 -0
- package/skills/grok-generate2dmap/references/map-strategies.md +341 -0
- package/skills/grok-generate2dmap/references/object-production-gate.md +24 -0
- package/skills/grok-generate2dmap/references/pipeline.md +149 -0
- package/skills/grok-generate2dmap/references/prop-pack-contract.md +174 -0
- package/skills/grok-generate2dmap/references/side-scroll-stages.md +29 -0
- package/skills/grok-generate2dmap/scripts/compose_layered_preview.py +157 -0
- package/skills/grok-generate2dmap/scripts/extract_prop_pack.py +369 -0
- package/skills/grok-generate2dsprite/LICENSE +21 -0
- package/skills/grok-generate2dsprite/SKILL.md +313 -0
- package/skills/grok-generate2dsprite/SOURCE.md +6 -0
- package/skills/grok-generate2dsprite/agentsam.skill.json +25 -0
- package/skills/grok-generate2dsprite/references/modes.md +124 -0
- package/skills/grok-generate2dsprite/references/prompt-rules.md +315 -0
- package/skills/grok-generate2dsprite/scripts/generate2dsprite.py +1031 -0
- package/skills/grok-generate2dsprite/scripts/make_layout_guide.py +109 -0
- package/skills/grok-imagine/SKILL.md +201 -0
- package/skills/grok-imagine/agentsam.skill.json +25 -0
- package/skills/grok-multiplayer-p2p/SKILL.md +111 -0
- package/skills/grok-multiplayer-p2p/agentsam.skill.json +25 -0
- package/skills/grok-multiplayer-p2p/references/react-binding.md +144 -0
- package/skills/grok-multiplayer-p2p/references/signaling-relay.md +269 -0
- package/skills/grok-neon/SKILL.md +157 -0
- package/skills/grok-neon/agentsam.skill.json +25 -0
- package/skills/grok-og/SKILL.md +102 -0
- package/skills/grok-og/agentsam.skill.json +25 -0
- package/skills/grok-og/references/brand-pass.md +53 -0
- package/skills/grok-og/references/custom-card.md +107 -0
- package/skills/grok-og/references/favicon-and-icons.md +48 -0
- package/skills/grok-og/references/og-type-contract.md +18 -0
- package/skills/grok-og/references/placeholder-card.md +21 -0
- package/skills/grok-og/references/x-banner.md +41 -0
- package/skills/grok-threejs/SKILL.md +67 -0
- package/skills/grok-threejs/agentsam.skill.json +25 -0
- package/skills/grok-threejs/references/llms-full.txt +2780 -0
- package/skills/grok-video2dsprite/LICENSE +21 -0
- package/skills/grok-video2dsprite/SKILL.md +236 -0
- package/skills/grok-video2dsprite/SOURCE.md +6 -0
- package/skills/grok-video2dsprite/agentsam.skill.json +25 -0
- package/skills/grok-video2dsprite/references/pipeline.md +90 -0
- package/skills/grok-video2dsprite/references/prompt-rules.md +63 -0
- package/skills/grok-video2dsprite/scripts/video2dsprite.py +490 -0
- package/skills/grok-xai-api/SKILL.md +152 -0
- package/skills/grok-xai-api/agentsam.skill.json +25 -0
- package/src/cli/command-catalog.js +46 -4
- package/src/cli/dispatch.js +1 -0
- package/src/cli.js +10 -0
- package/src/cloudflare/runtime-status.js +2 -1
- package/src/commands/autorag.js +67 -13
- package/src/commands/cheat-sheet.js +29 -4
- package/src/commands/codebaseindex.js +45 -2
- package/src/commands/knowledge.js +59 -2
- package/src/commands/machine-binary.js +103 -0
- package/src/commands/machine.js +137 -0
- package/src/commands/security.js +67 -4
- package/src/commands/setup.js +33 -16
- package/src/commands/status.js +8 -1
- package/src/features/resolve.js +1 -0
- package/src/knowledge/engine.js +21 -2
- package/src/knowledge/runtime-discovery.js +127 -0
- package/src/lib/identity-scaffold.js +53 -13
- package/src/lib/setup/recipes.js +1 -1
- package/src/models/discovery.js +40 -22
- package/src/models/inventory-core.d.ts +105 -0
- package/src/models/inventory-core.js +54 -6
- package/src/security/logs.js +3 -0
- package/src/ui/cli/help.js +41 -0
- package/test/apps-scaffold-contract.test.mjs +3 -2
- package/test/cli/api-key-env-whoami.test.mjs +2 -2
- package/test/cli/catalog-authority.test.mjs +64 -0
- package/test/cli/machine-command.test.mjs +34 -0
- package/test/fixtures/project-authority/customer-app/.agentsam/app.json +5 -0
- package/test/fixtures/project-authority/customer-app/apps/my-product/index.ts +1 -0
- package/test/fixtures/project-authority/customer-app/wrangler.toml +17 -0
- package/test/integration/autorag-project-authority.integration.test.mjs +51 -0
- package/test/integration/autorag-runtime-discovery.test.mjs +107 -0
- package/test/integration/cms-app-scaffold.test.mjs +37 -31
- package/test/integration/cms-site-tenancy-contract.test.mjs +28 -17
- package/test/integration/codebaseindex-ingest.test.mjs +41 -0
- package/test/integration/database-hosted-honesty.test.mjs +49 -0
- package/test/integration/desktop-keychain-boundary.test.mjs +45 -0
- package/test/integration/hosted-work-host-authority.test.mjs +47 -0
- package/test/integration/inventory-eligibility.test.mjs +59 -0
- package/test/integration/local-provider-bridge.test.mjs +65 -0
- package/test/integration/local-studio-settings-production.test.mjs +40 -0
- package/test/integration/lockfile-sync.test.mjs +26 -0
- package/test/integration/provider-env-cli.test.mjs +13 -3
- package/test/integration/runtime-status.integration.test.mjs +16 -1
- package/test/integration/theme-package-registry.test.mjs +31 -0
- package/test/repository-snapshot-view.test.mjs +2 -2
- package/test/security.test.mjs +14 -0
- package/test/terminal-machine-identity.test.mjs +2 -2
- package/apps/client-cms-editor/backend/src/demo-bootstrap.ts +0 -145
- package/packages/agentsam-nav/dist/Nav.d.ts +0 -43
- package/packages/agentsam-nav/dist/NavCollapsible.d.ts +0 -10
- package/packages/agentsam-nav/dist/NavContent.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavFooter.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavGroup.d.ts +0 -3
- package/packages/agentsam-nav/dist/NavHeader.d.ts +0 -2
- package/packages/agentsam-nav/dist/NavMenu.d.ts +0 -22
- package/packages/agentsam-nav/dist/NavProvider.d.ts +0 -35
- package/packages/agentsam-nav/dist/NavTrigger.d.ts +0 -2
- package/packages/agentsam-nav/dist/Surfaces.d.ts +0 -41
- package/packages/agentsam-nav/dist/browser.d.ts +0 -11
- package/packages/agentsam-nav/dist/browser.global.js +0 -132
- package/packages/agentsam-nav/dist/browser.js +0 -613
- package/packages/agentsam-nav/dist/contracts.d.ts +0 -76
- package/packages/agentsam-nav/dist/index.d.ts +0 -11
- package/packages/agentsam-nav/dist/index.js +0 -614
- package/packages/agentsam-nav/dist/legacy.css +0 -83
- package/packages/agentsam-nav/dist/shell.css +0 -113
- package/packages/agentsam-nav/dist/theme.css +0 -113
- package/packages/agentsam-nav/dist/utils.d.ts +0 -1
- package/packages/agentsam-repository/src/goap.js.bak-20260922011251 +0 -670
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Game AI & Pathfinding (A* on grids, steering, FSM, behavior trees, navmesh)
|
|
2
|
+
|
|
3
|
+
Consolidated from Red Blob Games (the canonical A*/grid reference), Craig Reynolds' steering work, and Game AI Pro / behavior-tree canon (see Sources). Focus: what an AI builder needs so enemies/agents **move sensibly and don't jitter, get stuck, or tank the frame rate.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Pathfinding: A* on a grid (the default)
|
|
8
|
+
|
|
9
|
+
**A\*** finds the shortest path by expanding the node that minimizes `f = g + h`, where `g` = cost so far and `h` = heuristic estimate to goal. It's Dijkstra + a goal-directed heuristic.
|
|
10
|
+
|
|
11
|
+
Rules:
|
|
12
|
+
- **Heuristic must be admissible** (never overestimate true cost) or A* may return a non-optimal path.
|
|
13
|
+
- **4-directional grid → Manhattan distance** `|dx|+|dy|`.
|
|
14
|
+
- **8-directional grid → octile/Chebyshev distance** (diagonal cost ≈ 1.414). Using Manhattan on an 8-dir grid overestimates → wrong paths; using Euclidean on a 4-dir grid underestimates → slow.
|
|
15
|
+
- **Use a binary heap / priority queue** for the open set. A linear scan of the open set is the #1 A* perf bug — turns O(E log V) into O(V²) and stutters on big maps.
|
|
16
|
+
- **Track a closed set / best-g per node** so you don't reprocess nodes; update `g` if a cheaper path to a node is found.
|
|
17
|
+
- **Weighted tiles:** give terrain costs (mud=5, road=1) via `g`; A* naturally routes around expensive terrain.
|
|
18
|
+
- **Diagonal corner-cutting:** forbid diagonal moves that clip through two wall corners, or agents visually cut through walls.
|
|
19
|
+
- **Reconstruct the path** by following `cameFrom` parents from goal back to start, then reverse.
|
|
20
|
+
|
|
21
|
+
Related tools (Red Blob Games):
|
|
22
|
+
- **Breadth-First Search / Dijkstra maps ("flow fields"):** when *many* agents chase *one* goal (tower defense, RTS), compute one BFS/Dijkstra field from the goal and have every agent follow the gradient — far cheaper than one A* per agent.
|
|
23
|
+
- **Greedy Best-First** is faster but not optimal; A* is the balanced default.
|
|
24
|
+
- **JPS (Jump Point Search)** optimizes A* on uniform-cost grids.
|
|
25
|
+
|
|
26
|
+
Libraries: **PathFinding.js** (grid A*/JPS/BFS, easy), or roll your own for control.
|
|
27
|
+
|
|
28
|
+
**Path-following gotchas:** smooth the raw grid path (string-pulling / line-of-sight simplification) so agents don't zig-zag along cell centers; recompute paths sparingly (on target move / periodically, not every frame); handle "no path exists" explicitly.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 2. Steering behaviors (smooth local movement)
|
|
33
|
+
|
|
34
|
+
A* gives *where* to go; **steering** (Craig Reynolds) makes agents *move there naturally* by accumulating steering forces on velocity:
|
|
35
|
+
- **Seek / Arrive** — move toward a target; Arrive decelerates within a slowing radius (prevents overshoot/orbit).
|
|
36
|
+
- **Flee / Evade** — away from a threat.
|
|
37
|
+
- **Pursuit** — seek the target's *predicted future* position.
|
|
38
|
+
- **Wander** — smooth random roaming (jitter a point on a circle ahead), not teleporting random targets.
|
|
39
|
+
- **Obstacle avoidance** — steer around obstacles detected by a look-ahead feeler.
|
|
40
|
+
- **Separation / Alignment / Cohesion** = **Flocking (boids)** for crowds/schools/swarms.
|
|
41
|
+
- **Path following** — follow a computed A* path smoothly rather than snapping cell-to-cell.
|
|
42
|
+
|
|
43
|
+
Rules: **combine forces** (weighted sum or priority), **clamp to max force and max speed**, apply with **delta time**. Steering handles the smooth "how"; A*/flow fields handle the strategic "where." Use both together — A* alone looks robotic; steering alone gets stuck on walls.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. Finite State Machines (FSM) — decision-making for simple agents
|
|
48
|
+
|
|
49
|
+
- Agent is in exactly one **state** (Idle, Patrol, Chase, Attack, Flee); **transitions** fire on conditions (saw player → Chase; lost player → Patrol; low HP → Flee).
|
|
50
|
+
- Each state has `enter()`, `update(dt)`, `exit()`. Keep transition logic centralized/table-driven.
|
|
51
|
+
- Great for enemies with a handful of behaviors. **Downside:** transitions explode combinatorially (n states → up to n² transitions) — becomes spaghetti past ~6–8 states.
|
|
52
|
+
- **Hierarchical FSMs** (states containing sub-states) tame some of that.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 4. Behavior Trees (BT) — scalable decision-making
|
|
57
|
+
|
|
58
|
+
When FSMs get unwieldy, use a **behavior tree**: a tree of nodes evaluated each tick, each returning **Success / Failure / Running**.
|
|
59
|
+
- **Composites:** **Sequence** (run children in order, fail-fast — "AND"), **Selector/Fallback** (try children until one succeeds — "OR"), **Parallel**.
|
|
60
|
+
- **Decorators:** Inverter, Repeat, Cooldown, Succeeder, condition guards.
|
|
61
|
+
- **Leaves:** Actions (MoveTo, Attack, Reload) and Conditions (IsPlayerVisible, HasAmmo).
|
|
62
|
+
- **`Running` is the key concept:** long actions (walking a path) return `Running` and resume next tick — don't block.
|
|
63
|
+
- Far more **modular/reusable/authorable** than FSMs; the industry standard for complex NPCs. Consider a lib (behaviortree.js) or a small hand-rolled implementation.
|
|
64
|
+
- **Utility AI / GOAP** are alternatives for emergent/goal-driven behavior at higher complexity.
|
|
65
|
+
|
|
66
|
+
**Rule:** simple enemies → FSM; complex/multi-goal NPCs → behavior tree. Don't build a BT framework for a single 3-state slime.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## 5. Navmesh basics (3D / open spaces)
|
|
71
|
+
|
|
72
|
+
- Grids are natural for tile games; for **open 3D/continuous** worlds, a **navigation mesh** (convex polygons covering walkable surfaces) is more efficient and gives smoother paths than a dense grid.
|
|
73
|
+
- Run A* over the polygon graph (portal edges), then **string-pull ("funnel algorithm")** through polygon portals for a smooth, natural path.
|
|
74
|
+
- In JS: **recast-navigation-js** (WASM port of industry-standard Recast/Detour) bakes navmeshes from level geometry and provides crowd/agent avoidance; three-pathfinding for a simpler Three.js navmesh follower.
|
|
75
|
+
- Navmeshes handle multi-level geometry (ramps, bridges) that flat grids can't; support off-mesh links (jumps, ladders).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 6. Performance & correctness rules
|
|
80
|
+
- **Don't A* every agent every frame.** Cache paths; recompute on target movement or a throttled interval; **time-slice** pathfinding across frames or use a **web worker** for big searches so the main thread doesn't hitch.
|
|
81
|
+
- **Flow field / Dijkstra map** when many agents share one goal — one computation for all.
|
|
82
|
+
- **Binary heap** open set, **typed arrays** for large grids, reuse buffers (no per-search allocation churn).
|
|
83
|
+
- **Run AI on a fixed timestep** (decoupled from render) for determinism/netcode; often at a lower rate than render (e.g. 10–20Hz for decisions) with steering interpolating between.
|
|
84
|
+
- **Stagger** AI updates across agents (LOD-AI): distant/off-screen agents think less often.
|
|
85
|
+
- Handle **dynamic obstacles**: mark blocked cells / re-path when the map changes; use local steering avoidance for moving agents.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 7. Bug-prevention checklist
|
|
90
|
+
- **Non-admissible heuristic** (Euclidean on 4-dir, Manhattan on 8-dir) → non-optimal or slow paths; match heuristic to movement.
|
|
91
|
+
- **Linear open-set scan** → severe stutter on big maps; use a binary heap.
|
|
92
|
+
- **A* per agent per frame** → frame drops; cache/throttle/flow-field/worker.
|
|
93
|
+
- **Following raw grid path** → robotic zig-zag; smooth/string-pull and add steering.
|
|
94
|
+
- **No Arrive/slowing radius** → agents overshoot and orbit the target.
|
|
95
|
+
- **Corner-cutting diagonals** → agents clip through wall corners; forbid unsafe diagonals.
|
|
96
|
+
- **FSM sprawl** → unmaintainable transitions; switch to a behavior tree.
|
|
97
|
+
- **Blocking actions in a BT** → tree stalls; return `Running` for long actions.
|
|
98
|
+
- **Ignoring "no path"** → crashes/agents freeze; handle unreachable goals explicitly.
|
|
99
|
+
- **Not re-pathing on map change** → agents walk into new walls; invalidate cached paths.
|
|
100
|
+
- **AI tied to render frame rate** → non-deterministic/uneven; fixed timestep, staggered.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Defaults to apply
|
|
105
|
+
- **Default AI stack:** **A\* on a grid** (binary-heap open set, movement-matched heuristic, weighted tiles) for "where," **steering behaviors** (seek/arrive/avoid/separation) for smooth "how," and an **FSM for simple enemies / behavior tree for complex NPCs** for "what to do."
|
|
106
|
+
- **Use flow fields (BFS/Dijkstra map) when many agents chase one target** (tower defense/RTS) — big perf win over per-agent A*.
|
|
107
|
+
- **For 3D/open worlds, generate navmeshes via recast-navigation-js**; smooth paths with the funnel algorithm.
|
|
108
|
+
- **Bake in the perf guardrails**: cache paths, throttle/time-slice/worker pathfinding, run AI on a fixed lower-rate tick with steering interpolation, and stagger distant agents. Always handle "no path found."
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Sources
|
|
113
|
+
- Red Blob Games — Introduction to A* (canonical, interactive): https://www.redblobgames.com/pathfinding/a-star/introduction.html ; A* Implementation Guide: https://www.redblobgames.com/pathfinding/a-star/implementation.html
|
|
114
|
+
- Red Blob Games — Grids/heuristics & "Pathfinding for Tower Defense" (flow fields): https://www.redblobgames.com/pathfinding/tower-defense/ ; grid pathfinding tricks: https://www.redblobgames.com/pathfinding/grids/algorithms.html
|
|
115
|
+
- Craig Reynolds — "Steering Behaviors For Autonomous Characters": https://www.red3d.com/cwr/steer/ ; Boids: https://www.red3d.com/cwr/boids/
|
|
116
|
+
- Amit Patel / Red Blob — heuristics guide: https://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html
|
|
117
|
+
- PathFinding.js (grid A*/JPS/BFS): https://github.com/qiao/PathFinding.js
|
|
118
|
+
- Jump Point Search (Harabor & Grastien): https://harablog.wordpress.com/2011/09/07/jump-point-search/
|
|
119
|
+
- Behavior Trees — Chris Simpson, "Behavior trees for AI: How they work": https://www.gamedeveloper.com/programming/behavior-trees-for-ai-how-they-work ; behaviortree.js: https://github.com/Calamari/BehaviorTree.js
|
|
120
|
+
- Game Programming Patterns — State (FSM): https://gameprogrammingpatterns.com/state.html
|
|
121
|
+
- Recast/Detour navmesh: https://github.com/recastnavigation/recastnavigation ; recast-navigation-js: https://github.com/isaac-mason/recast-navigation-js ; three-pathfinding: https://github.com/donmccurdy/three-pathfinding
|
|
122
|
+
- GOAP (Jeff Orkin, F.E.A.R.): https://alumni.media.mit.edu/~jorkin/goap.html
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Game Audio in the Browser (Web Audio API, mobile unlock, Howler.js, gain buses, spatial, latency)
|
|
2
|
+
|
|
3
|
+
Consolidated from MDN Web Audio docs and Howler.js docs (see Sources). Focus: what an AI builder needs so audio **actually plays on mobile, stays low-latency, and mixes cleanly** — the #1 audio bug in browser games is "no sound on iOS."
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. The autoplay unlock — the single most important rule
|
|
8
|
+
|
|
9
|
+
Browsers block audio until the user interacts with the page. An `AudioContext` created on page load starts in the **`"suspended"`** state and will silently play nothing until resumed.
|
|
10
|
+
|
|
11
|
+
**Rule: create/resume the AudioContext (or unlock your audio lib) from inside the FIRST real user gesture** (`click`/`touchend`/`keydown`), and call `resume()` **synchronously** in that handler's call stack (iOS Safari is strict — an `await` before `resume()` can break the gesture chain).
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
let audioCtx;
|
|
15
|
+
function unlockAudio() {
|
|
16
|
+
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
|
17
|
+
if (audioCtx.state === 'suspended') audioCtx.resume(); // call synchronously in the gesture
|
|
18
|
+
}
|
|
19
|
+
// Attach once; remove after first success. `pointerdown`/`touchend`/`keydown` all count.
|
|
20
|
+
window.addEventListener('pointerdown', unlockAudio, { once: true });
|
|
21
|
+
window.addEventListener('keydown', unlockAudio, { once: true });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- Show a "tap to start" / "click to play" screen so the first gesture reliably unlocks audio (and any pointer lock / fullscreen you need). Games should not expect audio before the player clicks in.
|
|
25
|
+
- Also **resume on `visibilitychange`/focus** — contexts can get re-suspended when a tab is backgrounded on mobile.
|
|
26
|
+
- Listen to the `statechange` event if you need to reflect mute/paused UI.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. Web Audio API vs `<audio>` vs Howler
|
|
31
|
+
|
|
32
|
+
- **Don't use `<audio>` / `Audio()` elements for game SFX.** They have high latency, limited simultaneous playback, and inconsistent mobile behavior. Fine for background music streaming only.
|
|
33
|
+
- **Web Audio API** is the correct foundation: decode once into an `AudioBuffer`, then fire many low-latency, overlapping `AudioBufferSourceNode`s. Gives you precise scheduling, gain buses, and spatialization.
|
|
34
|
+
- **Howler.js** is the recommended default library — it wraps Web Audio (with `<audio>` fallback), auto-handles the **mobile unlock**, sprite support, fades, spatial audio, and pooling. Use it unless you specifically need raw Web Audio control. (Note: `AudioBufferSourceNode` is one-shot — you create a new source per play; that's normal and cheap. Howler manages this for you.)
|
|
35
|
+
|
|
36
|
+
**Preload & decode up front:** `fetch → arrayBuffer → audioCtx.decodeAudioData` during a loading screen. Decoding on first play causes an audible hitch.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Gain buses (music / SFX / master) — do this from the start
|
|
41
|
+
|
|
42
|
+
Route everything through a small mixer graph so you can control category volumes and a master mute:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
source → (per-sound gain) → sfxBus ─┐
|
|
46
|
+
music → musicGain ────────────────┼→ masterGain → audioCtx.destination
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
const master = audioCtx.createGain();
|
|
51
|
+
const musicBus = audioCtx.createGain();
|
|
52
|
+
const sfxBus = audioCtx.createGain();
|
|
53
|
+
musicBus.connect(master); sfxBus.connect(master); master.connect(audioCtx.destination);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
- Set volumes with **`gain.setTargetAtTime(value, audioCtx.currentTime, 0.02)`** or `setValueAtTime`, **not** a raw `gain.value =` mid-play — abrupt jumps cause clicks/pops. Ramp fades over ~10–50ms.
|
|
57
|
+
- Perceived loudness is logarithmic: map a 0–1 slider to gain with a curve (e.g. `gain = slider²`) so sliders feel linear.
|
|
58
|
+
- Howler equivalent: per-sound `volume`, category via `Howler` groups / separate `Howl` instances, and `Howler.volume()` for master.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 4. Latency (keep audio tight)
|
|
63
|
+
|
|
64
|
+
- Prefer **Web Audio buffer sources** over `<audio>` for anything reactive — element playback latency ruins hit feedback.
|
|
65
|
+
- Optionally construct the context with **`{ latencyHint: 'interactive' }`** (default) for lowest latency; use `'playback'` only for pure music.
|
|
66
|
+
- **Reuse decoded buffers**; never `decodeAudioData` in the gameplay hot path.
|
|
67
|
+
- Schedule to `audioCtx.currentTime` for sample-accurate timing (e.g. rhythm games): `source.start(when)`. Don't rely on `setTimeout` for musical timing — use the Web Audio clock ("A Tale of Two Clocks").
|
|
68
|
+
- Avoid creating hundreds of nodes per frame; cap concurrent voices and stop/disconnect finished sources (set `source.onended` to disconnect) to avoid graph buildup.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 5. Variation & layering (feel)
|
|
73
|
+
|
|
74
|
+
- **Pitch-randomize** repeated SFX so footsteps/gunfire don't sound robotic: set `source.playbackRate.value = 1 + (Math.random()*2-1)*0.1` (Howler: `rate`). Randomize volume slightly too.
|
|
75
|
+
- **Layer** a sound from multiple samples (thump + body + transient) — see `game-feel-juice.md`.
|
|
76
|
+
- **Round-robin / random pick** from a few variants of the same sound.
|
|
77
|
+
- Use **audio sprites** (one file, offset+duration segments) to cut HTTP requests and decode overhead — Howler supports `sprite` natively.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 6. Spatial / positional audio (for 3D or top-down games)
|
|
82
|
+
|
|
83
|
+
- **`PannerNode`** positions a source in 3D relative to `audioCtx.listener`. Connect `source → panner → bus`. Update `panner.positionX/Y/Z` and `listener.positionX/Y/Z` (+ listener `forward`/`up`) each frame to match camera/player.
|
|
84
|
+
- `panningModel`: `'HRTF'` (realistic, headphone-friendly, costlier) or `'equalpower'` (cheap stereo). `distanceModel`: `'inverse'`/`'linear'`/`'exponential'` with `refDistance`, `maxDistance`, `rolloffFactor` for falloff.
|
|
85
|
+
- For simple **2D stereo pan** (left/right by screen x), a `StereoPannerNode` (pan −1..1) is cheaper and sufficient — don't reach for HRTF in a 2D game.
|
|
86
|
+
- Howler exposes `pos()`, `orientation()`, and `pannerAttr()` for this.
|
|
87
|
+
- Update positions in the render loop; set the position AudioParams via `.value` (or `setValueAtTime`) to avoid zipper noise on fast movement.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 7. Formats & assets
|
|
92
|
+
- Use **compressed formats**: `.webm`/`.ogg` (Opus/Vorbis) with an `.mp3`/`.m4a` fallback for Safari. Howler takes an array of sources and picks a supported one.
|
|
93
|
+
- Keep SFX short and mono (mono halves size and works with panners); keep music stereo but compressed.
|
|
94
|
+
- Watch total download; long music tracks can stream via `html5: true` in Howler (uses `<audio>`, saves memory, slightly higher latency — fine for music).
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 8. Bug-prevention checklist
|
|
99
|
+
- **No sound on mobile/iOS** → didn't resume `AudioContext` synchronously inside a user gesture; add a tap-to-start unlock.
|
|
100
|
+
- **Silence after backgrounding tab** → context re-suspended; resume on `visibilitychange`/focus.
|
|
101
|
+
- **Clicks/pops on volume change or start/stop** → setting `gain.value` abruptly; ramp with `setTargetAtTime` and fade in/out a few ms.
|
|
102
|
+
- **Laggy hit sounds** → using `<audio>` elements; use Web Audio buffer sources.
|
|
103
|
+
- **Hitch on first play of a sound** → decoding at play time; preload + `decodeAudioData` during loading.
|
|
104
|
+
- **Robotic repeated sounds** → no pitch/volume variation; randomize `playbackRate`.
|
|
105
|
+
- **Growing memory / stuck voices** → not disconnecting finished sources or capping concurrent voices.
|
|
106
|
+
- **Works in Chrome, silent in Safari** → format not supported (provide ogg/opus **and** mp3/m4a) or async break before `resume()`.
|
|
107
|
+
- **No volume controls** → always expose master/music/sfx sliders + mute (accessibility + player expectation).
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Defaults to apply
|
|
112
|
+
- **Default to Howler.js** for generated games (handles unlock, sprites, fades, spatial, pooling) — fall back to raw Web Audio only when we need custom DSP/scheduling.
|
|
113
|
+
- **Always wire the mixer graph**: master + music + sfx gain buses with sliders and a mute, from the first commit. Map sliders through a `x²` curve.
|
|
114
|
+
- **Always add a "tap to start" gate** that unlocks audio (and pointer lock/fullscreen) on the first gesture, and re-resume on visibility change — kills the #1 "no audio on mobile" bug.
|
|
115
|
+
- **Preload + decode on the loading screen**, reuse buffers, pitch-randomize repeated SFX, ramp all gain changes. Provide ogg/opus + mp3 fallbacks.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Sources
|
|
120
|
+
- MDN — Web Audio API Best Practices (autoplay, gesture, gain, controls): https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Best_practices
|
|
121
|
+
- MDN — `AudioContext.resume()`: https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/resume
|
|
122
|
+
- MDN — `BaseAudioContext.decodeAudioData()`: https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/decodeAudioData
|
|
123
|
+
- MDN — `GainNode` / `AudioParam.setTargetAtTime`: https://developer.mozilla.org/en-US/docs/Web/API/GainNode , https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setTargetAtTime
|
|
124
|
+
- MDN — `PannerNode` + Web audio spatialization basics: https://developer.mozilla.org/en-US/docs/Web/API/PannerNode , https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API/Web_audio_spatialization_basics
|
|
125
|
+
- MDN — `StereoPannerNode`: https://developer.mozilla.org/en-US/docs/Web/API/StereoPannerNode
|
|
126
|
+
- MDN — Autoplay guide for media & Web Audio: https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide
|
|
127
|
+
- Chris Wilson — "A Tale of Two Clocks" (Web Audio scheduling): https://web.dev/articles/audio-scheduling
|
|
128
|
+
- Howler.js — docs & repo: https://howlerjs.com/ , https://github.com/goldfire/howler.js
|
|
129
|
+
- Chrome autoplay policy for Web Audio: https://developer.chrome.com/blog/autoplay/#webaudio
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Babylon.js — Deep Engine Guide (Havok physics, cameras, SceneOptimizer, WebGPU, assets, GUI, freezing)
|
|
2
|
+
|
|
3
|
+
Babylon.js is the "batteries-included" 3D engine: built-in physics, inspector, GUI, node materials, camera behaviors, SceneOptimizer, and a first-class WebGPU backend. Use it when you want an integrated engine rather than assembling Three.js + libraries.
|
|
4
|
+
|
|
5
|
+
This file assumes the general loop/orientation/perf rules and the short Babylon perf bullets in `threejs-foundational.md` and goes **much deeper** without repeating them.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Engine setup & the render loop
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import { Engine, Scene, Vector3 } from "@babylonjs/core";
|
|
13
|
+
const engine = new Engine(canvas, true, { preserveDrawingBuffer: false, stencil: true, powerPreference: "high-performance" });
|
|
14
|
+
const scene = new Scene(engine);
|
|
15
|
+
engine.runRenderLoop(() => scene.render());
|
|
16
|
+
window.addEventListener("resize", () => engine.resize());
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `engine.runRenderLoop` is Babylon's RAF loop; hook game logic into **`scene.onBeforeRenderObservable`** and read delta with **`engine.getDeltaTime()`** (ms) or `scene.getAnimationRatio()` (frame-rate compensation factor). Scale movement by delta as always.
|
|
20
|
+
- Use **`scene.onBeforeRenderObservable.add(fn)`** for per-frame logic instead of stuffing everything in the render loop closure — it's observable, disposable, and composable.
|
|
21
|
+
- `engine.setHardwareScalingLevel(n)` renders at 1/n resolution (n>1 = lower res, big mobile win; n<1 = supersample).
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 2. WebGPU (real, production-ready — prefer it with fallback)
|
|
26
|
+
|
|
27
|
+
Babylon has a mature **`WebGPUEngine`** that dramatically cuts CPU-side draw-call overhead and enables compute shaders.
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
import { WebGPUEngine, Engine } from "@babylonjs/core";
|
|
31
|
+
async function createEngine(canvas) {
|
|
32
|
+
if (await WebGPUEngine.IsSupportedAsync) {
|
|
33
|
+
const e = new WebGPUEngine(canvas, { antialias: true });
|
|
34
|
+
await e.initAsync(); // REQUIRED before creating a Scene
|
|
35
|
+
return e;
|
|
36
|
+
}
|
|
37
|
+
return new Engine(canvas, true, { powerPreference: "high-performance" }); // WebGL2 fallback
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `WebGPUEngine.initAsync()` **must be awaited** before you create the scene — a top setup bug.
|
|
42
|
+
- **Snapshot rendering** (`engine.snapshotRendering = true`, WebGPU only) records the command stream for a static scene and replays it, slashing CPU cost for scenes whose draw list doesn't change. Set `snapshotRenderingMode` and refresh when the scene structure changes.
|
|
43
|
+
- WebGPU supports **compute shaders** (particles, culling, GPU picking) — not available on WebGL.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 3. Cameras & Camera Behaviors (free polish)
|
|
48
|
+
|
|
49
|
+
- **ArcRotateCamera** — orbit/inspection. Set limits and inertia so it feels good: `lowerRadiusLimit`/`upperRadiusLimit`, `lowerBetaLimit`/`upperBetaLimit`, `wheelPrecision`, `panningSensibility`, `inertia`. `camera.attachControl(canvas, true)`.
|
|
50
|
+
- **UniversalCamera** — FPS/free camera (WASD + mouse; combines FreeCamera + touch/gamepad). `camera.applyGravity`, `camera.checkCollisions`, `camera.ellipsoid` for simple capsule collision against meshes with `mesh.checkCollisions = true` (built-in, no physics engine needed).
|
|
51
|
+
- **FollowCamera** — third-person chase; set `radius`, `heightOffset`, `rotationOffset`, `cameraAcceleration`, `maxCameraSpeed`.
|
|
52
|
+
- **Keep `camera.maxZ` as small as practical** and `minZ` as large as practical — improves depth precision, culling, and overdraw.
|
|
53
|
+
|
|
54
|
+
**Behaviors** attach polished motion for free (this is a Babylon differentiator):
|
|
55
|
+
- **FramingBehavior** — auto-frames a target mesh nicely (`camera.useFramingBehavior = true`).
|
|
56
|
+
- **BouncingBehavior** — soft bounce at radius limits.
|
|
57
|
+
- **AutoRotationBehavior** — idle turntable spin (great for product/menu scenes).
|
|
58
|
+
Enable via `camera.useAutoRotationBehavior = true` etc., then tune the behavior object.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 4. Physics — Havok (V2 plugin, the current default)
|
|
63
|
+
|
|
64
|
+
Havok is the recommended physics engine (WASM, fast, deterministic-ish). Use the **Physics V2** API (`PhysicsAggregate` / `PhysicsBody` / `PhysicsShape`).
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import { HavokPlugin, PhysicsAggregate, PhysicsShapeType } from "@babylonjs/core";
|
|
68
|
+
import HavokPhysics from "@babylonjs/havok";
|
|
69
|
+
|
|
70
|
+
const havok = await HavokPhysics(); // MUST await — loads the .wasm
|
|
71
|
+
scene.enablePhysics(new Vector3(0, -9.81, 0), new HavokPlugin(true, havok));
|
|
72
|
+
|
|
73
|
+
// dynamic body:
|
|
74
|
+
new PhysicsAggregate(sphere, PhysicsShapeType.SPHERE, { mass: 1, restitution: 0.6, friction: 0.5 }, scene);
|
|
75
|
+
// static body: mass 0
|
|
76
|
+
new PhysicsAggregate(ground, PhysicsShapeType.BOX, { mass: 0 }, scene);
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- **`await HavokPhysics()` is mandatory** and the #1 init error. First `HavokPlugin` arg (`_useDeltaForWorldStep`, usually `true`) makes physics use engine delta for smoother variable framerate.
|
|
80
|
+
- **Bundler gotcha (Vite/webpack):** the `HavokPhysics.wasm` file must be served. Use `await HavokPhysics({ locateFile: f => \`/assets/${f}\` })` or configure the bundler to copy the wasm.
|
|
81
|
+
- **PhysicsAggregate** is the easy path: it builds the matching `PhysicsShape` + `PhysicsBody` automatically. For fine control (compound shapes, sharing shapes across many bodies) build `PhysicsBody` + `PhysicsShape` yourself.
|
|
82
|
+
- Read/write motion: `aggregate.body.setLinearVelocity(v)`, `applyImpulse`, `setMassProperties`, `body.setMotionType(PhysicsMotionType.ANIMATED/DYNAMIC/STATIC)`.
|
|
83
|
+
- Collisions/triggers: `body.setCollisionCallbackEnabled(true)` + `body.getCollisionObservable().add(...)`; use `PhysicsShapeType.CONTAINER`/mesh shapes sparingly (mesh colliders are expensive — prefer primitives or convex hulls).
|
|
84
|
+
- **glTF imports:** apply physics to the actual mesh, not the `__root__` transform node — often re-parent or use the child mesh; otherwise transforms get double-applied.
|
|
85
|
+
- Debug: `new BABYLON.Debug.PhysicsViewer(scene).showBody(aggregate.body)`.
|
|
86
|
+
- Older engines (Cannon, Ammo, Oimo) still exist via the V1 plugin, but **Havok V2 is the recommended choice** for new games. Ammo remains an option if you need its specific features/soft bodies.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 5. Asset loading — containers & glTF (do it right for level switching)
|
|
91
|
+
|
|
92
|
+
- **glTF/GLB is the format.** `import "@babylonjs/loaders";` then `await ImportMeshAsync("model.glb", scene)` (or `SceneLoader.ImportMeshAsync` / `AppendSceneAsync`). Use Draco + KTX2/Basis compression.
|
|
93
|
+
- **AssetContainer is the key tool for reusable/levels:** `LoadAssetContainerAsync(url, scene)` loads meshes/materials/textures **without adding them to the scene**. Then `container.addAllToScene()` / `container.removeAllFromScene()` and, crucially, **`container.dispose()`** to fully free GPU resources when unloading a level. This gives clean load/unload cycles and lets you **instantiate** copies:
|
|
94
|
+
- `container.instantiateModelsToScene(name => name, cloneMaterials)` clones a loaded model (with skeletons/animations) cheaply for spawning many enemies from one load.
|
|
95
|
+
- **AssetsManager** gives progress-tracked batch loading with tasks (mesh, texture, binary) and `onProgress`/`onFinish` — good for a loading screen.
|
|
96
|
+
- **Babylon does not GC GPU resources** any more than Three.js does — dispose meshes/materials/textures or (better) dispose the AssetContainer when leaving a level.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 6. Materials & Node Material Editor
|
|
101
|
+
|
|
102
|
+
- **PBRMaterial** for realistic; **StandardMaterial** for simpler/cheaper. Reuse material instances across meshes (shared material = fewer state changes).
|
|
103
|
+
- **Node Material** — visual shader graph (Node Material Editor at nme.babylonjs.com). Export as JSON and load with `NodeMaterial.ParseFromSnippetAsync("snippetId", scene)` or from file. Great for custom effects (dissolve, water, force-fields) without writing GLSL/WGSL by hand, and it compiles to both WebGL and WebGPU.
|
|
104
|
+
- **Material plugins** and `material.freeze()` (see freezing below) reduce per-frame shader/uniform recompute.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 7. GUI (built-in — no DOM juggling)
|
|
109
|
+
|
|
110
|
+
`import { AdvancedDynamicTexture, Button, ... } from "@babylonjs/gui";`
|
|
111
|
+
|
|
112
|
+
- **Fullscreen UI:** `AdvancedDynamicTexture.CreateFullscreenUI("ui")` — a 2D overlay in screen space for HUD/menus (buttons, sliders, text, stack panels, grids). Resolution-independent, handles pointer input, works on mobile.
|
|
113
|
+
- **In-world UI:** apply an ADT to a mesh's material for diegetic screens/labels; use `linkWithMesh` to attach floating labels/health bars that track 3D objects.
|
|
114
|
+
- Prefer Babylon GUI over hand-rolled DOM overlays for game HUDs — it integrates with the scene, scales with the canvas, and doesn't fight CSS.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 8. Performance — FREEZING is the Babylon superpower
|
|
119
|
+
|
|
120
|
+
For anything static, tell Babylon to stop recomputing it. This is the highest-leverage Babylon optimization and under-used in generated code.
|
|
121
|
+
|
|
122
|
+
- **`mesh.freezeWorldMatrix()`** — mesh never moves ⇒ skip world-matrix recompute each frame. Call `unfreezeWorldMatrix()` if it needs to move again. Also set `mesh.doNotSyncBoundingInfo = true` for static meshes.
|
|
123
|
+
- **`material.freeze()`** — material params fixed ⇒ skip uniform/shader re-evaluation. `unfreeze()` to change it.
|
|
124
|
+
- **`scene.freezeActiveMeshes()`** — locks the active-mesh (culling) list; use when the set of visible meshes doesn't change (e.g., fixed camera or after setup). `unfreezeActiveMeshes()` when it changes. Huge CPU savings on large static scenes.
|
|
125
|
+
- **`scene.blockMaterialDirtyMechanism = true`** while bulk-creating materials to avoid repeated recompiles.
|
|
126
|
+
|
|
127
|
+
Other big levers (deeper than the three.js file's bullets):
|
|
128
|
+
- **`scene.performancePriority = ScenePerformancePriority.Intermediate` (or `Aggressive`)** — auto-applies a bundle of optimizations (freezes active meshes, skips some checks). `Aggressive` disables per-mesh picking/some features — verify nothing you need breaks.
|
|
129
|
+
- **Thin instances** (`mesh.thinInstanceAdd(matrix)` / `thinInstanceSetBuffer`) for thousands of identical static objects → one draw call, cheaper than regular instances. Regular **instances** (`mesh.createInstance()`) when you need per-instance picking/parenting.
|
|
130
|
+
- **`scene.autoClear = false`** (and `autoClearDepthAndStencil`) when the scene fully covers the viewport — skips the clear.
|
|
131
|
+
- **`scene.skipPointerMovePicking = true`** if you don't need hover/move picking (picking on every pointer move is a silent CPU cost). Set `mesh.isPickable = false` on non-interactive meshes.
|
|
132
|
+
- **`scene.skipFrustumClipping`**, `mesh.alwaysSelectAsActiveMesh` — micro-tune culling for known-visible meshes.
|
|
133
|
+
- **Merge meshes** (`Mesh.MergeMeshes([...], true, true, undefined, false, true)`) for static geometry sharing a material.
|
|
134
|
+
- **LOD:** `mesh.addLODLevel(distance, lowMesh)` and `addLODLevel(farDist, null)` to cull entirely far away.
|
|
135
|
+
- Keep **`camera.maxZ` low**; limit lights (each real-time light multiplies shader cost); use **shadow generators sparingly** with small map sizes and `RefreshRate` for static lights.
|
|
136
|
+
- Avoid per-frame allocations: use **`TmpVectors.Vector3[0]`** scratch vectors and the `...ToRef()` method variants (`addToRef`, `scaleToRef`) instead of returning new objects.
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 9. SceneOptimizer — adaptive quality to hit a target FPS
|
|
141
|
+
|
|
142
|
+
Instead of guessing settings per device, let Babylon degrade quality until it reaches a target framerate.
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
import { SceneOptimizer, SceneOptimizerOptions } from "@babylonjs/core";
|
|
146
|
+
// preset tiers try progressively harder optimizations to reach the target FPS:
|
|
147
|
+
SceneOptimizer.OptimizeAsync(scene,
|
|
148
|
+
SceneOptimizerOptions.HighDegradationAllowed(60), // target 60 fps
|
|
149
|
+
() => console.log("reached target"),
|
|
150
|
+
() => console.log("could not reach target"));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- Presets: `LowDegradationAllowed`, `ModerateDegradationAllowed`, `HighDegradationAllowed(targetFps)`. They apply, in escalating order, optimizations like: reduce hardware scaling, merge meshes, disable shadows, reduce texture size, disable post-processes, cut particle counts.
|
|
154
|
+
- You can build a custom `SceneOptimizerOptions` with specific `SceneOptimization` steps and priorities. Great for shipping one build that adapts from low-end mobile to desktop automatically.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 10. Inspector & profiling
|
|
159
|
+
|
|
160
|
+
- **`import "@babylonjs/inspector"; scene.debugLayer.show()`** — the in-browser inspector: scene tree, per-mesh stats, materials, textures, and a **Statistics/Performance** tab (draw calls, active meshes, frame breakdown, GPU/CPU time). This is the fastest way to find what's slow.
|
|
161
|
+
- Watch **draw calls** and **active meshes** counts; use the profiler to see whether you're CPU-bound (draw calls, matrix updates → freeze/instance) or GPU-bound (overdraw, shader cost, texture size → LOD/compression/fewer lights).
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 11. Common Babylon pitfalls (checklist)
|
|
166
|
+
|
|
167
|
+
- Forgetting `await WebGPUEngine.initAsync()` / `await HavokPhysics()` → cryptic init failures.
|
|
168
|
+
- Havok `.wasm` not served by the bundler → physics silently absent; use `locateFile` / copy the wasm.
|
|
169
|
+
- Not disposing meshes/materials/textures or the AssetContainer on level switch → GPU memory leak.
|
|
170
|
+
- Applying physics to a glTF `__root__` node → double transforms / wrong positions.
|
|
171
|
+
- Static scene running full cost because nothing is frozen → freeze world matrices, materials, active meshes.
|
|
172
|
+
- Picking on every pointer move (`skipPointerMovePicking` not set; meshes left `isPickable`) → CPU drain.
|
|
173
|
+
- Too many real-time lights / large shadow maps → shader + fill cost explosion.
|
|
174
|
+
- Per-frame `new Vector3()`/`Matrix` allocations → GC churn; use `TmpVectors` + `...ToRef()`.
|
|
175
|
+
- Huge `camera.maxZ` → z-fighting and wasted culling range.
|
|
176
|
+
- Mesh colliders for everything instead of primitive/convex shapes → physics perf collapse.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Defaults to apply
|
|
181
|
+
|
|
182
|
+
- **Reach for Babylon when the game wants an integrated engine** (built-in physics, GUI, inspector, camera behaviors) rather than the assemble-it-yourself Three.js route.
|
|
183
|
+
- **Always await `initAsync()` (WebGPU) and `HavokPhysics()`**, and wire the **WebGPU-with-WebGL2-fallback** pattern by default.
|
|
184
|
+
- **Emit freezing by default for static content**: `freezeWorldMatrix()` on non-moving meshes, `material.freeze()` on static materials, and `scene.freezeActiveMeshes()` / `performancePriority` once the scene is set up. This is the single biggest Babylon win most generated code misses.
|
|
185
|
+
- **Ship `SceneOptimizer.OptimizeAsync(scene, HighDegradationAllowed(60))`** so one build adapts from low-end mobile to desktop instead of hardcoding quality.
|
|
186
|
+
- **Use AssetContainers** for level load/unload (`instantiateModelsToScene` to spawn many from one load; `dispose()` to free on unload) to avoid the memory leaks that plague generated 3D games.
|
|
187
|
+
- **Use Babylon GUI** (fullscreen ADT) for HUDs/menus instead of DOM overlays; use **camera Behaviors** (Framing/AutoRotation) for free menu/product polish.
|
|
188
|
+
- Set **`skipPointerMovePicking`, low `maxZ`, `isPickable=false`** on non-interactive meshes as defaults.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Sources
|
|
193
|
+
- Babylon.js docs — Optimizing your scene (freeze APIs, autoClear, performancePriority, active meshes): https://doc.babylonjs.com/features/featuresDeepDive/scene/optimize_your_scene
|
|
194
|
+
- Babylon.js docs — Physics V2 / Havok (`enablePhysics`, `PhysicsAggregate`, `PhysicsBody/Shape`): https://doc.babylonjs.com/features/featuresDeepDive/physics/usingPhysicsEngine and https://github.com/BabylonJS/Documentation/blob/master/content/features/featuresDeepDive/physics/v2/usingPhysicsEngine.md
|
|
195
|
+
- `@babylonjs/havok` npm (init pattern, `await HavokPhysics()`, `locateFile`): https://www.npmjs.com/package/@babylonjs/havok
|
|
196
|
+
- Babylon.js docs — WebGPU (`WebGPUEngine`, `initAsync`, snapshot rendering): https://doc.babylonjs.com/setup/support/webGPU
|
|
197
|
+
- Babylon.js docs — Cameras & Camera Behaviors: https://doc.babylonjs.com/features/featuresDeepDive/cameras/camera_introduction and https://doc.babylonjs.com/features/featuresDeepDive/behaviors/cameraBehaviors
|
|
198
|
+
- Babylon.js docs — Asset Containers & glTF loading: https://doc.babylonjs.com/features/featuresDeepDive/importers/assetContainers and https://doc.babylonjs.com/features/featuresDeepDive/importers/loadingFileTypes
|
|
199
|
+
- Babylon.js docs — SceneOptimizer: https://doc.babylonjs.com/features/featuresDeepDive/scene/sceneOptimizer
|
|
200
|
+
- Babylon.js docs — GUI: https://doc.babylonjs.com/features/featuresDeepDive/gui/gui ; Node Material: https://doc.babylonjs.com/features/featuresDeepDive/materials/node_material/nodeMaterial
|
|
201
|
+
- Babylon.js docs — Inspector: https://doc.babylonjs.com/toolsAndResources/inspector
|
|
202
|
+
- Babylon.js forum — optimization best practices thread: https://forum.babylonjs.com/t/best-practices-for-optimizing-babylon-js-scenes-not-just-on-lower-end-devices/58688
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Collision & Physics for Browser Games (2D AABB/SAT/broadphase, swept collision, 3D Rapier/cannon, fixed timestep)
|
|
2
|
+
|
|
3
|
+
Consolidated from MDN game collision docs, Gaffer On Games, Rapier/cannon-es docs, and the SAT/swept-AABB canon (see Sources). Focus: what an AI builder needs so collisions are **correct and stable** — the classic bugs are tunneling through walls at speed, jitter, sinking into the ground, and O(n²) slowdowns.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Do you even need a physics engine?
|
|
8
|
+
|
|
9
|
+
- **Simple arcade games** (platformers, top-down, breakout, shooters) → hand-rolled **AABB** collision + resolution is often better: fully deterministic, no dependency, and you control feel. Most 2D games don't need Box2D/Matter.
|
|
10
|
+
- **Rigid-body simulation** (stacking, ragdolls, joints, realistic bouncing, vehicles) → use an engine: **2D → Matter.js / Planck.js (Box2D) / Rapier2D**; **3D → Rapier (rapier.js, WASM, fast, favored) / cannon-es / Ammo.js**.
|
|
11
|
+
- **Rule:** don't pull in a full rigid-body engine for a game that just needs "does the player box overlap the wall box." Physics engines add non-determinism and tuning overhead. Match the tool to the game.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 2. 2D collision detection primitives
|
|
16
|
+
|
|
17
|
+
- **AABB (axis-aligned bounding box)** — the workhorse. Two boxes overlap iff they overlap on **both** axes:
|
|
18
|
+
```js
|
|
19
|
+
const hit = a.x < b.x+b.w && a.x+a.w > b.x && a.y < b.y+b.h && a.y+a.h > b.y;
|
|
20
|
+
```
|
|
21
|
+
Cheap, ideal for tile maps, most 2D gameplay. Only valid for **non-rotated** boxes.
|
|
22
|
+
- **Circle/distance** — for round objects: compare squared distance to summed radii (`dx*dx+dy*dy < (r1+r2)²` — avoid `sqrt`).
|
|
23
|
+
- **SAT (Separating Axis Theorem)** — for **rotated boxes / convex polygons**: two convex shapes don't intersect iff there exists an axis (a face normal of either shape) on which their projections don't overlap. Test all face normals; if any gap exists → no collision. The **minimum overlap axis gives the collision normal + penetration depth** (the MTV, minimum translation vector) for resolution.
|
|
24
|
+
- SAT only works on **convex** shapes — decompose concave shapes into convex pieces.
|
|
25
|
+
- For circles vs polygon, also test the axis from circle center to nearest vertex.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 3. Broadphase: don't test every pair (O(n²) killer)
|
|
30
|
+
|
|
31
|
+
Checking all pairs is O(n²) — fine for dozens, catastrophic for hundreds/thousands. Split into **broadphase** (cheap culling of pairs that can't touch) → **narrowphase** (exact AABB/SAT on survivors).
|
|
32
|
+
|
|
33
|
+
- **Uniform spatial hash grid** — bucket objects into cells by position; only test objects sharing a cell. Best default for many similar-sized, evenly-distributed objects (bullets, particles, .io games). Cell size ≈ average object size (2–3×).
|
|
34
|
+
- **Quadtree** — recursively subdivide space; good for **non-uniform / clustered / varied-size** objects. Rebuild or update each frame.
|
|
35
|
+
- **Sort and sweep (sweep-and-prune)** — sort AABB endpoints on an axis; good when objects move coherently.
|
|
36
|
+
- **Rule:** the moment you have >~100 dynamic colliders, add a broadphase. Physics engines do this internally; hand-rolled collision needs it explicitly.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 4. Tunneling & swept collision (the top correctness bug)
|
|
41
|
+
|
|
42
|
+
**Tunneling:** a fast object moves so far in one frame that it passes *through* a thin wall without ever overlapping it on any frame. Discrete AABB checks miss it entirely.
|
|
43
|
+
|
|
44
|
+
Fixes (in order of preference):
|
|
45
|
+
- **Swept collision (continuous / CCD):** instead of testing the box at its new position, test the **motion path**. **Swept AABB** computes the fraction of the frame `t (0..1)` at which the moving box first touches the target, then moves the object to exactly that contact point and resolves — no overlap ever occurs. Essential for bullets, fast platformers, thin walls.
|
|
46
|
+
- **Substepping / smaller fixed timestep:** break a big move into several small steps and test each. Simpler than swept, good enough for moderate speeds.
|
|
47
|
+
- **Raycast** fast/small projectiles (bullets) instead of moving a body — cast a ray along the travel and hit the first collider.
|
|
48
|
+
- **Minimum wall thickness** ≥ max per-step travel is a cheap band-aid but fragile.
|
|
49
|
+
- In engines, **enable CCD** on fast bodies (Rapier `setCcdEnabled(true)`, cannon-es CCD options).
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 5. Collision *resolution* (not just detection)
|
|
54
|
+
|
|
55
|
+
- **AABB resolution:** compute penetration on each axis; push the object out along the **axis of least penetration** (this makes walls/floors behave). Zero the velocity component on the axis you resolved (hitting the floor kills downward velocity).
|
|
56
|
+
- **Resolve axes separately** for platformers ("move X, resolve X, then move Y, resolve Y") — prevents catching on tile seams and gives clean wall-slide behavior.
|
|
57
|
+
- **Ground detection:** you're grounded if resolving downward this frame; set a small `coyoteTime` (see input skill) for feel.
|
|
58
|
+
- **Avoid sinking/jitter:** apply a tiny "skin"/epsilon and don't over-correct; with engines, tune restitution/friction and let the solver settle. Resting jitter usually means variable timestep or fighting corrections.
|
|
59
|
+
- With **SAT**, resolve along the MTV (normal × penetration depth); reflect/scale velocity along the normal for bounces.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 6. Fixed timestep — the stability foundation (tie-in)
|
|
64
|
+
|
|
65
|
+
**Physics MUST run on a fixed timestep with an accumulator** ("Fix Your Timestep!", Gaffer On Games). Variable `dt` makes collision/physics non-deterministic and unstable (tunneling worsens, springs explode, replays/netcode desync).
|
|
66
|
+
```js
|
|
67
|
+
const STEP = 1/60; let acc = 0;
|
|
68
|
+
function frame(dt){ acc += Math.min(dt, 0.25); // clamp to avoid spiral-of-death after tab-out
|
|
69
|
+
while (acc >= STEP){ physicsStep(STEP); acc -= STEP; }
|
|
70
|
+
const alpha = acc / STEP; render(alpha); // interpolate between last two states
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
- **Clamp accumulated dt** (e.g. ≤0.25s) so a backgrounded tab doesn't trigger a "spiral of death" of catch-up steps.
|
|
74
|
+
- **Interpolate rendering** between the previous and current physics state using `alpha` for smooth visuals at any display rate.
|
|
75
|
+
- All physics engines expect a fixed step — call `world.step()` at a fixed rate, not per rAF with variable dt. Rapier is deterministic given fixed steps + same inputs (good for netcode).
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 7. 3D character controllers
|
|
80
|
+
|
|
81
|
+
Falling capsules from raw rigid-body dynamics feel bad for players — use a **kinematic character controller**:
|
|
82
|
+
- **Rapier `KinematicCharacterController`** (favored): create via `world.createCharacterController(offset)`, use a kinematic body + capsule/collider, then each fixed step call `computeColliderMovement(collider, desiredTranslation)` and read `computedMovement()` to get the collision-corrected move. Built-in **autostep** (stairs), **snap-to-ground** (don't float off slopes/ramps), **max slope climb angle**, and slide-along-walls. Set gravity/jump yourself (kinematic = you control motion).
|
|
83
|
+
- **cannon-es**: `PointerLockControlsCannon` example / a sphere or capsule body; more manual.
|
|
84
|
+
- Rules: use a **capsule** (not a box) so the player slides over small steps and around corners; apply movement each **fixed step**; do ground checks via the controller's grounded flag or a short downward ray; keep the visual mesh synced to the body each frame (with interpolation).
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 8. Bug-prevention checklist
|
|
89
|
+
- **Variable timestep for physics** → non-deterministic, jitter, worse tunneling, netcode desync; fixed step + accumulator.
|
|
90
|
+
- **No dt clamp** → "spiral of death" after tab-out; clamp accumulated dt.
|
|
91
|
+
- **Fast objects passing through walls** → tunneling; use swept AABB / CCD / raycast / substeps.
|
|
92
|
+
- **SAT on concave shapes** → wrong results; decompose into convex pieces.
|
|
93
|
+
- **AABB test on rotated boxes** → false/missed hits; use SAT for rotation.
|
|
94
|
+
- **O(n²) pair testing** → frame drops at scale; add spatial hash/quadtree broadphase.
|
|
95
|
+
- **Resolving both axes at once** → catching on tile seams / clipping corners; resolve X then Y.
|
|
96
|
+
- **Not zeroing velocity on the resolved axis** → gravity accumulates, object shudders into ground.
|
|
97
|
+
- **Player floats off ramps / trips on stairs** → missing snap-to-ground / autostep; use a character controller (Rapier) with those enabled.
|
|
98
|
+
- **Box character controller** → snags on edges; use a capsule.
|
|
99
|
+
- **`sqrt` in hot distance checks** → wasted perf; compare squared distances.
|
|
100
|
+
- **Reading physics state without render interpolation** → visible stutter at high refresh rates; interpolate with `alpha`.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Defaults to apply
|
|
105
|
+
- **Right-size the solution:** hand-rolled **AABB + separate-axis resolution + swept collision** for arcade/2D/platformers; **Rapier** (2D or 3D) when we need real rigid bodies, joints, or a character controller. Don't default to a heavy engine.
|
|
106
|
+
- **Always run physics on a fixed timestep with a clamped accumulator + render interpolation** — this one pattern prevents tunneling instability and jitter, and it ties directly into the fixed-timestep game loop (see `threejs-foundational.md`).
|
|
107
|
+
- **Ship anti-tunneling by default** for fast objects (swept AABB or CCD/raycast for projectiles) and a **broadphase (spatial hash or quadtree)** once there are many colliders.
|
|
108
|
+
- **For 3D player movement, generate a Rapier `KinematicCharacterController` with a capsule, autostep, snap-to-ground, and slope limits** — not a raw dynamic body — so movement feels right out of the box.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Sources
|
|
113
|
+
- MDN — 2D collision detection (AABB, circle, SAT): https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection
|
|
114
|
+
- Gaffer On Games — "Fix Your Timestep!": https://gafferongames.com/post/fix_your_timestep/ ; "Collision Response and Coulomb Friction": https://gafferongames.com/post/collision_response_and_coulomb_friction/
|
|
115
|
+
- Swept AABB collision — jitter physics / "Swept AABB Collision Detection and Response" (gamedev): https://www.gamedev.net/tutorials/programming/general-and-gameplay-programming/swept-aabb-collision-detection-and-response-r3084/
|
|
116
|
+
- SAT reference — Metanet/N tutorial & dyn4j "SAT (Separating Axis Theorem)": https://dyn4j.org/2010/01/sat/ ; https://www.metanetsoftware.com/technique/tutorialA.html
|
|
117
|
+
- Spatial hashing / broadphase — "Spatial Hashing" (gamedev) & quadtree collision (GameDev Academy): https://gameprogrammingpatterns.com/spatial-partition.html
|
|
118
|
+
- Rapier — docs (rigid bodies, CCD, character controller): https://rapier.rs/docs/ ; character controller: https://rapier.rs/docs/user_guides/javascript/character_controller ; JS bindings: https://github.com/dimforge/rapier.js
|
|
119
|
+
- cannon-es — repo/examples (incl. PointerLockControlsCannon): https://github.com/pmndrs/cannon-es ; https://pmndrs.github.io/cannon-es/
|
|
120
|
+
- Matter.js: https://brm.io/matter-js/ ; Planck.js (Box2D): https://github.com/piqnt/planck.js
|
|
121
|
+
- Box2D docs (continuous collision / solver background): https://box2d.org/documentation/
|
|
122
|
+
- Game Programming Patterns — Spatial Partition & Game Loop: https://gameprogrammingpatterns.com/spatial-partition.html , https://gameprogrammingpatterns.com/game-loop.html
|