summer-engine 3.1.0 → 3.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +27 -5
- package/.codex-plugin/plugin.json +28 -6
- package/.cursor-plugin/plugin.json +27 -5
- package/.factory-plugin/plugin.json +27 -5
- package/.opencode/plugins/summer.js +1 -1
- package/CHANGELOG.md +102 -0
- package/README.md +18 -3
- package/assets/audit/scene_audit.gd +4136 -0
- package/assets/seeing/seeing_probe.gd +1873 -0
- package/dist/cli/commands/skills.js +20 -2
- package/dist/core/api-client.d.ts +9 -1
- package/dist/core/api-client.js +21 -4
- package/dist/core/capabilities/agent-playbook.js +46 -0
- package/dist/core/capabilities/audit/accept.d.ts +55 -0
- package/dist/core/capabilities/audit/accept.js +147 -0
- package/dist/core/capabilities/audit/args.d.ts +95 -0
- package/dist/core/capabilities/audit/args.js +89 -0
- package/dist/core/capabilities/audit/audit.d.ts +56 -0
- package/dist/core/capabilities/audit/audit.js +287 -0
- package/dist/core/capabilities/audit/frame.d.ts +14 -0
- package/dist/core/capabilities/audit/frame.js +35 -0
- package/dist/core/capabilities/audit/gaps.d.ts +96 -0
- package/dist/core/capabilities/audit/gaps.js +257 -0
- package/dist/core/capabilities/audit/judge.d.ts +162 -0
- package/dist/core/capabilities/audit/judge.js +1134 -0
- package/dist/core/capabilities/audit/math.d.ts +110 -0
- package/dist/core/capabilities/audit/math.js +263 -0
- package/dist/core/capabilities/audit/summary.d.ts +74 -0
- package/dist/core/capabilities/audit/summary.js +212 -0
- package/dist/core/capabilities/board.d.ts +31 -0
- package/dist/core/capabilities/board.js +43 -0
- package/dist/core/capabilities/camera-view.d.ts +4 -2
- package/dist/core/capabilities/camera-view.js +6 -3
- package/dist/core/capabilities/connect-signal.d.ts +85 -0
- package/dist/core/capabilities/connect-signal.js +285 -0
- package/dist/core/capabilities/engine-fallbacks.d.ts +5 -0
- package/dist/core/capabilities/engine-fallbacks.js +5 -0
- package/dist/core/capabilities/engine-ops.d.ts +5 -1
- package/dist/core/capabilities/engine-ops.js +0 -3
- package/dist/core/capabilities/engine-receipt.d.ts +12 -0
- package/dist/core/capabilities/engine-receipt.js +26 -0
- package/dist/core/capabilities/file-read.d.ts +64 -0
- package/dist/core/capabilities/file-read.js +325 -0
- package/dist/core/capabilities/game-task-plan.js +4 -2
- package/dist/core/capabilities/grep.d.ts +60 -0
- package/dist/core/capabilities/grep.js +190 -0
- package/dist/core/capabilities/inspect-node.d.ts +56 -0
- package/dist/core/capabilities/inspect-node.js +225 -0
- package/dist/core/capabilities/inspect-resource.d.ts +61 -0
- package/dist/core/capabilities/inspect-resource.js +376 -0
- package/dist/core/capabilities/math3d.d.ts +28 -0
- package/dist/core/capabilities/math3d.js +88 -0
- package/dist/core/capabilities/motion-generation.d.ts +62 -0
- package/dist/core/capabilities/motion-generation.js +138 -0
- package/dist/core/capabilities/placement-batch.d.ts +102 -0
- package/dist/core/capabilities/placement-batch.js +483 -0
- package/dist/core/capabilities/placement-math.d.ts +70 -0
- package/dist/core/capabilities/placement-math.js +209 -0
- package/dist/core/capabilities/placement-script.d.ts +42 -0
- package/dist/core/capabilities/placement-script.js +1096 -0
- package/dist/core/capabilities/placement.d.ts +485 -0
- package/dist/core/capabilities/placement.js +1731 -0
- package/dist/core/capabilities/project-context.d.ts +33 -2
- package/dist/core/capabilities/project-context.js +87 -4
- package/dist/core/capabilities/replace-node.d.ts +91 -0
- package/dist/core/capabilities/replace-node.js +423 -0
- package/dist/core/capabilities/runtime-control.d.ts +18 -2
- package/dist/core/capabilities/runtime-control.js +67 -2
- package/dist/core/capabilities/scene-batch.d.ts +63 -0
- package/dist/core/capabilities/scene-batch.js +394 -0
- package/dist/core/capabilities/scene-readback.d.ts +36 -0
- package/dist/core/capabilities/scene-readback.js +54 -0
- package/dist/core/capabilities/seeing/args.d.ts +348 -0
- package/dist/core/capabilities/seeing/args.js +178 -0
- package/dist/core/capabilities/seeing/candidates.d.ts +127 -0
- package/dist/core/capabilities/seeing/candidates.js +282 -0
- package/dist/core/capabilities/seeing/math.d.ts +134 -0
- package/dist/core/capabilities/seeing/math.js +269 -0
- package/dist/core/capabilities/seeing/probe.d.ts +62 -0
- package/dist/core/capabilities/seeing/probe.js +188 -0
- package/dist/core/capabilities/seeing/scoring.d.ts +235 -0
- package/dist/core/capabilities/seeing/scoring.js +639 -0
- package/dist/core/capabilities/seeing/seeing.d.ts +233 -0
- package/dist/core/capabilities/seeing/seeing.js +1341 -0
- package/dist/core/capabilities/seeing/shot-store.d.ts +52 -0
- package/dist/core/capabilities/seeing/shot-store.js +204 -0
- package/dist/core/capabilities/surface-snap-mesh.d.ts +88 -0
- package/dist/core/capabilities/surface-snap-mesh.js +425 -0
- package/dist/core/capabilities/surface-snap.d.ts +77 -0
- package/dist/core/capabilities/surface-snap.js +461 -0
- package/dist/core/capabilities/tool-dispatch.js +231 -72
- package/dist/core/capabilities/tscn.d.ts +85 -0
- package/dist/core/capabilities/tscn.js +391 -0
- package/dist/core/capabilities/world-snapshot.d.ts +58 -0
- package/dist/core/capabilities/world-snapshot.js +150 -0
- package/dist/core/capability-skew.d.ts +5 -5
- package/dist/core/capability-skew.js +14 -6
- package/dist/core/headless/worker-engine-client.d.ts +1 -1
- package/dist/core/headless/worker-engine-client.js +2 -2
- package/dist/core/library-read.d.ts +37 -5
- package/dist/core/library-read.js +264 -33
- package/dist/core/library-search.d.ts +2 -2
- package/dist/installer/agent-config.js +5 -3
- package/dist/installer/agent-table.d.ts +16 -0
- package/dist/installer/agent-table.js +52 -17
- package/dist/mcp/server.js +9 -3
- package/dist/mcp/tools/asset-tools.js +21 -3
- package/dist/mcp/tools/audit-tools.d.ts +23 -0
- package/dist/mcp/tools/audit-tools.js +60 -0
- package/dist/mcp/tools/debug-tools.js +3 -1
- package/dist/mcp/tools/file-tools.js +17 -4
- package/dist/mcp/tools/generate-tools.js +74 -66
- package/dist/mcp/tools/library-tools.js +2 -0
- package/dist/mcp/tools/perception-tools.js +6 -19
- package/dist/mcp/tools/placement-tools.d.ts +24 -0
- package/dist/mcp/tools/placement-tools.js +91 -0
- package/dist/mcp/tools/project-tools.js +18 -15
- package/dist/mcp/tools/scene-tools.js +71 -83
- package/dist/mcp/tools/seeing-tools.d.ts +24 -0
- package/dist/mcp/tools/seeing-tools.js +64 -0
- package/dist/mcp/tools/spatial-tools.js +17 -9
- package/dist/mcp/tools/visual-tools.js +59 -7
- package/gemini-extension.json +26 -4
- package/integrations/amp/README.md +1 -1
- package/integrations/crush/README.md +1 -1
- package/integrations/grok-build/README.md +1 -1
- package/integrations/hermes/README.md +1 -1
- package/integrations/kimi-code/README.md +1 -1
- package/integrations/mistral-vibe/README.md +1 -1
- package/integrations/opencode/README.md +1 -1
- package/integrations/rovo-dev/README.md +1 -1
- package/integrations/vscode-copilot/README.md +1 -1
- package/integrations/warp/README.md +1 -1
- package/integrations/windsurf/README.md +1 -1
- package/library/references/mcp-tools-reference/mcp-tools-reference.md +59 -15
- package/library/references/mcp-tools-reference/resource.yaml +1 -1
- package/library/references/summer-folder/resource.yaml +1 -1
- package/library/references/summer-folder/summer-folder.md +1 -1
- package/library/skills/3d-lighting/SKILL.md +1 -1
- package/library/skills/README.md +36 -14
- package/library/skills/adaptive-music/SKILL.md +1 -1
- package/library/skills/agent-playtesting/SKILL.md +1 -1
- package/library/skills/ai-characters-in-games/SKILL.md +220 -0
- package/library/skills/ai-characters-in-games/resource.yaml +30 -0
- package/library/skills/ambient-bed/SKILL.md +1 -1
- package/library/skills/animated-loop/SKILL.md +1 -1
- package/library/skills/animation-tree/SKILL.md +1 -1
- package/library/skills/art-direction/SKILL.md +1 -1
- package/library/skills/asset-strategy/SKILL.md +2 -1
- package/library/skills/asset-strategy/resource.yaml +2 -1
- package/library/skills/audio-direction/SKILL.md +1 -1
- package/library/skills/auto-fire-targeting/SKILL.md +1 -1
- package/library/skills/brainstorm-game/SKILL.md +1 -1
- package/library/skills/brainstorming/SKILL.md +1 -1
- package/library/skills/browse-templates/SKILL.md +3 -3
- package/library/skills/browse-templates/resource.yaml +1 -1
- package/library/skills/camera-rigs/SKILL.md +128 -0
- package/library/skills/camera-rigs/resource.yaml +29 -0
- package/library/skills/celeste-momentum-platforming/SKILL.md +1 -1
- package/library/skills/character-animation-wiring/SKILL.md +1 -1
- package/library/skills/character-model/SKILL.md +1 -1
- package/library/skills/character-movement/SKILL.md +326 -0
- package/library/skills/character-movement/references/default-third-person-controller.gd +201 -0
- package/library/skills/character-movement/references/default-third-person-verification-probe.gd +154 -0
- package/library/skills/character-movement/references/movement-3d.md +136 -0
- package/library/skills/character-movement/references/platformer-feel.md +83 -0
- package/library/skills/character-movement/resource.yaml +40 -0
- package/library/skills/character-portrait/SKILL.md +1 -1
- package/library/skills/cinematic-cutscene/SKILL.md +1 -1
- package/library/skills/combat-basics/SKILL.md +159 -0
- package/library/skills/combat-basics/references/projectiles-and-aoe.md +100 -0
- package/library/skills/combat-basics/resource.yaml +34 -0
- package/library/skills/concept-art/SKILL.md +1 -1
- package/library/skills/create-asset-sheet/SKILL.md +1 -1
- package/library/skills/debug/SKILL.md +1 -1
- package/library/skills/debugging-game-feel/SKILL.md +1 -1
- package/library/skills/design-level/SKILL.md +1 -1
- package/library/skills/design-mechanic/SKILL.md +1 -1
- package/library/skills/design-npc/SKILL.md +1 -1
- package/library/skills/diagnosing-perf-regressions/SKILL.md +1 -1
- package/library/skills/dispatching-parallel-agents/SKILL.md +1 -1
- package/library/skills/driving-the-editor-ui/SKILL.md +1 -1
- package/library/skills/environment-kit/SKILL.md +1 -1
- package/library/skills/export-and-ship/SKILL.md +1 -1
- package/library/skills/fabricating-assets/SKILL.md +1 -1
- package/library/skills/facial-and-lipsync/SKILL.md +1 -1
- package/library/skills/fps-controller/SKILL.md +9 -7
- package/library/skills/fps-controller/resource.yaml +4 -1
- package/library/skills/game-feel/SKILL.md +1 -1
- package/library/skills/gameskill/SKILL.md +1 -1
- package/library/skills/gdscript-patterns/SKILL.md +1 -1
- package/library/skills/generate-motion/SKILL.md +9 -9
- package/library/skills/generate-motion/resource.yaml +2 -1
- package/library/skills/godogen-visual-proof-game-generation/SKILL.md +1 -1
- package/library/skills/headless-scripting/SKILL.md +1 -1
- package/library/skills/in-game-purchases/SKILL.md +254 -0
- package/library/skills/in-game-purchases/resource.yaml +29 -0
- package/library/skills/instantiate-asset-pack/SKILL.md +1 -1
- package/library/skills/investigating-bugs/SKILL.md +1 -1
- package/library/skills/lumera-single-image-scene-reconstruction/SKILL.md +1 -1
- package/library/skills/make-game/SKILL.md +25 -3
- package/library/skills/make-game/references/2d-platformer-polish.md +93 -0
- package/library/skills/make-game/references/2d-platformer.md +117 -0
- package/library/skills/make-game/references/2d-rpg-content.md +234 -0
- package/library/skills/make-game/references/2d-rpg.md +241 -0
- package/library/skills/make-game/references/2d-survivors-progression.md +201 -0
- package/library/skills/make-game/references/2d-survivors.md +237 -0
- package/library/skills/make-game/references/3d-fps-systems.md +216 -0
- package/library/skills/make-game/references/3d-fps.md +177 -0
- package/library/skills/make-game/references/3d-horror-systems.md +186 -0
- package/library/skills/make-game/references/3d-horror.md +201 -0
- package/library/skills/make-game/references/3d-third-person-systems.md +220 -0
- package/library/skills/make-game/references/3d-third-person.md +215 -0
- package/library/skills/make-game/resource.yaml +6 -1
- package/library/skills/multiplayer/SKILL.md +150 -0
- package/library/skills/multiplayer/resource.yaml +33 -0
- package/library/skills/multiplayer-movement/SKILL.md +510 -0
- package/library/skills/multiplayer-movement/files.md +501 -0
- package/library/skills/multiplayer-movement/resource.yaml +30 -0
- package/library/skills/multiplayer-project/SKILL.md +521 -0
- package/library/skills/multiplayer-project/files.md +448 -0
- package/library/skills/multiplayer-project/resource.yaml +36 -0
- package/library/skills/multiplayer-publish/SKILL.md +229 -0
- package/library/skills/multiplayer-publish/export-errors.md +89 -0
- package/library/skills/multiplayer-publish/resource.yaml +28 -0
- package/library/skills/multiplayer-state/SKILL.md +438 -0
- package/library/skills/multiplayer-state/files.md +592 -0
- package/library/skills/multiplayer-state/resource.yaml +35 -0
- package/library/skills/multiplayer-testing/SKILL.md +236 -0
- package/library/skills/multiplayer-testing/resource.yaml +39 -0
- package/library/skills/music-track/SKILL.md +1 -1
- package/library/skills/navigate-summer/SKILL.md +1 -1
- package/library/skills/new-project/SKILL.md +1 -1
- package/library/skills/organic-model/SKILL.md +1 -1
- package/library/skills/pixel-art/SKILL.md +1 -1
- package/library/skills/play/SKILL.md +1 -1
- package/library/skills/playtesting-a-feature/SKILL.md +1 -1
- package/library/skills/procedural-animation/SKILL.md +2 -2
- package/library/skills/procedural-animation/resource.yaml +1 -1
- package/library/skills/prop-model/SKILL.md +1 -1
- package/library/skills/psx-retro-rendering/SKILL.md +1 -1
- package/library/skills/realtime-wet-surfaces/SKILL.md +2 -2
- package/library/skills/remote-deploy/SKILL.md +1 -1
- package/library/skills/retarget/SKILL.md +1 -1
- package/library/skills/running-in-the-cloud/SKILL.md +1 -1
- package/library/skills/save-load/SKILL.md +156 -0
- package/library/skills/save-load/resource.yaml +27 -0
- package/library/skills/scene-composition/SKILL.md +1 -1
- package/library/skills/scene-hierarchy-design/SKILL.md +1 -1
- package/library/skills/scene-scripting/SKILL.md +1 -1
- package/library/skills/scene-to-level/SKILL.md +1 -1
- package/library/skills/skill-create/SKILL.md +1 -1
- package/library/skills/skill-improve/SKILL.md +1 -1
- package/library/skills/skill-test/SKILL.md +1 -1
- package/library/skills/skintokens-auto-rigging/SKILL.md +1 -1
- package/library/skills/skybox-panorama/SKILL.md +1 -1
- package/library/skills/sound-effect/SKILL.md +1 -1
- package/library/skills/spatial-placement/SKILL.md +134 -3
- package/library/skills/spatial-placement/references/kit-placement-tools.md +364 -0
- package/library/skills/spatial-placement/resource.yaml +16 -2
- package/library/skills/sprite-sheet/SKILL.md +1 -1
- package/library/skills/summer-analytics/SKILL.md +147 -0
- package/library/skills/summer-analytics/resource.yaml +28 -0
- package/library/skills/summer-friends/SKILL.md +226 -0
- package/library/skills/summer-friends/resource.yaml +28 -0
- package/library/skills/summer-leaderboards/SKILL.md +231 -0
- package/library/skills/summer-leaderboards/resource.yaml +30 -0
- package/library/skills/summer-match-results/SKILL.md +286 -0
- package/library/skills/summer-match-results/resource.yaml +32 -0
- package/library/skills/summer-matchmaking/SKILL.md +301 -0
- package/library/skills/summer-matchmaking/resource.yaml +32 -0
- package/library/skills/summer-parties/SKILL.md +253 -0
- package/library/skills/summer-parties/resource.yaml +30 -0
- package/library/skills/summer-player-data/SKILL.md +322 -0
- package/library/skills/summer-player-data/resource.yaml +30 -0
- package/library/skills/summer-store/SKILL.md +271 -0
- package/library/skills/summer-store/resource.yaml +29 -0
- package/library/skills/summer-world-chat/SKILL.md +195 -0
- package/library/skills/summer-world-chat/resource.yaml +30 -0
- package/library/skills/summer-world-saves/SKILL.md +284 -0
- package/library/skills/summer-world-saves/resource.yaml +29 -0
- package/library/skills/text-to-motion/SKILL.md +99 -0
- package/library/skills/text-to-motion/prompts-and-controls.md +34 -0
- package/library/skills/text-to-motion/resource.yaml +41 -0
- package/library/skills/text-to-motion/rigging-for-text-motion.md +54 -0
- package/library/skills/tileable-texture/SKILL.md +1 -1
- package/library/skills/trailer-shot/SKILL.md +1 -1
- package/library/skills/tune-performance/SKILL.md +1 -1
- package/library/skills/ui-basics/SKILL.md +10 -1
- package/library/skills/ui-graphics/SKILL.md +1 -1
- package/library/skills/use-widget-asset/SKILL.md +1 -1
- package/library/skills/using-summer/SKILL.md +2 -2
- package/library/skills/using-summer/resource.yaml +2 -2
- package/library/skills/vehicle-model/SKILL.md +1 -1
- package/library/skills/verification-before-completion/SKILL.md +1 -1
- package/library/skills/verifying-scenes/SKILL.md +76 -2
- package/library/skills/verifying-scenes/resource.yaml +13 -2
- package/library/skills/vfx-dissolve/SKILL.md +1 -1
- package/library/skills/vfx-fire/SKILL.md +1 -1
- package/library/skills/vfx-hit-spark/SKILL.md +1 -1
- package/library/skills/vfx-lightning/SKILL.md +1 -1
- package/library/skills/vfx-magic-glow/SKILL.md +1 -1
- package/library/skills/vfx-muzzle-flash/SKILL.md +1 -1
- package/library/skills/vfx-smoke/SKILL.md +1 -1
- package/library/skills/vfx-water-ripple/SKILL.md +1 -1
- package/library/skills/voice-line/SKILL.md +1 -1
- package/library/skills/web-and-touch-input/SKILL.md +38 -0
- package/library/skills/web-and-touch-input/resource.yaml +28 -0
- package/library/skills/web-compatibility-look/SKILL.md +62 -0
- package/library/skills/web-compatibility-look/resource.yaml +30 -0
- package/library/skills/world-building-3d/SKILL.md +8 -1
- package/library/skills/world-building-3d/references/spatial-tools.md +1 -1
- package/library/skills/world-building-3d/resource.yaml +2 -1
- package/library/skills/writing-plans/SKILL.md +1 -1
- package/library/skills/writing-skills/SKILL.md +1 -1
- package/library/templates/3d-lan-multiplayer-starter/resource.yaml +2 -2
- package/library/tools/attach-to-surface/resource.yaml +142 -0
- package/library/tools/batch/resource.yaml +9 -3
- package/library/tools/connect-ports/resource.yaml +120 -0
- package/library/tools/connect-signal/resource.yaml +3 -2
- package/library/tools/debug-views/resource.yaml +94 -0
- package/library/tools/frame-nodes/resource.yaml +104 -0
- package/library/tools/frame-shot/resource.yaml +134 -0
- package/library/tools/generate-motion/resource.yaml +44 -9
- package/library/tools/get-board/resource.yaml +50 -0
- package/library/tools/get-project-context/resource.yaml +13 -2
- package/library/tools/grep/resource.yaml +77 -0
- package/library/tools/inspect-asset/resource.yaml +71 -0
- package/library/tools/inspect-node/resource.yaml +9 -2
- package/library/tools/inspect-resource/resource.yaml +16 -5
- package/library/tools/instantiate-scene/resource.yaml +27 -2
- package/library/tools/measure/resource.yaml +96 -0
- package/library/tools/place-adjacent/resource.yaml +113 -0
- package/library/tools/play/resource.yaml +15 -2
- package/library/tools/raycast/resource.yaml +101 -0
- package/library/tools/read-file/resource.yaml +36 -2
- package/library/tools/read-library/resource.yaml +2 -2
- package/library/tools/repeat-along/resource.yaml +129 -0
- package/library/tools/replace-node/resource.yaml +11 -5
- package/library/tools/scene-audit/resource.yaml +144 -0
- package/library/tools/screenshot/resource.yaml +13 -2
- package/library/tools/search-assets/resource.yaml +18 -0
- package/library/tools/shot-sheet/resource.yaml +110 -0
- package/library/tools/snap-to-surface/resource.yaml +4 -1
- package/library/tools/world-snapshot/resource.yaml +24 -3
- package/library/tools/zoom/resource.yaml +105 -0
- package/package.json +1 -1
- package/registry/generated/aliases.json +17 -3
- package/registry/generated/counts.json +3 -3
- package/registry/generated/gemini-extension.json +26 -4
- package/registry/generated/index.json +2154 -469
- package/registry/generated/marketplace.claude.json +2 -2
- package/registry/generated/plugin.claude.json +27 -5
- package/registry/generated/plugin.codex.json +28 -6
- package/registry/generated/plugin.cursor.json +27 -5
- package/registry/generated/plugin.factory.json +27 -5
- package/registry/generated/skills-index.md +36 -14
- package/registry/generated/skills-registry.json +421 -124
- package/registry/generated/templates-registry.json +2 -2
- package/library/skills/host-authoritative-state/SKILL.md +0 -332
- package/library/skills/host-authoritative-state/resource.yaml +0 -28
- package/library/skills/peer-to-peer-multiplayer/SKILL.md +0 -373
- package/library/skills/peer-to-peer-multiplayer/resource.yaml +0 -28
- package/library/skills/setup-multiplayer/SKILL.md +0 -336
- package/library/skills/setup-multiplayer/resource.yaml +0 -28
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
class_name SummerThirdPersonController
|
|
2
|
+
extends CharacterBody3D
|
|
3
|
+
|
|
4
|
+
## Canonical controller for a playable Summer humanoid character package.
|
|
5
|
+
## Copy this file unchanged first. Customize the copy only when the user asks.
|
|
6
|
+
|
|
7
|
+
const REQUIRED_ROLES: Array[StringName] = [&"idle", &"walk", &"run", &"jump"]
|
|
8
|
+
const REQUIRED_ACTIONS: Array[StringName] = [
|
|
9
|
+
&"move_left",
|
|
10
|
+
&"move_right",
|
|
11
|
+
&"move_forward",
|
|
12
|
+
&"move_back",
|
|
13
|
+
&"jump",
|
|
14
|
+
&"sprint",
|
|
15
|
+
&"ui_cancel",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
@export_file("*.json") var character_manifest_path: String
|
|
19
|
+
|
|
20
|
+
@export_category("Movement")
|
|
21
|
+
@export var walk_speed: float = 4.0
|
|
22
|
+
@export var run_speed: float = 7.0
|
|
23
|
+
@export var acceleration: float = 24.0
|
|
24
|
+
@export var jump_velocity: float = 5.5
|
|
25
|
+
@export var turn_speed: float = 12.0
|
|
26
|
+
@export var floor_snap_distance: float = 0.3
|
|
27
|
+
@export var floor_stick_velocity: float = 0.5
|
|
28
|
+
|
|
29
|
+
@export_category("Camera")
|
|
30
|
+
@export var mouse_sensitivity: float = 0.002
|
|
31
|
+
@export var camera_height: float = 1.5
|
|
32
|
+
@export var camera_distance: float = 4.5
|
|
33
|
+
@export_flags_3d_physics var camera_collision_mask: int = 1
|
|
34
|
+
@export var camera_pitch_degrees: float = -20.0
|
|
35
|
+
|
|
36
|
+
@export_category("Respawn")
|
|
37
|
+
@export var respawn_below_y: float = -15.0
|
|
38
|
+
|
|
39
|
+
@export_category("Required Nodes")
|
|
40
|
+
@export_node_path("Node3D") var visual_root_path: NodePath = ^"CharacterRotationRoot"
|
|
41
|
+
@export_node_path("Node3D") var camera_yaw_path: NodePath = ^"CameraYaw"
|
|
42
|
+
@export_node_path("Node3D") var camera_pitch_path: NodePath = ^"CameraYaw/CameraPitch"
|
|
43
|
+
@export_node_path("SpringArm3D") var spring_arm_path: NodePath = ^"CameraYaw/CameraPitch/SpringArm3D"
|
|
44
|
+
@export_node_path("AnimationPlayer") var animation_player_path: NodePath = ^"CharacterRotationRoot/Character/Visual/AnimationPlayer"
|
|
45
|
+
|
|
46
|
+
@onready var _visual_root: Node3D = get_node(visual_root_path) as Node3D
|
|
47
|
+
@onready var _camera_yaw: Node3D = get_node(camera_yaw_path) as Node3D
|
|
48
|
+
@onready var _camera_pitch: Node3D = get_node(camera_pitch_path) as Node3D
|
|
49
|
+
@onready var _spring_arm: SpringArm3D = get_node(spring_arm_path) as SpringArm3D
|
|
50
|
+
@onready var _animation_player: AnimationPlayer = get_node(animation_player_path) as AnimationPlayer
|
|
51
|
+
|
|
52
|
+
var _gravity: float = 0.0
|
|
53
|
+
var _spawn_position: Vector3
|
|
54
|
+
var _clips_by_role: Dictionary = {}
|
|
55
|
+
var _active_role: StringName = &""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
func _ready() -> void:
|
|
59
|
+
_validate_scene_contract()
|
|
60
|
+
_gravity = float(ProjectSettings.get_setting("physics/3d/default_gravity"))
|
|
61
|
+
_spawn_position = global_position
|
|
62
|
+
floor_snap_length = floor_snap_distance
|
|
63
|
+
_camera_yaw.position.y = camera_height
|
|
64
|
+
_camera_pitch.rotation = Vector3(deg_to_rad(camera_pitch_degrees), 0.0, 0.0)
|
|
65
|
+
_spring_arm.spring_length = camera_distance
|
|
66
|
+
_spring_arm.collision_mask = camera_collision_mask
|
|
67
|
+
_spring_arm.add_excluded_object(get_rid())
|
|
68
|
+
_configure_character_animation()
|
|
69
|
+
_play_role(&"idle")
|
|
70
|
+
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
func _unhandled_input(event: InputEvent) -> void:
|
|
74
|
+
if event.is_action_pressed(&"ui_cancel"):
|
|
75
|
+
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
76
|
+
return
|
|
77
|
+
if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
|
78
|
+
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
79
|
+
if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED:
|
|
80
|
+
return
|
|
81
|
+
if event is InputEventMouseMotion:
|
|
82
|
+
_camera_yaw.rotate_y(-event.screen_relative.x * mouse_sensitivity)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
func _physics_process(delta: float) -> void:
|
|
86
|
+
var was_on_floor := is_on_floor()
|
|
87
|
+
if was_on_floor:
|
|
88
|
+
# A small downward velocity keeps the body snapped to flat and gently
|
|
89
|
+
# descending floors instead of producing a one-frame false airborne state.
|
|
90
|
+
velocity.y = -floor_stick_velocity
|
|
91
|
+
else:
|
|
92
|
+
velocity.y -= _gravity * delta
|
|
93
|
+
if Input.is_action_just_pressed(&"jump") and was_on_floor:
|
|
94
|
+
velocity.y = jump_velocity
|
|
95
|
+
|
|
96
|
+
var input_vector: Vector2 = Input.get_vector(
|
|
97
|
+
&"move_left",
|
|
98
|
+
&"move_right",
|
|
99
|
+
&"move_forward",
|
|
100
|
+
&"move_back"
|
|
101
|
+
)
|
|
102
|
+
var camera_forward: Vector3 = -_camera_yaw.global_transform.basis.z
|
|
103
|
+
var camera_right: Vector3 = _camera_yaw.global_transform.basis.x
|
|
104
|
+
camera_forward.y = 0.0
|
|
105
|
+
camera_right.y = 0.0
|
|
106
|
+
camera_forward = camera_forward.normalized()
|
|
107
|
+
camera_right = camera_right.normalized()
|
|
108
|
+
var movement_direction: Vector3 = (
|
|
109
|
+
camera_right * input_vector.x + camera_forward * -input_vector.y
|
|
110
|
+
)
|
|
111
|
+
if movement_direction.length_squared() > 1.0:
|
|
112
|
+
movement_direction = movement_direction.normalized()
|
|
113
|
+
|
|
114
|
+
var is_sprinting: bool = Input.is_action_pressed(&"sprint")
|
|
115
|
+
var target_speed: float = run_speed if is_sprinting else walk_speed
|
|
116
|
+
var target_velocity: Vector3 = movement_direction * target_speed
|
|
117
|
+
velocity.x = move_toward(velocity.x, target_velocity.x, acceleration * delta)
|
|
118
|
+
velocity.z = move_toward(velocity.z, target_velocity.z, acceleration * delta)
|
|
119
|
+
|
|
120
|
+
move_and_slide()
|
|
121
|
+
_turn_visual_toward(movement_direction, delta)
|
|
122
|
+
_update_animation_state(is_sprinting)
|
|
123
|
+
if global_position.y < respawn_below_y:
|
|
124
|
+
global_position = _spawn_position
|
|
125
|
+
velocity = Vector3.ZERO
|
|
126
|
+
_play_role(&"idle")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
func _validate_scene_contract() -> void:
|
|
130
|
+
assert(_visual_root != null, "Missing CharacterRotationRoot")
|
|
131
|
+
assert(_camera_yaw != null, "Missing CameraYaw")
|
|
132
|
+
assert(_camera_pitch != null, "Missing CameraPitch")
|
|
133
|
+
assert(_spring_arm != null, "Missing SpringArm3D")
|
|
134
|
+
assert(_animation_player != null, "Missing character AnimationPlayer")
|
|
135
|
+
assert(character_manifest_path != "", "Set character_manifest_path")
|
|
136
|
+
for action in REQUIRED_ACTIONS:
|
|
137
|
+
assert(InputMap.has_action(action), "Missing input action: %s" % action)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
func _configure_character_animation() -> void:
|
|
141
|
+
var file := FileAccess.open(character_manifest_path, FileAccess.READ)
|
|
142
|
+
assert(file != null, "Missing character manifest: %s" % character_manifest_path)
|
|
143
|
+
var manifest = JSON.parse_string(file.get_as_text())
|
|
144
|
+
assert(manifest is Dictionary, "Invalid character manifest JSON")
|
|
145
|
+
assert(manifest.get("status") == "ready", "Character package is not ready")
|
|
146
|
+
assert(
|
|
147
|
+
manifest.get("normalization", {}).get("forwardAxis") == "-Z",
|
|
148
|
+
"Character package forward axis must be -Z"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
for entry in manifest.get("animations", []):
|
|
152
|
+
if not entry is Dictionary:
|
|
153
|
+
continue
|
|
154
|
+
var role := StringName(String(entry.get("semanticRole", "")).to_lower())
|
|
155
|
+
if role not in REQUIRED_ROLES:
|
|
156
|
+
continue
|
|
157
|
+
var clip := StringName(String(entry.get("name", "")))
|
|
158
|
+
assert(clip != &"", "Requested character role has no clip: %s" % role)
|
|
159
|
+
assert(not _clips_by_role.has(role), "Ambiguous character role: %s" % role)
|
|
160
|
+
assert(_animation_player.has_animation(clip), "Manifest clip is not playable: %s" % clip)
|
|
161
|
+
_clips_by_role[role] = clip
|
|
162
|
+
|
|
163
|
+
for role in REQUIRED_ROLES:
|
|
164
|
+
assert(_clips_by_role.has(role), "Missing requested character role: %s" % role)
|
|
165
|
+
for role in [&"idle", &"walk", &"run"]:
|
|
166
|
+
_animation_player.get_animation(_clips_by_role[role]).loop_mode = Animation.LOOP_LINEAR
|
|
167
|
+
_animation_player.get_animation(_clips_by_role[&"jump"]).loop_mode = Animation.LOOP_NONE
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
func _update_animation_state(is_sprinting: bool) -> void:
|
|
171
|
+
var horizontal_speed := Vector2(velocity.x, velocity.z).length()
|
|
172
|
+
var next_role: StringName
|
|
173
|
+
if not is_on_floor():
|
|
174
|
+
next_role = &"jump"
|
|
175
|
+
elif horizontal_speed <= 0.05:
|
|
176
|
+
next_role = &"idle"
|
|
177
|
+
elif is_sprinting:
|
|
178
|
+
next_role = &"run"
|
|
179
|
+
else:
|
|
180
|
+
next_role = &"walk"
|
|
181
|
+
_play_role(next_role)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
func _play_role(role: StringName) -> void:
|
|
185
|
+
assert(_clips_by_role.has(role), "Missing requested character role: %s" % role)
|
|
186
|
+
if role == _active_role:
|
|
187
|
+
return
|
|
188
|
+
_animation_player.play(_clips_by_role[role])
|
|
189
|
+
# Apply the first keyed pose immediately. This prevents a visible bind-pose
|
|
190
|
+
# frame while the AnimationPlayer waits for its next process tick.
|
|
191
|
+
_animation_player.advance(0.0)
|
|
192
|
+
_active_role = role
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
func _turn_visual_toward(direction: Vector3, delta: float) -> void:
|
|
196
|
+
if direction.length_squared() <= 0.0001:
|
|
197
|
+
return
|
|
198
|
+
var local_direction: Vector3 = global_transform.basis.inverse() * direction
|
|
199
|
+
var target_yaw := atan2(-local_direction.x, -local_direction.z)
|
|
200
|
+
var weight := 1.0 - exp(-turn_speed * delta)
|
|
201
|
+
_visual_root.rotation.y = lerp_angle(_visual_root.rotation.y, target_yaw, weight)
|
package/library/skills/character-movement/references/default-third-person-verification-probe.gd
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
extends SummerProbeBase
|
|
2
|
+
|
|
3
|
+
## Runtime proof for the canonical playable humanoid controller.
|
|
4
|
+
## Pass this source unchanged as the probe_source of a RunVerification op:
|
|
5
|
+
## summer_batch(ops=[{"op": "RunVerification", "max_seconds": 30, "probe_source": <this file>}])
|
|
6
|
+
##
|
|
7
|
+
## If RunVerification cannot run, this file is not part of your job: finish on
|
|
8
|
+
## the saved state and hand the playtest to the user. Do not paste this into a
|
|
9
|
+
## script, a scene, or the project.
|
|
10
|
+
## Saved frames come back in the result's `frames`; every reported boolean must
|
|
11
|
+
## be true.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
func _find_first(node: Node, type_name: StringName) -> Node:
|
|
15
|
+
if node.is_class(type_name):
|
|
16
|
+
return node
|
|
17
|
+
for child in node.get_children():
|
|
18
|
+
var found := _find_first(child, type_name)
|
|
19
|
+
if found != null:
|
|
20
|
+
return found
|
|
21
|
+
return null
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
func _pose_delta(before: Transform3D, after: Transform3D) -> float:
|
|
25
|
+
var translation_delta := before.origin.distance_to(after.origin)
|
|
26
|
+
var rotation_delta := before.basis.get_rotation_quaternion().angle_to(
|
|
27
|
+
after.basis.get_rotation_quaternion()
|
|
28
|
+
)
|
|
29
|
+
return maxf(translation_delta, rotation_delta)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
func _arm_down_ratio(
|
|
33
|
+
skeleton: Skeleton3D,
|
|
34
|
+
upper_arm_name: StringName,
|
|
35
|
+
hand_name: StringName
|
|
36
|
+
) -> float:
|
|
37
|
+
var upper_arm_index := skeleton.find_bone(upper_arm_name)
|
|
38
|
+
var hand_index := skeleton.find_bone(hand_name)
|
|
39
|
+
if upper_arm_index < 0 or hand_index < 0:
|
|
40
|
+
return -1.0
|
|
41
|
+
var upper_arm_position := skeleton.get_bone_global_pose(upper_arm_index).origin
|
|
42
|
+
var hand_position := skeleton.get_bone_global_pose(hand_index).origin
|
|
43
|
+
var arm_vector := hand_position - upper_arm_position
|
|
44
|
+
if arm_vector.length_squared() <= 0.000001:
|
|
45
|
+
return -1.0
|
|
46
|
+
return (upper_arm_position.y - hand_position.y) / arm_vector.length()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
func _ready() -> void:
|
|
50
|
+
await super._ready()
|
|
51
|
+
await settle(20)
|
|
52
|
+
|
|
53
|
+
var scene := get_tree().current_scene
|
|
54
|
+
var player := _find_first(scene, &"CharacterBody3D") as CharacterBody3D
|
|
55
|
+
report("player_exists", player != null)
|
|
56
|
+
if player == null:
|
|
57
|
+
finish()
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
# Fresh scenes commonly spawn the capsule above the platform. Sampling after
|
|
61
|
+
# a fixed 20 frames can catch the initial Jump clip and falsely call it idle.
|
|
62
|
+
# Wait a bounded amount for real floor contact, then give the controller one
|
|
63
|
+
# short state transition window before measuring the resting animation.
|
|
64
|
+
for frame_index in 120:
|
|
65
|
+
if player.is_on_floor():
|
|
66
|
+
break
|
|
67
|
+
await settle(1)
|
|
68
|
+
report("idle_grounded", player.is_on_floor())
|
|
69
|
+
await settle(12)
|
|
70
|
+
|
|
71
|
+
var animation_player := _find_first(player, &"AnimationPlayer") as AnimationPlayer
|
|
72
|
+
var skeleton := _find_first(player, &"Skeleton3D") as Skeleton3D
|
|
73
|
+
report("animation_player_exists", animation_player != null)
|
|
74
|
+
report("skeleton_exists", skeleton != null)
|
|
75
|
+
if animation_player == null or skeleton == null:
|
|
76
|
+
finish()
|
|
77
|
+
return
|
|
78
|
+
|
|
79
|
+
var idle_animation := String(animation_player.current_animation)
|
|
80
|
+
report("idle_animation", idle_animation)
|
|
81
|
+
report(
|
|
82
|
+
"idle_named",
|
|
83
|
+
idle_animation == "Idle" or idle_animation.ends_with("/Idle")
|
|
84
|
+
)
|
|
85
|
+
report("idle_playing", animation_player.is_playing())
|
|
86
|
+
|
|
87
|
+
var before: Array[Transform3D] = []
|
|
88
|
+
for bone_index in skeleton.get_bone_count():
|
|
89
|
+
before.append(skeleton.get_bone_global_pose(bone_index))
|
|
90
|
+
await settle(24)
|
|
91
|
+
var maximum_pose_delta := 0.0
|
|
92
|
+
for bone_index in skeleton.get_bone_count():
|
|
93
|
+
maximum_pose_delta = maxf(
|
|
94
|
+
maximum_pose_delta,
|
|
95
|
+
_pose_delta(
|
|
96
|
+
before[bone_index],
|
|
97
|
+
skeleton.get_bone_global_pose(bone_index)
|
|
98
|
+
)
|
|
99
|
+
)
|
|
100
|
+
report("idle_pose_delta", maximum_pose_delta)
|
|
101
|
+
report("idle_moves_skeleton", maximum_pose_delta > 0.0001)
|
|
102
|
+
|
|
103
|
+
var left_arm_down_ratio := _arm_down_ratio(
|
|
104
|
+
skeleton,
|
|
105
|
+
&"LeftArm",
|
|
106
|
+
&"LeftHand"
|
|
107
|
+
)
|
|
108
|
+
var right_arm_down_ratio := _arm_down_ratio(
|
|
109
|
+
skeleton,
|
|
110
|
+
&"RightArm",
|
|
111
|
+
&"RightHand"
|
|
112
|
+
)
|
|
113
|
+
var idle_arm_bones_exist := (
|
|
114
|
+
left_arm_down_ratio >= 0.0 and right_arm_down_ratio >= 0.0
|
|
115
|
+
)
|
|
116
|
+
var idle_arms_relaxed := (
|
|
117
|
+
idle_arm_bones_exist
|
|
118
|
+
and minf(left_arm_down_ratio, right_arm_down_ratio) > 0.2
|
|
119
|
+
and (left_arm_down_ratio + right_arm_down_ratio) * 0.5 > 0.55
|
|
120
|
+
)
|
|
121
|
+
report("idle_arm_bones_exist", idle_arm_bones_exist)
|
|
122
|
+
report("idle_left_arm_down_ratio", left_arm_down_ratio)
|
|
123
|
+
report("idle_right_arm_down_ratio", right_arm_down_ratio)
|
|
124
|
+
report("idle_arms_relaxed", idle_arms_relaxed)
|
|
125
|
+
await save_frame("canonical-third-person-idle")
|
|
126
|
+
|
|
127
|
+
var camera_pitch := player.get_node_or_null("CameraYaw/CameraPitch") as Node3D
|
|
128
|
+
report("camera_pitch_exists", camera_pitch != null)
|
|
129
|
+
if camera_pitch != null:
|
|
130
|
+
var pitch_before := camera_pitch.rotation
|
|
131
|
+
var vertical_mouse := InputEventMouseMotion.new()
|
|
132
|
+
vertical_mouse.relative = Vector2(0.0, 160.0)
|
|
133
|
+
player._unhandled_input(vertical_mouse)
|
|
134
|
+
report(
|
|
135
|
+
"vertical_mouse_keeps_fixed_pitch",
|
|
136
|
+
camera_pitch.rotation.is_equal_approx(pitch_before)
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
var start_position := player.global_position
|
|
140
|
+
Input.action_press(&"move_forward")
|
|
141
|
+
await settle(24)
|
|
142
|
+
var moving_animation := animation_player.current_animation
|
|
143
|
+
var moving_position := player.global_position
|
|
144
|
+
Input.action_release(&"move_forward")
|
|
145
|
+
report("forward_progress", start_position.z - moving_position.z)
|
|
146
|
+
report("forward_moves_toward_minus_z", moving_position.z < start_position.z)
|
|
147
|
+
report("locomotion_animation", moving_animation)
|
|
148
|
+
report(
|
|
149
|
+
"locomotion_animation_active",
|
|
150
|
+
moving_animation == "Walk" or moving_animation == "Run"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
await save_frame("canonical-third-person-moving")
|
|
154
|
+
finish()
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# 3D Controllers: First + Third Person
|
|
2
|
+
|
|
3
|
+
Both build on the minimal 3D walk in SKILL.md. The difference is entirely the
|
|
4
|
+
camera rig and how look rotation is split across nodes.
|
|
5
|
+
|
|
6
|
+
## First-person node layout (non-negotiable)
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
Player (CharacterBody3D) # yaw (horizontal look) rotates THIS
|
|
10
|
+
CollisionShape3D # CapsuleShape3D, radius 0.35, height 1.8
|
|
11
|
+
Head (Node3D) # y = 1.5; pitch (vertical look) rotates THIS
|
|
12
|
+
Camera3D # fov 70-75
|
|
13
|
+
WeaponHolder (Node3D) # weapons/tools ride here
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Yaw on the body, pitch on the Head. Never pitch the body (gimbal lock + physics
|
|
17
|
+
tilt). Camera is a child of Head so it inherits both rotations.
|
|
18
|
+
|
|
19
|
+
```gdscript
|
|
20
|
+
extends CharacterBody3D
|
|
21
|
+
|
|
22
|
+
@export var speed: float = 5.0
|
|
23
|
+
@export var sprint_speed: float = 8.0
|
|
24
|
+
@export var jump_velocity: float = 4.5
|
|
25
|
+
@export var mouse_sensitivity: float = 0.002
|
|
26
|
+
|
|
27
|
+
@onready var head: Node3D = $Head
|
|
28
|
+
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
|
29
|
+
|
|
30
|
+
func _ready() -> void:
|
|
31
|
+
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
32
|
+
|
|
33
|
+
func _unhandled_input(event: InputEvent) -> void:
|
|
34
|
+
if event is InputEventMouseMotion:
|
|
35
|
+
rotate_y(-event.relative.x * mouse_sensitivity)
|
|
36
|
+
head.rotate_x(-event.relative.y * mouse_sensitivity)
|
|
37
|
+
head.rotation.x = clampf(head.rotation.x, -PI / 2.0, PI / 2.0)
|
|
38
|
+
if event.is_action_pressed("ui_cancel"):
|
|
39
|
+
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
40
|
+
|
|
41
|
+
func _physics_process(delta: float) -> void:
|
|
42
|
+
if not is_on_floor():
|
|
43
|
+
velocity.y -= gravity * delta
|
|
44
|
+
if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
45
|
+
velocity.y = jump_velocity
|
|
46
|
+
var cur_speed := sprint_speed if Input.is_action_pressed("sprint") else speed
|
|
47
|
+
var input_dir := Input.get_vector("move_left", "move_right", "move_forward", "move_back")
|
|
48
|
+
var direction := (transform.basis * Vector3(input_dir.x, 0.0, input_dir.y)).normalized()
|
|
49
|
+
if direction:
|
|
50
|
+
velocity.x = direction.x * cur_speed
|
|
51
|
+
velocity.z = direction.z * cur_speed
|
|
52
|
+
else:
|
|
53
|
+
velocity.x = move_toward(velocity.x, 0.0, cur_speed)
|
|
54
|
+
velocity.z = move_toward(velocity.z, 0.0, cur_speed)
|
|
55
|
+
move_and_slide()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Crouch
|
|
59
|
+
|
|
60
|
+
Keep two collision shapes (standing + crouching), toggle `disabled`, and a
|
|
61
|
+
`RayCast3D` pointing up (`CeilingCheck`) so you cannot stand up under a ceiling.
|
|
62
|
+
Lerp the Head y from 1.5 to ~0.9 for a smooth crouch, not a snap.
|
|
63
|
+
|
|
64
|
+
## Headbob (optional polish)
|
|
65
|
+
|
|
66
|
+
Drive the Camera's local y with `sin(time * bob_freq) * bob_amp` scaled by
|
|
67
|
+
horizontal speed, only while `is_on_floor()`. Keep amp under 0.08 or it nauseates.
|
|
68
|
+
|
|
69
|
+
## Third-person node layout
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
Player (CharacterBody3D)
|
|
73
|
+
CharacterRotationRoot (Node3D) # only this visual node yaws toward movement
|
|
74
|
+
character.tscn # normalized role-neutral visual package
|
|
75
|
+
CameraController (Node3D)
|
|
76
|
+
SpringArm3D # extends behind the -Z-facing visual on local +Z
|
|
77
|
+
Camera3D
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The `SpringArm3D` is the whole trick: it raycasts from the pivot to the camera
|
|
81
|
+
and shortens when a wall is in the way, so the camera never clips into geometry.
|
|
82
|
+
Set its `collision_mask` to your Level layer only (not the player).
|
|
83
|
+
|
|
84
|
+
Third-person differences from FPS:
|
|
85
|
+
- Mouse motion rotates the `CameraController` (orbit), not the body.
|
|
86
|
+
- The visual wrapper rotates to face the MOVEMENT direction, decoupled from the camera.
|
|
87
|
+
Because imported characters face Godot forward `-Z`, lerp
|
|
88
|
+
`CharacterRotationRoot.rotation.y` toward `atan2(-direction.x, -direction.z)`.
|
|
89
|
+
- Rotate only `CharacterRotationRoot.rotation.y`. Do not yaw the CharacterBody3D or add a
|
|
90
|
+
compensating 180-degree model rotation. The package already normalizes forward to `-Z`.
|
|
91
|
+
- Keep the third-person camera behind the character on the camera rig's local `+Z` side.
|
|
92
|
+
- Camera direction drives movement basis: build `direction` from the spring-arm's
|
|
93
|
+
global basis, flattened to the XZ plane, so "forward" means "where the camera
|
|
94
|
+
looks".
|
|
95
|
+
|
|
96
|
+
## Canonical humanoid animation states
|
|
97
|
+
|
|
98
|
+
Keep translation in the controller and use package animations only as in-place visuals.
|
|
99
|
+
For a standard playable third-person character, read and copy
|
|
100
|
+
`default-third-person-controller.gd`. It already loads exact semantic roles
|
|
101
|
+
from `character.json`, selects state after `move_and_slide()`, gives airborne
|
|
102
|
+
Jump highest priority, changes clips only when state changes, uses camera-relative
|
|
103
|
+
movement, and turns only the normalized `-Z` visual root. Do not reassemble the
|
|
104
|
+
controller from partial examples.
|
|
105
|
+
|
|
106
|
+
Do not advance the `CharacterBody3D` from animation root translation. Rotate only the
|
|
107
|
+
visual child when movement is non-zero, and keep the last facing direction while idle.
|
|
108
|
+
|
|
109
|
+
## Legacy clip-name compatibility only
|
|
110
|
+
|
|
111
|
+
Use substring discovery only for old, non-package GLBs that have no manifest. Resolve Jump
|
|
112
|
+
before Run and claim each real clip at most once. Never use this for a canonical package:
|
|
113
|
+
|
|
114
|
+
```gdscript
|
|
115
|
+
func resolve_legacy_roles(player: AnimationPlayer) -> Dictionary:
|
|
116
|
+
var result: Dictionary = {}
|
|
117
|
+
var used_clips: Dictionary = {}
|
|
118
|
+
for role in [&"jump", &"idle", &"walk", &"run"]: # Jump must precede Run.
|
|
119
|
+
for clip in player.get_animation_list():
|
|
120
|
+
if used_clips.has(clip):
|
|
121
|
+
continue
|
|
122
|
+
if String(clip).to_lower().contains(String(role)):
|
|
123
|
+
result[role] = clip
|
|
124
|
+
used_clips[clip] = true
|
|
125
|
+
break
|
|
126
|
+
return result
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Validate every requested role after legacy resolution. Do not fall back from Jump to Walk
|
|
130
|
+
or Run, and do not reuse one clip for multiple roles.
|
|
131
|
+
|
|
132
|
+
## Shared traps
|
|
133
|
+
|
|
134
|
+
- Capture/release the mouse as a pair or you trap the player (see SKILL.md).
|
|
135
|
+
- Sprint reads an unbound `sprint` action = silent no-op; bind it first.
|
|
136
|
+
- Spring arm with the wrong mask collides with the player and jams at length 0.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Platformer Feel
|
|
2
|
+
|
|
3
|
+
The base controller in SKILL.md runs, but a raw `is_on_floor()` jump feels stiff
|
|
4
|
+
and unforgiving. These four additions are what separate a prototype from a game
|
|
5
|
+
that feels good. Add them in this order; each is independent.
|
|
6
|
+
|
|
7
|
+
## Coyote time
|
|
8
|
+
|
|
9
|
+
Let the player jump for a few frames AFTER walking off a ledge. Without it, jumps
|
|
10
|
+
near edges feel like the game ate the input.
|
|
11
|
+
|
|
12
|
+
```gdscript
|
|
13
|
+
@export var coyote_time: float = 0.1
|
|
14
|
+
var _coyote_timer: float = 0.0
|
|
15
|
+
|
|
16
|
+
func _physics_process(delta: float) -> void:
|
|
17
|
+
if not is_on_floor():
|
|
18
|
+
velocity.y += gravity * delta
|
|
19
|
+
_coyote_timer -= delta
|
|
20
|
+
else:
|
|
21
|
+
_coyote_timer = coyote_time
|
|
22
|
+
# jump check uses _coyote_timer > 0.0 instead of is_on_floor()
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Jump buffering
|
|
26
|
+
|
|
27
|
+
If the player presses jump a few frames BEFORE landing, remember it and fire on
|
|
28
|
+
touchdown. Without it, fast players feel like jumps get dropped.
|
|
29
|
+
|
|
30
|
+
```gdscript
|
|
31
|
+
@export var jump_buffer: float = 0.1
|
|
32
|
+
var _buffer_timer: float = 0.0
|
|
33
|
+
|
|
34
|
+
func _physics_process(delta: float) -> void:
|
|
35
|
+
if Input.is_action_just_pressed("jump"):
|
|
36
|
+
_buffer_timer = jump_buffer
|
|
37
|
+
_buffer_timer -= delta
|
|
38
|
+
if _buffer_timer > 0.0 and _coyote_timer > 0.0:
|
|
39
|
+
velocity.y = jump_velocity
|
|
40
|
+
_buffer_timer = 0.0
|
|
41
|
+
_coyote_timer = 0.0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Variable jump height
|
|
45
|
+
|
|
46
|
+
Tap = short hop, hold = full jump. Cut upward velocity when the button releases.
|
|
47
|
+
|
|
48
|
+
```gdscript
|
|
49
|
+
if Input.is_action_just_released("jump") and velocity.y < 0.0:
|
|
50
|
+
velocity.y *= 0.4
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Asymmetric gravity (fall faster than you rise)
|
|
54
|
+
|
|
55
|
+
A heavier fall gravity is the single biggest feel upgrade. Rising floaty, falling
|
|
56
|
+
snappy reads as responsive.
|
|
57
|
+
|
|
58
|
+
```gdscript
|
|
59
|
+
@export var fall_multiplier: float = 1.6
|
|
60
|
+
|
|
61
|
+
func _physics_process(delta: float) -> void:
|
|
62
|
+
var g := gravity * (fall_multiplier if velocity.y > 0.0 else 1.0)
|
|
63
|
+
if not is_on_floor():
|
|
64
|
+
velocity.y += g * delta
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## One-way platforms
|
|
68
|
+
|
|
69
|
+
Use a `CollisionPolygon2D`/`CollisionShape2D` on a platform with
|
|
70
|
+
`one_way_collision = true` in the platform's collision shape. Drop through by
|
|
71
|
+
briefly disabling the player's collision with that layer, or move the player to a
|
|
72
|
+
lower collision layer while `move_down` is held.
|
|
73
|
+
|
|
74
|
+
## Tuning starting points
|
|
75
|
+
|
|
76
|
+
| Feel | speed | jump_velocity | gravity scale |
|
|
77
|
+
|------|-------|---------------|---------------|
|
|
78
|
+
| Tight/precise | 180 | -320 | 1.0 (fall 1.8) |
|
|
79
|
+
| Floaty/explorer | 220 | -420 | 0.7 (fall 1.3) |
|
|
80
|
+
| Fast/action | 300 | -380 | 1.0 (fall 2.0) |
|
|
81
|
+
|
|
82
|
+
Never ship default gravity 980 with a -350 jump and call it done - that is the
|
|
83
|
+
"why does my jump feel bad" default. Tune fall_multiplier first.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
id: skill/character-movement
|
|
2
|
+
kind: skill
|
|
3
|
+
version: 1.2.1
|
|
4
|
+
summary: "Player controllers for 2D and 3D, plus the canonical third-person controller for Summer humanoid packages: movement owns translation, clips follow state."
|
|
5
|
+
use_when:
|
|
6
|
+
- building or fixing player movement — gravity, collision, side-scroller, top-down, or 3D walk
|
|
7
|
+
- wiring an imported Summer character package (character.tscn) as the playable third-person player
|
|
8
|
+
- walk, run, and jump animation states, facing, or mouse look for a player controller
|
|
9
|
+
- the jump feels bad — coyote time, jump buffering, variable jump height, one-way platforms
|
|
10
|
+
do_not_use_when:
|
|
11
|
+
- a production first-person shooter controller with air control and external velocity — use fps-controller
|
|
12
|
+
- Celeste-style momentum precision platforming with dash and wall climb — use celeste-momentum-platforming
|
|
13
|
+
facets:
|
|
14
|
+
lifecycle:
|
|
15
|
+
- build
|
|
16
|
+
domains:
|
|
17
|
+
- character-controller
|
|
18
|
+
- gameplay
|
|
19
|
+
- 2d
|
|
20
|
+
- 3d
|
|
21
|
+
modalities:
|
|
22
|
+
- scripts
|
|
23
|
+
- scenes
|
|
24
|
+
- animation
|
|
25
|
+
compatibility:
|
|
26
|
+
engine: ">=4.6"
|
|
27
|
+
related:
|
|
28
|
+
skills:
|
|
29
|
+
- skill/make-game
|
|
30
|
+
- skill/camera-rigs
|
|
31
|
+
- skill/combat-basics
|
|
32
|
+
- skill/fps-controller
|
|
33
|
+
- skill/celeste-momentum-platforming
|
|
34
|
+
- skill/character-animation-wiring
|
|
35
|
+
- skill/character-model
|
|
36
|
+
- skill/playtesting-a-feature
|
|
37
|
+
source: official
|
|
38
|
+
license: MIT
|
|
39
|
+
status: preview
|
|
40
|
+
recommended: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: character-portrait
|
|
3
|
-
description:
|
|
3
|
+
description: "Generate a single polished character bust for dialogue UI, character select, lore cards, or codex entries. One character, locked composition, VN-style."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: 2d-assets
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cinematic-cutscene
|
|
3
|
-
description:
|
|
3
|
+
description: "Generate a non-interactive cutscene — reference-locked look, a 5-10s image-to-video shot, optional TTS dialogue — wired as a fading VideoStreamPlayer."
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: [Cursor, Claude Code, Windsurf, Codex]
|
|
6
6
|
category: video
|