添加 claude code game studios 到项目
This commit is contained in:
33
docs/CLAUDE.md
Normal file
33
docs/CLAUDE.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Docs Directory
|
||||
|
||||
When authoring or editing files in this directory, follow these standards.
|
||||
|
||||
## Architecture Decision Records (`docs/architecture/`)
|
||||
|
||||
Use the ADR template: `.claude/docs/templates/architecture-decision-record.md`
|
||||
|
||||
**Required sections:** Title, Status, Context, Decision, Consequences,
|
||||
ADR Dependencies, Engine Compatibility, GDD Requirements Addressed
|
||||
|
||||
**Status lifecycle:** `Proposed` → `Accepted` → `Superseded`
|
||||
- Never skip `Accepted` — stories referencing a `Proposed` ADR are auto-blocked
|
||||
- Use `/architecture-decision` to create ADRs through the guided flow
|
||||
|
||||
**TR Registry:** `docs/architecture/tr-registry.yaml`
|
||||
- Stable requirement IDs (e.g. `TR-MOV-001`) that link GDD requirements to stories
|
||||
- Never renumber existing IDs — only append new ones
|
||||
- Updated by `/architecture-review` Phase 8
|
||||
|
||||
**Control Manifest:** `docs/architecture/control-manifest.md`
|
||||
- Flat programmer rules sheet: Required / Forbidden / Guardrails per layer
|
||||
- Date-stamped `Manifest Version:` in header
|
||||
- Stories embed this version; `/story-done` checks for staleness
|
||||
|
||||
**Validation:** Run `/architecture-review` after completing a set of ADRs.
|
||||
|
||||
## Engine Reference (`docs/engine-reference/`)
|
||||
|
||||
Version-pinned engine API snapshots. **Always check here before using any
|
||||
engine API** — the LLM's training data predates the pinned engine version.
|
||||
|
||||
Current engine: see `docs/engine-reference/godot/VERSION.md`
|
||||
688
docs/COLLABORATIVE-DESIGN-PRINCIPLE.md
Normal file
688
docs/COLLABORATIVE-DESIGN-PRINCIPLE.md
Normal file
@@ -0,0 +1,688 @@
|
||||
# Collaborative Design Principle
|
||||
|
||||
**Last Updated:** 2026-02-13
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Core Philosophy
|
||||
|
||||
This agent architecture is designed for **USER-DRIVEN COLLABORATION**, not autonomous AI generation.
|
||||
|
||||
### ✅ The Right Model: Collaborative Consultant
|
||||
|
||||
```
|
||||
Agent = Expert Consultant
|
||||
User = Creative Director (Final Decision Maker)
|
||||
|
||||
Agents:
|
||||
- Ask clarifying questions
|
||||
- Research and present options
|
||||
- Explain trade-offs and reasoning
|
||||
- Draft proposals for review
|
||||
- Wait for user approval before writing
|
||||
|
||||
Users:
|
||||
- Make all creative and strategic decisions
|
||||
- Approve or reject agent suggestions
|
||||
- Direct the design vision
|
||||
- Sign off before anything is written to files
|
||||
```
|
||||
|
||||
### ❌ The Wrong Model: Autonomous Generator
|
||||
|
||||
```
|
||||
❌ Agent creates design and writes it
|
||||
❌ Agent makes decisions without user input
|
||||
❌ Agent writes code without approval
|
||||
❌ User just says "make a game" and waits
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Collaborative Workflow Pattern
|
||||
|
||||
Every agent interaction should follow this pattern:
|
||||
|
||||
### Pattern: Question → Options → Decision → Draft → Approval
|
||||
|
||||
```
|
||||
1. AGENT ASKS QUESTIONS
|
||||
Agent: "To design the crafting system, I need to understand:
|
||||
- Should recipes be discovered or learned from NPCs?
|
||||
- How punishing should failed attempts be?
|
||||
- Is there a resource cost to experimentation?"
|
||||
|
||||
2. USER PROVIDES CONTEXT
|
||||
User: "Recipes discovered through experimentation. Failed attempts should consume
|
||||
materials but give partial XP. We're targeting Explorers primarily."
|
||||
|
||||
3. AGENT PRESENTS OPTIONS WITH REASONING
|
||||
Agent: "Based on your goals, here are three approaches:
|
||||
|
||||
**Option A: Full Randomized Discovery**
|
||||
- Mechanics: Try any item combo, random success chance
|
||||
- ✅ Maximum exploration reward
|
||||
- ❌ Can feel arbitrary without skill expression
|
||||
- Example games: Minecraft early, Doodle God
|
||||
- Aligns with pillars: ✅ Emergent Stories, ⚠️ Meaningful Choices (low)
|
||||
|
||||
**Option B: Ingredient Hint System**
|
||||
- Mechanics: Items show 'compatible tags' when inspected
|
||||
- ✅ Rewards observation and deduction
|
||||
- ✅ Skill expression through pattern recognition
|
||||
- ❌ Less pure discovery magic
|
||||
- Example games: Potion Craft, Opus Magnum
|
||||
- Aligns with pillars: ✅ Meaningful Choices, ✅ Emergent Stories
|
||||
|
||||
**Option C: Hybrid (Discovery + Hints)**
|
||||
- Mechanics: Start random, unlock hint system after X failures
|
||||
- ✅ Accessible to multiple player types
|
||||
- ✅ Rewards persistence
|
||||
- ❌ More complex to implement
|
||||
- Aligns with pillars: ✅✅ Both pillars strongly
|
||||
|
||||
Which approach aligns best with your vision?"
|
||||
|
||||
4. USER DECIDES
|
||||
User: "Option C feels right. Let's set X = 5 failures to unlock hints."
|
||||
|
||||
5. AGENT DRAFTS BASED ON DECISION
|
||||
Agent: "Great. Here's a draft of the core mechanic:
|
||||
|
||||
### Core Loop
|
||||
1. Player selects 2-4 ingredients from inventory
|
||||
2. System checks against recipe database
|
||||
3. If match: Success! Recipe learned, item created, full XP
|
||||
4. If no match: Failure. Materials consumed, 25% XP gained
|
||||
5. After 5 failures on any item combo, hint system unlocks for those items
|
||||
6. Hints show ingredient 'tags' (e.g., 'Fire', 'Healing', 'Metal')
|
||||
|
||||
### Formula Proposal
|
||||
success_xp = base_xp * recipe_tier^1.5
|
||||
failure_xp = success_xp * 0.25
|
||||
hint_unlock_threshold = 5 # Failures to unlock hints
|
||||
|
||||
Does this match your intent?"
|
||||
|
||||
6. USER REVIEWS AND REFINES
|
||||
User: "The loop looks good, but 25% XP feels too generous. Make it 15%.
|
||||
Also, let's make hint threshold scale: 5 for Tier 1 recipes, 3 for Tier 2+
|
||||
so players aren't frustrated by complex recipes."
|
||||
|
||||
7. AGENT UPDATES DRAFT
|
||||
Agent: "Updated:
|
||||
failure_xp = success_xp * 0.15
|
||||
hint_unlock_threshold = 5 if recipe_tier == 1 else 3
|
||||
|
||||
Should I continue drafting the Edge Cases section, or would you like
|
||||
to review formulas in more detail first?"
|
||||
|
||||
8. USER APPROVES NEXT STEP
|
||||
User: "Formulas are good. Continue with Edge Cases."
|
||||
|
||||
9. [REPEAT CYCLE FOR EACH SECTION]
|
||||
|
||||
10. FINAL APPROVAL BEFORE WRITING
|
||||
Agent: "The full GDD draft is complete. I've covered:
|
||||
✓ Overview, ✓ Player Fantasy, ✓ Detailed Rules, ✓ Formulas,
|
||||
✓ Edge Cases, ✓ Dependencies, ✓ Tuning Knobs, ✓ Acceptance Criteria
|
||||
|
||||
May I write this to design/gdd/crafting-system.md?"
|
||||
|
||||
User: "Yes, write it." ← ONLY NOW does file get created
|
||||
|
||||
11. AGENT WRITES FILE
|
||||
Agent: [Uses Write tool]
|
||||
"Created design/gdd/crafting-system.md. Would you like me to run
|
||||
/design-review to validate it against the standard?"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 How This Applies to Different Tasks
|
||||
|
||||
### 🎨 Design Tasks
|
||||
|
||||
**Example:** "Design the combat system"
|
||||
|
||||
```
|
||||
❌ WRONG (Autonomous):
|
||||
User: "Design the combat system"
|
||||
Agent: [Writes design/gdd/combat-system.md with full design]
|
||||
Agent: "Done! Here's your combat system."
|
||||
|
||||
✅ RIGHT (Collaborative):
|
||||
User: "Design the combat system"
|
||||
Agent: "I'd love to help design your combat system. First, some questions:
|
||||
1. What's the core fantasy? (precise timing? tactical positioning? resource management?)
|
||||
2. Real-time or turn-based?
|
||||
3. How much depth vs accessibility? (think Celeste vs Dark Souls)
|
||||
4. Any reference games that nail the feel you want?"
|
||||
|
||||
User: [Answers questions]
|
||||
Agent: [Presents 3 combat approaches with pros/cons]
|
||||
User: [Picks one, refines it]
|
||||
Agent: [Drafts mechanics section]
|
||||
User: [Reviews, requests changes]
|
||||
Agent: [Iterates until user approves]
|
||||
Agent: "Ready to write to design/gdd/combat-system.md?"
|
||||
User: "Yes"
|
||||
Agent: [Writes file]
|
||||
```
|
||||
|
||||
### 💻 Coding Tasks
|
||||
|
||||
**Example:** "Implement the damage calculation"
|
||||
|
||||
```
|
||||
❌ WRONG:
|
||||
User: "Implement damage calculation"
|
||||
Agent: [Writes src/gameplay/combat/damage_calculator.gd]
|
||||
Agent: "Implemented!"
|
||||
|
||||
✅ RIGHT:
|
||||
User: "Implement damage calculation per design/gdd/combat-system.md"
|
||||
Agent: [Reads design doc]
|
||||
"I see the design calls for:
|
||||
- Base damage * weapon multiplier * crit multiplier * resistance
|
||||
|
||||
Before I implement, a few architectural questions:
|
||||
1. Should DamageCalculator be a static utility or a node in the scene tree?
|
||||
2. Where should weapon/resistance data live? (CharacterStats? Equipment class?)
|
||||
3. Do we need damage events for VFX/SFX hookup?
|
||||
|
||||
Also, I notice the design doc doesn't specify rounding behavior.
|
||||
Should we round damage (to int) or allow decimal damage?"
|
||||
|
||||
User: "Static utility. Stats on CharacterStats node. Yes, emit signal on damage.
|
||||
Round damage down to int."
|
||||
|
||||
Agent: "Got it. Here's the proposed architecture:
|
||||
|
||||
[Shows code structure]
|
||||
|
||||
Does this match your expectations? Any changes before I write the code?"
|
||||
|
||||
User: "Looks good. Implement it."
|
||||
|
||||
Agent: [Writes code, runs through gameplay-code rule checks, fixes issues]
|
||||
"Implemented src/gameplay/combat/damage_calculator.gd.
|
||||
The gameplay-code rule flagged one issue: multiplier was hardcoded.
|
||||
I moved it to assets/data/combat_config.json.
|
||||
|
||||
Would you like me to write tests now, or review the code first?"
|
||||
```
|
||||
|
||||
### 🎯 Brainstorming Tasks
|
||||
|
||||
**Example:** `/brainstorm roguelike`
|
||||
|
||||
```
|
||||
❌ WRONG:
|
||||
User: "/brainstorm roguelike"
|
||||
Skill: [Generates 1 concept and writes it to design/concept.md]
|
||||
|
||||
✅ RIGHT:
|
||||
User: "/brainstorm roguelike"
|
||||
Skill: "I'll help you brainstorm roguelike concepts using professional
|
||||
ideation frameworks. First:
|
||||
|
||||
1. What excites you about roguelikes? (permadeath tension? build variety?
|
||||
skill mastery? emergent stories?)
|
||||
2. Any sub-genre? (action, deckbuilder, puzzle, tactics?)
|
||||
3. Any roguelikes you love/hate and why?
|
||||
4. Solo dev or team? (Affects scope recommendations)
|
||||
|
||||
[User answers]
|
||||
|
||||
Okay! I'll generate 10 raw concepts across 3 categories:
|
||||
- Traditional (combat-focused)
|
||||
- Hybrid (combat + other verb)
|
||||
- Unconventional (non-combat primary loop)
|
||||
|
||||
[Generates 10 concepts with MDA analysis for each]
|
||||
|
||||
Which 2-3 resonate most? I'll do deep MDA + SDT analysis on those."
|
||||
|
||||
User: "Concepts 3, 7, and 9 are interesting"
|
||||
|
||||
Skill: [Analyzes those 3 in depth]
|
||||
"Here's the deep analysis:
|
||||
|
||||
Concept 3: [Full breakdown]
|
||||
Concept 7: [Full breakdown]
|
||||
Concept 9: [Full breakdown]
|
||||
|
||||
Based on MDA alignment, target audience, and scope:
|
||||
**I recommend Concept 7** because [reasoning].
|
||||
|
||||
But you should pick based on what excites YOU most.
|
||||
Which concept do you want to develop into your game?"
|
||||
|
||||
User: "Let's go with Concept 7"
|
||||
|
||||
Skill: "Excellent. Should I:
|
||||
A) Create the full concept document now (design/concept.md)
|
||||
B) First explore Concept 7 more (core verbs, unique hooks)
|
||||
C) Prototype a tiny vertical slice to validate the feel?"
|
||||
|
||||
User: "A, create the doc"
|
||||
|
||||
Skill: "Writing design/concept.md..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ How Agents Should Ask Questions
|
||||
|
||||
### Good Question Patterns
|
||||
|
||||
✅ **Multiple Choice with Reasoning:**
|
||||
```
|
||||
"Should enemies telegraph attacks?
|
||||
A) Yes, 0.5s before (accessible, rhythm-based)
|
||||
B) Yes, 0.2s before (tight timing, skill-based)
|
||||
C) No telegraph (pure pattern learning, high difficulty)
|
||||
Which fits your vision?"
|
||||
```
|
||||
|
||||
✅ **Constrained Options with Trade-offs:**
|
||||
```
|
||||
"Inventory system options:
|
||||
1. Grid-based (Resident Evil, Diablo): Deep space management, slower
|
||||
2. List-based (Skyrim, Fallout): Fast access, less strategic
|
||||
3. Hybrid (weight limit + limited slots): Medium complexity
|
||||
|
||||
Given your 'Meaningful Choices' pillar, I'd lean toward #1 or #3. Thoughts?"
|
||||
```
|
||||
|
||||
✅ **Open-Ended with Context:**
|
||||
```
|
||||
"The design doc doesn't specify what happens when a player dies while crafting.
|
||||
Some options:
|
||||
- Materials lost (harsh, risk/reward)
|
||||
- Materials returned to inventory (forgiving)
|
||||
- Work-in-progress saved (complex to implement)
|
||||
|
||||
What fits your target difficulty?"
|
||||
```
|
||||
|
||||
### Bad Question Patterns
|
||||
|
||||
❌ **Too Open-Ended:**
|
||||
```
|
||||
"What should the combat system be like?"
|
||||
← Too broad, user doesn't know where to start
|
||||
```
|
||||
|
||||
❌ **Leading/Assuming:**
|
||||
```
|
||||
"I'll make combat real-time since that's standard for this genre."
|
||||
← Didn't ask, just assumed
|
||||
```
|
||||
|
||||
❌ **Binary Without Context:**
|
||||
```
|
||||
"Should we have a skill tree? Yes or no?"
|
||||
← No pros/cons, no reference to game pillars
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ Structured Decision UI (AskUserQuestion)
|
||||
|
||||
Use the `AskUserQuestion` tool to present decisions as a **selectable UI** instead
|
||||
of plain markdown text. This gives the user a clean interface to pick from options
|
||||
(or type "Other" for a custom answer).
|
||||
|
||||
### The Explain → Capture Pattern
|
||||
|
||||
Detailed reasoning doesn't fit in the tool's short descriptions. So use a two-step
|
||||
pattern:
|
||||
|
||||
1. **Explain first** — Write your full expert analysis in conversation text:
|
||||
detailed pros/cons, theory references, example games, pillar alignment. This is
|
||||
where the reasoning lives.
|
||||
|
||||
2. **Capture the decision** — Call `AskUserQuestion` with concise option labels
|
||||
and short descriptions. The user picks from the UI or types a custom answer.
|
||||
|
||||
### When to Use AskUserQuestion
|
||||
|
||||
✅ **Use it for:**
|
||||
- Every decision point where you'd present 2-4 options
|
||||
- Initial clarifying questions with constrained answers
|
||||
- Batching up to 4 independent questions in one call
|
||||
- Next-step choices ("Draft formulas or refine rules first?")
|
||||
- Architecture decisions ("Static utility or singleton?")
|
||||
- Strategic choices ("Simplify scope, slip deadline, or cut feature?")
|
||||
|
||||
❌ **Don't use it for:**
|
||||
- Open-ended discovery questions ("What excites you about roguelikes?")
|
||||
- Single yes/no confirmations ("May I write to file?")
|
||||
- When running as a Task subagent (tool may not be available)
|
||||
|
||||
### Format Guidelines
|
||||
|
||||
- **Labels**: 1-5 words (e.g., "Hybrid Discovery", "Full Randomized")
|
||||
- **Descriptions**: 1 sentence summarizing the approach and key trade-off
|
||||
- **Recommended**: Add "(Recommended)" to your preferred option's label
|
||||
- **Previews**: Use `markdown` field for comparing code structures or formulas
|
||||
- **Multi-select**: Use `multiSelect: true` when choices aren't mutually exclusive
|
||||
|
||||
### Example — Multi-Question Batch (Clarifying Questions)
|
||||
|
||||
After introducing the topic in conversation, batch constrained questions:
|
||||
|
||||
```
|
||||
AskUserQuestion:
|
||||
questions:
|
||||
- question: "Should crafting recipes be discovered or learned?"
|
||||
header: "Discovery"
|
||||
options:
|
||||
- label: "Experimentation"
|
||||
description: "Players discover by trying combinations — high mystery"
|
||||
- label: "NPC/Book Learning"
|
||||
description: "Recipes taught explicitly — accessible, lower mystery"
|
||||
- label: "Tiered Hybrid"
|
||||
description: "Basic recipes learned, advanced discovered — best of both"
|
||||
- question: "How punishing should failed crafts be?"
|
||||
header: "Failure"
|
||||
options:
|
||||
- label: "Materials Lost"
|
||||
description: "All consumed on failure — high stakes, risk/reward"
|
||||
- label: "Partial Recovery"
|
||||
description: "50% returned — moderate risk"
|
||||
- label: "No Loss"
|
||||
description: "Materials returned, only time spent — forgiving"
|
||||
```
|
||||
|
||||
### Example — Design Decision (After Full Analysis)
|
||||
|
||||
After writing the full pros/cons analysis in conversation text:
|
||||
|
||||
```
|
||||
AskUserQuestion:
|
||||
questions:
|
||||
- question: "Which crafting approach fits your vision?"
|
||||
header: "Approach"
|
||||
options:
|
||||
- label: "Hybrid Discovery (Recommended)"
|
||||
description: "Discovery base with earned hints — balances exploration and accessibility"
|
||||
- label: "Full Discovery"
|
||||
description: "Pure experimentation — maximum mystery, risk of frustration"
|
||||
- label: "Hint System"
|
||||
description: "Progressive hints reveal recipes — accessible but less surprise"
|
||||
```
|
||||
|
||||
### Example — Strategic Decision
|
||||
|
||||
After presenting the full strategic analysis with pillar alignment:
|
||||
|
||||
```
|
||||
AskUserQuestion:
|
||||
questions:
|
||||
- question: "How should we handle crafting scope for Alpha?"
|
||||
header: "Scope"
|
||||
options:
|
||||
- label: "Simplify to Core (Recommended)"
|
||||
description: "Recipe discovery only, 10 recipes — makes deadline, pillar visible"
|
||||
- label: "Full Implementation"
|
||||
description: "Complete system, 30 recipes — slips Alpha by 1 week"
|
||||
- label: "Cut Entirely"
|
||||
description: "Drop crafting, focus on combat — deadline met, pillar missing"
|
||||
```
|
||||
|
||||
### Team Skill Orchestration
|
||||
|
||||
In team skills, subagents return their analysis as text. The **orchestrator**
|
||||
(main session) calls `AskUserQuestion` at each decision point between phases:
|
||||
|
||||
```
|
||||
[game-designer returns 3 combat approaches with analysis]
|
||||
|
||||
Orchestrator uses AskUserQuestion:
|
||||
question: "Which combat approach should we develop?"
|
||||
options: [concise summaries of the 3 approaches]
|
||||
|
||||
[User picks → orchestrator passes decision to next phase]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 File Writing Protocol
|
||||
|
||||
### NEVER Write Files Without Explicit Approval
|
||||
|
||||
Every file write must follow:
|
||||
|
||||
```
|
||||
1. Agent: "I've completed the [design/code/doc]. Here's a summary:
|
||||
[Key points]
|
||||
|
||||
May I write this to [filepath]?"
|
||||
|
||||
2. User: "Yes" or "No, change X first" or "Show me the full draft"
|
||||
|
||||
3. IF User says "Yes":
|
||||
Agent: [Uses Write/Edit tool]
|
||||
"Written to [filepath]. Next steps?"
|
||||
|
||||
IF User says "No":
|
||||
Agent: [Makes requested changes]
|
||||
[Returns to step 1]
|
||||
```
|
||||
|
||||
### Incremental Section Writing (Design Documents)
|
||||
|
||||
For multi-section documents (design docs, lore entries, architecture docs), write
|
||||
each section to the file as it's approved instead of building the full document
|
||||
in conversation. This prevents context overflow during long iterative sessions.
|
||||
|
||||
```
|
||||
1. Agent creates file with skeleton (all section headers, empty bodies)
|
||||
Agent: "May I create design/gdd/crafting-system.md with the section skeleton?"
|
||||
User: "Yes"
|
||||
|
||||
2. For EACH section:
|
||||
Agent: [Drafts section in conversation]
|
||||
User: [Reviews, requests changes]
|
||||
Agent: [Revises until approved]
|
||||
Agent: "May I write this section to the file?"
|
||||
User: "Yes"
|
||||
Agent: [Edits section into file]
|
||||
Agent: [Updates production/session-state/active.md with progress]
|
||||
─── Context for this section can now be safely compacted ───
|
||||
─── The decisions are IN THE FILE ───
|
||||
|
||||
3. If session crashes or compacts mid-document:
|
||||
Agent: [Reads the file — completed sections are all there]
|
||||
Agent: [Reads production/session-state/active.md — knows what's next]
|
||||
Agent: "Sections 1-4 are complete. Ready to work on section 5?"
|
||||
```
|
||||
|
||||
Why this matters: A full design doc session with 8 sections and 2-3 revision
|
||||
cycles per section can accumulate 30-50k tokens of conversation. Incremental
|
||||
writing keeps the live context at ~3-5k tokens (only the current section's
|
||||
discussion), because completed sections are persisted to disk.
|
||||
|
||||
### Multi-File Writes
|
||||
|
||||
When a change affects multiple files:
|
||||
|
||||
```
|
||||
Agent: "This implementation requires changes to 3 files:
|
||||
1. src/gameplay/inventory.gd (core logic)
|
||||
2. src/ui/inventory_ui.gd (display)
|
||||
3. assets/data/inventory_config.json (constants)
|
||||
|
||||
Should I:
|
||||
A) Show you the code first, then write all 3
|
||||
B) Implement one file at a time with approval between each
|
||||
C) Write all 3 now (fastest, but less review)
|
||||
|
||||
For complex features, I recommend B."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎭 Agent Personality Guidelines
|
||||
|
||||
Agents should be:
|
||||
|
||||
### ✅ Collaborative Consultants
|
||||
- "Let me suggest three approaches and you pick"
|
||||
- "Here's my recommendation based on [reasoning], but you decide"
|
||||
- "I need your input on [specific decision]"
|
||||
|
||||
### ✅ Experts Who Explain
|
||||
- "I recommend Option A because [reasoning with game design theory]"
|
||||
- "This approach aligns with your 'Meaningful Choices' pillar because..."
|
||||
- "Here's how [reference game] handles this, and why that works"
|
||||
|
||||
### ✅ Patient Iterators
|
||||
- "No problem, I'll adjust that formula. How does this look?"
|
||||
- "Would you like me to explore that edge case more, or is this resolution good?"
|
||||
|
||||
### ❌ NOT Autonomous Executors
|
||||
- ❌ "I've designed your combat system [done]"
|
||||
- ❌ "Implemented and committed"
|
||||
- ❌ "I decided to use approach X"
|
||||
|
||||
### ❌ NOT Passive Order-Takers
|
||||
- ❌ "Okay" [does it without any questions]
|
||||
- ❌ [Doesn't ask about ambiguities]
|
||||
- ❌ [Doesn't flag potential issues]
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Applying This to Team Skills
|
||||
|
||||
Team skills (like `/team-combat`) orchestrate multiple agents, but still collaborative:
|
||||
|
||||
```
|
||||
User: "/team-combat 'grappling hook ability'"
|
||||
|
||||
Skill (Coordinator):
|
||||
"I'll coordinate the combat team to design and implement the grappling hook.
|
||||
Before we start, a few questions:
|
||||
|
||||
1. Is this a mobility tool, a combat tool, or both?
|
||||
2. Should it pull enemies to the player, player to point, or both?
|
||||
3. Any reference games? (Just Cause, Bionic Commando, Attack on Titan?)
|
||||
|
||||
[User answers]
|
||||
|
||||
Based on your answers, I'll have the team propose options.
|
||||
|
||||
**Phase 1: Design (game-designer)**
|
||||
Starting design phase...
|
||||
[game-designer asks questions, presents options]
|
||||
[User makes decisions]
|
||||
game-designer: 'Design complete. Proceeding to architecture phase.'
|
||||
|
||||
**Phase 2: Architecture (gameplay-programmer)**
|
||||
[gameplay-programmer proposes code structure]
|
||||
[User approves or requests changes]
|
||||
|
||||
**Phase 3: Parallel Implementation**
|
||||
I'll now coordinate 4 agents to implement in parallel:
|
||||
- gameplay-programmer: Core grapple physics
|
||||
- technical-artist: Cable VFX
|
||||
- sound-designer: Whoosh + impact SFX
|
||||
- ai-programmer: Enemy reactions to being grappled
|
||||
|
||||
Each will show you their work before writing files. Proceed?"
|
||||
|
||||
User: "Yes"
|
||||
|
||||
[Each agent shows their work, gets approval, then writes]
|
||||
|
||||
Skill (Coordinator):
|
||||
"All 4 subsystems implemented. Would you like me to:
|
||||
A) Have gameplay-programmer integrate them now
|
||||
B) Let you test each independently first
|
||||
C) Run /code-review before integration?"
|
||||
```
|
||||
|
||||
The orchestration is automated, but **decision points stay with the user**.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Quick Validation: Is Your Session Collaborative?
|
||||
|
||||
After any agent interaction, check:
|
||||
|
||||
- [ ] Did the agent ask clarifying questions?
|
||||
- [ ] Did the agent present multiple options with trade-offs?
|
||||
- [ ] Did you make the final decision?
|
||||
- [ ] Did the agent get your approval before writing files?
|
||||
- [ ] Did the agent explain WHY it recommended something?
|
||||
|
||||
If you answered "No" to any, the agent wasn't collaborative enough!
|
||||
|
||||
---
|
||||
|
||||
## 📚 Example Prompts That Enforce Collaboration
|
||||
|
||||
### For Users:
|
||||
|
||||
✅ **Good User Prompts:**
|
||||
```
|
||||
"I want to design a skill tree. Ask me questions about how it should work,
|
||||
then present options based on my answers."
|
||||
|
||||
"Propose three approaches to the inventory system with pros/cons for each."
|
||||
|
||||
"Before implementing this, show me the proposed architecture and explain
|
||||
your reasoning."
|
||||
```
|
||||
|
||||
❌ **Bad User Prompts (Enable Autonomous Behavior):**
|
||||
```
|
||||
"Create a combat system" ← No guidance, agent forced to guess
|
||||
|
||||
"Just do it" ← No collaboration opportunity
|
||||
|
||||
"Implement everything in the design doc" ← No approval points
|
||||
```
|
||||
|
||||
### For Agents:
|
||||
|
||||
Agents should internally follow:
|
||||
|
||||
```
|
||||
BEFORE proposing solutions:
|
||||
1. Identify what's ambiguous or unspecified
|
||||
2. Ask clarifying questions
|
||||
3. Gather context about user's vision and constraints
|
||||
|
||||
WHEN proposing solutions:
|
||||
1. Present 2-4 options (not just one)
|
||||
2. Explain trade-offs for each
|
||||
3. Reference game design theory, user's pillars, or comparable games
|
||||
4. Make a recommendation but defer final decision to user
|
||||
|
||||
BEFORE writing files:
|
||||
1. Show draft or summary
|
||||
2. Explicitly ask: "May I write this to [file]?"
|
||||
3. Wait for "yes"
|
||||
|
||||
WHEN implementing:
|
||||
1. Explain architectural choices
|
||||
2. Flag any deviations from design docs
|
||||
3. Ask about ambiguities rather than assuming
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status
|
||||
|
||||
This principle has been fully embedded across the project:
|
||||
|
||||
- **CLAUDE.md** — Collaboration protocol section added
|
||||
- **All 48 agent definitions** — Updated to enforce question-asking and approval
|
||||
- **All skills** — Updated to require approval before writing
|
||||
- **WORKFLOW-GUIDE.md** — Rewritten with collaborative examples
|
||||
- **README.md** — Clarifies collaborative (not autonomous) design
|
||||
- **AskUserQuestion tool** — Integrated into 16 skills for structured option UI
|
||||
1703
docs/WORKFLOW-GUIDE.md
Normal file
1703
docs/WORKFLOW-GUIDE.md
Normal file
File diff suppressed because it is too large
Load Diff
56
docs/architecture/tr-registry.yaml
Normal file
56
docs/architecture/tr-registry.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
# Technical Requirement ID Registry
|
||||
#
|
||||
# PURPOSE: Persistent, stable IDs for every GDD technical requirement.
|
||||
# Prevents TR-ID renumbering across /architecture-review runs, which would
|
||||
# break story references.
|
||||
#
|
||||
# RULES:
|
||||
# - IDs are PERMANENT. Never renumber, never delete (use status: deprecated).
|
||||
# - Add new entries only at the END of each system's list.
|
||||
# - When a GDD requirement is reworded (same intent): update `requirement`
|
||||
# text and add a `revised` date. The ID stays the same.
|
||||
# - When a requirement is removed from the GDD: set status: deprecated.
|
||||
# - When a requirement is split or replaced: set status: superseded-by with
|
||||
# the new TR-ID(s).
|
||||
#
|
||||
# WRITTEN BY: /architecture-review (appends new entries, never overwrites)
|
||||
# READ BY: /create-stories (embed IDs in stories)
|
||||
# /story-done (look up current requirement text at review time)
|
||||
# /story-readiness (validate TR-ID exists and is active)
|
||||
#
|
||||
# ID FORMAT: TR-[system-slug]-[NNN]
|
||||
# system-slug = short slug matching the GDD system name
|
||||
# NNN = three-digit zero-padded sequence per system, starting at 001
|
||||
#
|
||||
# STATUS VALUES: active | deprecated | superseded-by: TR-[system]-NNN
|
||||
|
||||
version: 1
|
||||
last_updated: ""
|
||||
|
||||
requirements: []
|
||||
|
||||
# --- EXAMPLE ENTRIES (remove when first real entries are added) ---
|
||||
#
|
||||
# - id: TR-combat-001
|
||||
# system: combat
|
||||
# gdd: design/gdd/combat-system.md
|
||||
# requirement: "Player takes damage from enemies based on attack power minus defence"
|
||||
# created: 2026-03-10
|
||||
# revised: ""
|
||||
# status: active
|
||||
#
|
||||
# - id: TR-combat-002
|
||||
# system: combat
|
||||
# gdd: design/gdd/combat-system.md
|
||||
# requirement: "Combo window timing is 0.4 seconds between hits"
|
||||
# created: 2026-03-10
|
||||
# revised: "2026-04-01"
|
||||
# status: active
|
||||
#
|
||||
# - id: TR-combat-003
|
||||
# system: combat
|
||||
# gdd: design/gdd/combat-system.md
|
||||
# requirement: "Old damage formula using raw stats"
|
||||
# created: 2026-03-10
|
||||
# revised: ""
|
||||
# status: superseded-by: TR-combat-001
|
||||
63
docs/engine-reference/README.md
Normal file
63
docs/engine-reference/README.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Engine Reference Documentation
|
||||
|
||||
This directory contains curated, version-pinned documentation snapshots for the
|
||||
game engine(s) used in this project. These files exist because **LLM knowledge
|
||||
has a cutoff date** and game engines update frequently.
|
||||
|
||||
## Why This Exists
|
||||
|
||||
Claude's training data has a knowledge cutoff (currently May 2025). Game engines
|
||||
like Godot, Unity, and Unreal ship updates that introduce breaking API changes,
|
||||
new features, and deprecated patterns. Without these reference files, agents will
|
||||
suggest outdated code.
|
||||
|
||||
## Structure
|
||||
|
||||
Each engine gets its own directory:
|
||||
|
||||
```
|
||||
<engine>/
|
||||
├── VERSION.md # Pinned version, verification date, knowledge gap window
|
||||
├── breaking-changes.md # API changes between versions, organized by risk level
|
||||
├── deprecated-apis.md # "Don't use X → Use Y" lookup tables
|
||||
├── current-best-practices.md # New practices not in model training data
|
||||
└── modules/ # Per-subsystem quick references (~150 lines max each)
|
||||
├── rendering.md
|
||||
├── physics.md
|
||||
└── ...
|
||||
```
|
||||
|
||||
## How Agents Use These Files
|
||||
|
||||
Engine-specialist agents are instructed to:
|
||||
|
||||
1. Read `VERSION.md` to confirm the current engine version
|
||||
2. Check `deprecated-apis.md` before suggesting any engine API
|
||||
3. Consult `breaking-changes.md` for version-specific concerns
|
||||
4. Read relevant `modules/*.md` for subsystem-specific work
|
||||
|
||||
## Maintenance
|
||||
|
||||
### When to Update
|
||||
|
||||
- After upgrading the engine version
|
||||
- When the LLM model is updated (new knowledge cutoff)
|
||||
- After running `/refresh-docs` (if available)
|
||||
- When you discover an API the model gets wrong
|
||||
|
||||
### How to Update
|
||||
|
||||
1. Update `VERSION.md` with the new engine version and date
|
||||
2. Add new entries to `breaking-changes.md` for the version transition
|
||||
3. Move newly deprecated APIs into `deprecated-apis.md`
|
||||
4. Update `current-best-practices.md` with new patterns
|
||||
5. Update relevant `modules/*.md` with API changes
|
||||
6. Set "Last verified" dates on all modified files
|
||||
|
||||
### Quality Rules
|
||||
|
||||
- Every file must have a "Last verified: YYYY-MM-DD" date
|
||||
- Keep module files under 150 lines (context budget)
|
||||
- Include code examples showing correct/incorrect patterns
|
||||
- Link to official documentation URLs for verification
|
||||
- Only document things that differ from the model's training data
|
||||
31
docs/engine-reference/godot/VERSION.md
Normal file
31
docs/engine-reference/godot/VERSION.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Godot Engine — Version Reference
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine Version** | Godot 4.6 |
|
||||
| **Release Date** | January 2026 |
|
||||
| **Project Pinned** | 2026-02-12 |
|
||||
| **Last Docs Verified** | 2026-02-12 |
|
||||
| **LLM Knowledge Cutoff** | May 2025 |
|
||||
|
||||
## Knowledge Gap Warning
|
||||
|
||||
The LLM's training data likely covers Godot up to ~4.3. Versions 4.4, 4.5,
|
||||
and 4.6 introduced significant changes that the model does NOT know about.
|
||||
Always cross-reference this directory before suggesting Godot API calls.
|
||||
|
||||
## Post-Cutoff Version Timeline
|
||||
|
||||
| Version | Release | Risk Level | Key Theme |
|
||||
|---------|---------|------------|-----------|
|
||||
| 4.4 | ~Mid 2025 | MEDIUM | Jolt physics option, FileAccess return types, shader texture type changes |
|
||||
| 4.5 | ~Late 2025 | HIGH | Accessibility (AccessKit), variadic args, @abstract, shader baker, SMAA |
|
||||
| 4.6 | Jan 2026 | HIGH | Jolt default, glow rework, D3D12 default on Windows, IK restored |
|
||||
|
||||
## Verified Sources
|
||||
|
||||
- Official docs: https://docs.godotengine.org/en/stable/
|
||||
- 4.5→4.6 migration: https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.6.html
|
||||
- 4.4→4.5 migration: https://docs.godotengine.org/en/stable/tutorials/migrating/upgrading_to_godot_4.5.html
|
||||
- Changelog: https://github.com/godotengine/godot/blob/master/CHANGELOG.md
|
||||
- Release notes: https://godotengine.org/releases/4.6/
|
||||
70
docs/engine-reference/godot/breaking-changes.md
Normal file
70
docs/engine-reference/godot/breaking-changes.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# Godot — Breaking Changes
|
||||
|
||||
Last verified: 2026-02-12
|
||||
|
||||
Changes between Godot versions, focused on post-LLM-cutoff changes (4.4+).
|
||||
|
||||
## 4.5 → 4.6 (Jan 2026 — POST-CUTOFF, HIGH RISK)
|
||||
|
||||
| Subsystem | Change | Details |
|
||||
|-----------|--------|---------|
|
||||
| Physics | Jolt is now the DEFAULT 3D physics engine | New projects use Jolt automatically. Existing projects keep their setting. Some HingeJoint3D properties (like `damp`) only work with GodotPhysics. |
|
||||
| Rendering | Glow processes BEFORE tonemapping | Was after tonemapping. Scenes with glow will look different. Adjust intensity/blend in WorldEnvironment. |
|
||||
| Rendering | D3D12 default on Windows | Was Vulkan. For better driver compatibility. |
|
||||
| Rendering | AgX tonemapper new controls | White point and contrast parameters added. |
|
||||
| Core | Quaternion initializes to identity | Was zero. Unlikely to affect most code but technically breaking. |
|
||||
| UI | Dual-focus system | Mouse/touch focus now separate from keyboard/gamepad focus. Visual feedback differs by input method. |
|
||||
| Animation | IK system fully restored | CCDIK, FABRIK, Jacobian IK, Spline IK, TwoBoneIK via SkeletonModifier3D nodes. |
|
||||
| Editor | New "Modern" theme default | Grayscale replaces blue-tint. Restore: Editor Settings → Interface → Theme → Style: Classic |
|
||||
| Editor | "Select Mode" keybind changed | New "Select Mode" (v key) prevents accidental transforms. Old mode renamed "Transform Mode" (q key). |
|
||||
| 2D | TileMapLayer scene tile rotation | Scene tiles can now be rotated like atlas tiles. |
|
||||
| Localization | CSV plural form support | No longer requires Gettext for plurals. Context columns added. |
|
||||
| C# | Automatic string extraction | Translation strings auto-extracted from C# code. |
|
||||
| Plugins | New EditorDock class | Specialized container for plugin docks with layout control. |
|
||||
|
||||
## 4.4 → 4.5 (Late 2025 — POST-CUTOFF, HIGH RISK)
|
||||
|
||||
| Subsystem | Change | Details |
|
||||
|-----------|--------|---------|
|
||||
| GDScript | Variadic arguments added | Functions can accept `...` arbitrary params — new language feature |
|
||||
| GDScript | `@abstract` decorator | Abstract classes and methods now enforceable |
|
||||
| GDScript | Script backtracing | Detailed call stacks available even in Release builds |
|
||||
| Rendering | Stencil buffer support | New capability for advanced visual effects |
|
||||
| Rendering | SMAA 1x antialiasing | New post-processing AA option |
|
||||
| Rendering | Shader Baker | Pre-compiles shaders — reportedly 20x faster startup on some demos |
|
||||
| Rendering | Bent normal maps, specular occlusion | New material features |
|
||||
| Accessibility | Screen reader support | Control nodes work with accessibility tools via AccessKit |
|
||||
| Editor | Live translation preview | Test GUI layouts in different languages in-editor |
|
||||
| Physics | 3D interpolation rearchitected | Moved from RenderingServer to SceneTree. API unchanged but internals differ. |
|
||||
| Animation | BoneConstraint3D | New: AimModifier3D, CopyTransformModifier3D, ConvertTransformModifier3D |
|
||||
| Resources | `duplicate_deep()` added | New explicit method for deep duplication of nested resources |
|
||||
| Navigation | Dedicated 2D navigation server | No longer a proxy to 3D navigation; smaller export for 2D games |
|
||||
| UI | FoldableContainer node | New accordion-style container for collapsible UI sections |
|
||||
| UI | Recursive Control behavior | Disable mouse/focus interactions across entire node hierarchies |
|
||||
| Platform | visionOS export support | New platform target |
|
||||
| Platform | SDL3 gamepad driver | Delegated gamepad handling to SDL library |
|
||||
| Platform | Android 16KB page support | Required for Google Play targeting Android 15+ |
|
||||
|
||||
## 4.3 → 4.4 (Mid 2025 — NEAR CUTOFF, VERIFY)
|
||||
|
||||
| Subsystem | Change | Details |
|
||||
|-----------|--------|---------|
|
||||
| Core | `FileAccess.store_*` return `bool` | Was `void`. Methods: `store_8`, `store_16`, `store_32`, `store_64`, `store_buffer`, `store_csv_line`, `store_double`, `store_float`, `store_half`, `store_line`, `store_pascal_string`, `store_real`, `store_string`, `store_var` |
|
||||
| Core | `OS.execute_with_pipe` | Added optional `blocking` parameter |
|
||||
| Core | `RegEx.compile/create_from_string` | Added optional `show_error` parameter |
|
||||
| Rendering | `RenderingDevice.draw_list_begin` | Many parameters removed; `breadcrumb` parameter added |
|
||||
| Rendering | Shader texture types | Parameter/return types changed from `Texture2D` to `Texture` |
|
||||
| Particles | `.restart()` method | Added optional `keep_seed` parameter (CPU/GPU 2D/3D) |
|
||||
| GUI | `RichTextLabel.push_meta` | Added optional `tooltip` parameter |
|
||||
| GUI | `GraphEdit.connect_node` | Added optional `keep_alive` parameter |
|
||||
|
||||
## 4.2 → 4.3 (In Training Data — LOW RISK)
|
||||
|
||||
| Subsystem | Change | Details |
|
||||
|-----------|--------|---------|
|
||||
| Animation | `Skeleton3D.add_bone` returns `int32` | Was `void` |
|
||||
| Animation | `bone_pose_updated` signal | Replaced by `skeleton_updated` |
|
||||
| TileMap | `TileMapLayer` replaces `TileMap` | One node per layer instead of multi-layer single node |
|
||||
| Navigation | `NavigationRegion2D` | Removed `avoidance_layers`, `constrain_avoidance` properties |
|
||||
| Editor | `EditorSceneFormatImporterFBX` | Renamed to `EditorSceneFormatImporterFBX2GLTF` |
|
||||
| Animation | AnimationMixer base class | AnimationPlayer and AnimationTree now extend AnimationMixer |
|
||||
107
docs/engine-reference/godot/current-best-practices.md
Normal file
107
docs/engine-reference/godot/current-best-practices.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Godot — Current Best Practices
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
Practices that are **new or changed** since the model's training data (~4.3).
|
||||
This supplements (not replaces) the agent's built-in knowledge.
|
||||
|
||||
## GDScript (4.5+)
|
||||
|
||||
- **Variadic arguments**: Functions can accept arbitrary parameter counts
|
||||
```gdscript
|
||||
func log_values(prefix: String, values: Variant...) -> void:
|
||||
for v in values:
|
||||
print(prefix, ": ", v)
|
||||
```
|
||||
|
||||
- **Abstract classes and methods**: Use `@abstract` to enforce inheritance
|
||||
```gdscript
|
||||
@abstract
|
||||
class_name BaseEnemy extends CharacterBody3D
|
||||
|
||||
@abstract
|
||||
func get_attack_pattern() -> Array[Attack]:
|
||||
pass # Subclasses MUST override
|
||||
```
|
||||
|
||||
- **Script backtracing**: Detailed call stacks available even in Release builds
|
||||
|
||||
## Physics (4.6)
|
||||
|
||||
- **Jolt Physics is the default 3D engine** for new projects
|
||||
- Better determinism and stability than GodotPhysics3D
|
||||
- Some HingeJoint3D properties (`damp`) only work with GodotPhysics
|
||||
- Switch: Project Settings → Physics → 3D → Physics Engine
|
||||
- 2D physics unchanged (still Godot Physics 2D)
|
||||
|
||||
## Rendering (4.6)
|
||||
|
||||
- **D3D12 is the default backend on Windows** (was Vulkan) — for better driver compatibility
|
||||
- **Glow now processes before tonemapping** with screen blending mode — existing glow setups may look different
|
||||
- **SSR overhauled** — significant improvement in realism, stability, and performance
|
||||
- **AgX tonemapper** — new white point and contrast controls
|
||||
|
||||
## Rendering (4.5)
|
||||
|
||||
- **Shader Baker**: Pre-compile shaders to eliminate startup hitching
|
||||
- **SMAA 1x**: New AA option — sharper than FXAA, cheaper than TAA
|
||||
- **Stencil buffer**: Available for advanced masking/portal effects
|
||||
- **Bent normal maps**: Directional occlusion in normal map textures
|
||||
- **Specular occlusion**: Ambient occlusion now affects reflections
|
||||
|
||||
## Accessibility (4.5+)
|
||||
|
||||
- **Screen reader support**: Control nodes integrate with accessibility tools via AccessKit
|
||||
- **Live translation preview**: Test GUI layouts in different languages directly in-editor
|
||||
- **FoldableContainer**: New accordion-style UI node for collapsible sections
|
||||
- **Recursive Control disable**: Disable mouse/focus interactions for entire node hierarchies with a single property
|
||||
|
||||
## Animation (4.5+)
|
||||
|
||||
- **BoneConstraint3D**: Bind bones to other bones with modifiers
|
||||
- AimModifier3D, CopyTransformModifier3D, ConvertTransformModifier3D
|
||||
|
||||
## Animation (4.6)
|
||||
|
||||
- **IK system fully restored**: Complete inverse kinematics reintroduced for 3D
|
||||
- Available modifiers: CCDIK, FABRIK, Jacobian IK, Spline IK, TwoBoneIK
|
||||
- Applied via `SkeletonModifier3D` nodes
|
||||
|
||||
## Resources (4.5+)
|
||||
|
||||
- **`duplicate_deep()`**: Explicit deep duplication for nested resource trees
|
||||
- Old `duplicate()` behavior retained for backward compatibility
|
||||
- Use `duplicate_deep()` when you need per-instance copies of nested resources
|
||||
|
||||
## Navigation (4.5+)
|
||||
|
||||
- **Dedicated 2D navigation server**: No longer proxied through 3D NavigationServer
|
||||
- Reduces export binary size for 2D-only games
|
||||
|
||||
## UI (4.6)
|
||||
|
||||
- **Dual-focus system**: Mouse/touch focus is now separate from keyboard/gamepad focus
|
||||
- Visual feedback differs depending on input method
|
||||
- Consider this when designing custom focus behavior
|
||||
|
||||
## Editor Workflow (4.6)
|
||||
|
||||
- Flexible dock drag-and-drop with blue outline preview (including bottom panel)
|
||||
- Most panels support floating windows (except Debugger)
|
||||
- New keyboard shortcuts: Alt+O (Output), Alt+S (Shader)
|
||||
- Export variable auto-generation: drag resource from FileSystem into script editor
|
||||
- Live preview in Quick Open dialog when "Live Preview" enabled
|
||||
- New "Select Mode" (v key) prevents accidental transforms; old mode renamed "Transform Mode" (q key)
|
||||
|
||||
## Tooling
|
||||
|
||||
- **ripgrep has no `gdscript` type**: `*.gd` is registered under `gap` (GAP programming language).
|
||||
`rg --type gdscript` is a hard error — the search never executes.
|
||||
Always use `rg --glob "*.gd"` (shell) or `glob: "*.gd"` (Grep tool) to filter GDScript files.
|
||||
|
||||
## Platform (4.5+)
|
||||
|
||||
- **visionOS export**: First new platform since open-sourcing (windowed app mode)
|
||||
- **SDL3 gamepad driver**: Better cross-platform gamepad support
|
||||
- **Android**: Edge-to-edge display, camera feed access, 16KB page support (Android 15+)
|
||||
- **Linux**: Wayland subwindow support for multi-window capability
|
||||
43
docs/engine-reference/godot/deprecated-apis.md
Normal file
43
docs/engine-reference/godot/deprecated-apis.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Godot — Deprecated APIs
|
||||
|
||||
Last verified: 2026-02-12
|
||||
|
||||
If an agent suggests any API in the "Deprecated" column, it MUST be replaced
|
||||
with the "Use Instead" column.
|
||||
|
||||
## Nodes & Classes
|
||||
|
||||
| Deprecated | Use Instead | Since | Notes |
|
||||
|------------|-------------|-------|-------|
|
||||
| `TileMap` | `TileMapLayer` | 4.3 | One node per layer instead of multi-layer node |
|
||||
| `VisibilityNotifier2D` | `VisibleOnScreenNotifier2D` | 4.0 | Renamed for clarity |
|
||||
| `VisibilityNotifier3D` | `VisibleOnScreenNotifier3D` | 4.0 | Renamed for clarity |
|
||||
| `YSort` | `Node2D.y_sort_enabled` | 4.0 | Property on Node2D, not a separate node |
|
||||
| `Navigation2D` / `Navigation3D` | `NavigationServer2D` / `NavigationServer3D` | 4.0 | Server-based API |
|
||||
| `EditorSceneFormatImporterFBX` | `EditorSceneFormatImporterFBX2GLTF` | 4.3 | Renamed |
|
||||
|
||||
## Methods & Properties
|
||||
|
||||
| Deprecated | Use Instead | Since | Notes |
|
||||
|------------|-------------|-------|-------|
|
||||
| `yield()` | `await signal` | 4.0 | GDScript 2.0 coroutine syntax |
|
||||
| `connect("signal", obj, "method")` | `signal.connect(callable)` | 4.0 | Callable-based connections |
|
||||
| `instance()` | `instantiate()` | 4.0 | Renamed |
|
||||
| `PackedScene.instance()` | `PackedScene.instantiate()` | 4.0 | Renamed |
|
||||
| `get_world()` | `get_world_3d()` | 4.0 | Explicit 2D/3D split |
|
||||
| `OS.get_ticks_msec()` | `Time.get_ticks_msec()` | 4.0 | Time singleton preferred |
|
||||
| `duplicate()` for nested resources | `duplicate_deep()` | 4.5 | Explicit deep copy control |
|
||||
| `Skeleton3D` signal `bone_pose_updated` | `skeleton_updated` | 4.3 | Renamed |
|
||||
| `AnimationPlayer.method_call_mode` | `AnimationMixer.callback_mode_method` | 4.3 | Moved to base class |
|
||||
| `AnimationPlayer.playback_active` | `AnimationMixer.active` | 4.3 | Moved to base class |
|
||||
|
||||
## Patterns (Not Just APIs)
|
||||
|
||||
| Deprecated Pattern | Use Instead | Why |
|
||||
|--------------------|-------------|-----|
|
||||
| String-based `connect()` | Typed signal connections | Type-safe, refactor-friendly |
|
||||
| `$NodePath` in `_process()` | `@onready var` cached reference | Performance: path lookup every frame |
|
||||
| Untyped `Array` / `Dictionary` | `Array[Type]`, typed variables | GDScript compiler optimizations |
|
||||
| `Texture2D` in shader parameters | `Texture` base type | Changed in 4.4 |
|
||||
| Manual post-process viewport chains | `Compositor` + `CompositorEffect` | Structured post-processing (4.3+) |
|
||||
| GodotPhysics3D for new projects | Jolt Physics 3D | Default since 4.6; better stability |
|
||||
76
docs/engine-reference/godot/modules/animation.md
Normal file
76
docs/engine-reference/godot/modules/animation.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Godot Animation — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **IK system fully restored**: Complete inverse kinematics for 3D skeletons
|
||||
- CCDIK, FABRIK, Jacobian IK, Spline IK, TwoBoneIK
|
||||
- Applied via `SkeletonModifier3D` nodes (not the old IK approach)
|
||||
- **Animation editor QoL**: Solo/hide/lock/delete for Bezier node groups; draggable timeline
|
||||
|
||||
### 4.5 Changes
|
||||
- **BoneConstraint3D**: Bind bones to other bones with modifiers
|
||||
- `AimModifier3D`, `CopyTransformModifier3D`, `ConvertTransformModifier3D`
|
||||
|
||||
### 4.3 Changes (in training data)
|
||||
- **AnimationMixer**: Base class for both AnimationPlayer and AnimationTree
|
||||
- `method_call_mode` → `callback_mode_method`
|
||||
- `playback_active` → `active`
|
||||
- `bone_pose_updated` signal → `skeleton_updated`
|
||||
- **`Skeleton3D.add_bone()`**: Now returns `int32` (was `void`)
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### AnimationPlayer (unchanged API, new base class)
|
||||
```gdscript
|
||||
@onready var anim_player: AnimationPlayer = %AnimationPlayer
|
||||
|
||||
func play_attack() -> void:
|
||||
anim_player.play(&"attack")
|
||||
await anim_player.animation_finished
|
||||
```
|
||||
|
||||
### IK Setup (4.6 — NEW)
|
||||
```gdscript
|
||||
# Add SkeletonModifier3D-based IK nodes as children of Skeleton3D
|
||||
# Available types:
|
||||
# - SkeletonModifier3D (base)
|
||||
# - TwoBoneIK (arms, legs)
|
||||
# - FABRIK (chains, tentacles)
|
||||
# - CCDIK (tails, spines)
|
||||
# - Jacobian IK (complex multi-joint)
|
||||
# - Spline IK (along curves)
|
||||
|
||||
# Configure in editor or code:
|
||||
# 1. Add IK modifier node as child of Skeleton3D
|
||||
# 2. Set target bone and tip bone
|
||||
# 3. Add a Marker3D as the IK target
|
||||
# 4. IK solver runs automatically each frame
|
||||
```
|
||||
|
||||
### BoneConstraint3D (4.5 — NEW)
|
||||
```gdscript
|
||||
# Add as child of Skeleton3D
|
||||
# Types:
|
||||
# - AimModifier3D: Point bone at target
|
||||
# - CopyTransformModifier3D: Mirror another bone's transform
|
||||
# - ConvertTransformModifier3D: Remap transform values
|
||||
```
|
||||
|
||||
### AnimationTree (base class changed in 4.3)
|
||||
```gdscript
|
||||
# AnimationTree now extends AnimationMixer (not Node directly)
|
||||
# Use AnimationMixer properties:
|
||||
@onready var anim_tree: AnimationTree = %AnimationTree
|
||||
|
||||
func _ready() -> void:
|
||||
anim_tree.active = true # NOT playback_active (deprecated 4.3)
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Using `playback_active` instead of `active` (deprecated since 4.3)
|
||||
- Using `bone_pose_updated` signal instead of `skeleton_updated` (renamed in 4.3)
|
||||
- Using old IK approach instead of SkeletonModifier3D system (restored in 4.6)
|
||||
- Not checking `is AnimationMixer` when type-checking animation nodes
|
||||
79
docs/engine-reference/godot/modules/audio.md
Normal file
79
docs/engine-reference/godot/modules/audio.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Godot Audio — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
No major breaking changes to the audio API in 4.4–4.6. The core audio system
|
||||
remains stable. Key updates are workflow improvements:
|
||||
|
||||
### 4.6 Changes
|
||||
- **No audio-specific breaking changes** in this release
|
||||
|
||||
### 4.5 Changes
|
||||
- **No audio-specific breaking changes** in this release
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### Playing Audio
|
||||
```gdscript
|
||||
@onready var sfx_player: AudioStreamPlayer = %SFXPlayer
|
||||
@onready var music_player: AudioStreamPlayer = %MusicPlayer
|
||||
|
||||
func play_sfx(stream: AudioStream) -> void:
|
||||
sfx_player.stream = stream
|
||||
sfx_player.play()
|
||||
|
||||
func play_music(stream: AudioStream, fade_time: float = 1.0) -> void:
|
||||
var tween: Tween = create_tween()
|
||||
tween.tween_property(music_player, "volume_db", -80.0, fade_time)
|
||||
await tween.finished
|
||||
music_player.stream = stream
|
||||
music_player.volume_db = 0.0
|
||||
music_player.play()
|
||||
```
|
||||
|
||||
### 3D Spatial Audio
|
||||
```gdscript
|
||||
@onready var audio_3d: AudioStreamPlayer3D = %AudioPlayer3D
|
||||
|
||||
func _ready() -> void:
|
||||
audio_3d.max_distance = 50.0
|
||||
audio_3d.attenuation_model = AudioStreamPlayer3D.ATTENUATION_INVERSE_DISTANCE
|
||||
audio_3d.unit_size = 10.0
|
||||
```
|
||||
|
||||
### Audio Buses
|
||||
```gdscript
|
||||
# Set bus volumes
|
||||
AudioServer.set_bus_volume_db(AudioServer.get_bus_index(&"Music"), volume_db)
|
||||
AudioServer.set_bus_volume_db(AudioServer.get_bus_index(&"SFX"), volume_db)
|
||||
|
||||
# Mute a bus
|
||||
AudioServer.set_bus_mute(AudioServer.get_bus_index(&"Music"), true)
|
||||
```
|
||||
|
||||
### Object Pooling for SFX
|
||||
```gdscript
|
||||
# Pre-create multiple AudioStreamPlayer nodes for concurrent sounds
|
||||
var _sfx_pool: Array[AudioStreamPlayer] = []
|
||||
|
||||
func _ready() -> void:
|
||||
for i in range(8):
|
||||
var player := AudioStreamPlayer.new()
|
||||
player.bus = &"SFX"
|
||||
add_child(player)
|
||||
_sfx_pool.append(player)
|
||||
|
||||
func play_pooled(stream: AudioStream) -> void:
|
||||
for player in _sfx_pool:
|
||||
if not player.playing:
|
||||
player.stream = stream
|
||||
player.play()
|
||||
return
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Creating new AudioStreamPlayer nodes at runtime instead of pooling
|
||||
- Not using audio buses for volume categories (Music, SFX, UI, Voice)
|
||||
- Using `_process()` for audio timing instead of signals (`finished`)
|
||||
72
docs/engine-reference/godot/modules/input.md
Normal file
72
docs/engine-reference/godot/modules/input.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Godot Input — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **Dual-focus system**: Mouse/touch focus is now separate from keyboard/gamepad focus
|
||||
- Visual feedback differs by input method
|
||||
- Custom focus implementations may need updating
|
||||
- **Select Mode keybind changed**: "Select Mode" is now `v` key; old mode renamed "Transform Mode" (`q` key)
|
||||
|
||||
### 4.5 Changes
|
||||
- **SDL3 gamepad driver**: Gamepad handling delegated to SDL library for better cross-platform support
|
||||
- **Recursive Control disable**: Single property disables mouse/focus for entire node hierarchies
|
||||
|
||||
### 4.3 Changes (in training data)
|
||||
- **InputEventShortcut**: Dedicated event type for menu shortcuts (optional)
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### Input Actions (unchanged)
|
||||
```gdscript
|
||||
func _physics_process(delta: float) -> void:
|
||||
var input_dir: Vector2 = Input.get_vector(
|
||||
&"move_left", &"move_right", &"move_forward", &"move_back"
|
||||
)
|
||||
if Input.is_action_just_pressed(&"jump"):
|
||||
jump()
|
||||
```
|
||||
|
||||
### Input Events (unchanged)
|
||||
```gdscript
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||
handle_click(event.position)
|
||||
elif event is InputEventKey:
|
||||
if event.keycode == KEY_ESCAPE and event.pressed:
|
||||
toggle_pause()
|
||||
```
|
||||
|
||||
### Focus Management (4.6 — CHANGED)
|
||||
```gdscript
|
||||
# Mouse/touch and keyboard/gamepad focus are now SEPARATE
|
||||
# Visual styles may differ depending on which input method is active
|
||||
# If you have custom focus drawing, test with both input methods
|
||||
|
||||
# Standard approach still works:
|
||||
func _ready() -> void:
|
||||
%StartButton.grab_focus() # Keyboard/gamepad focus
|
||||
|
||||
# But be aware: mouse hover focus != keyboard focus in 4.6
|
||||
```
|
||||
|
||||
### Gamepad (4.5+ — SDL3 backend)
|
||||
```gdscript
|
||||
# API unchanged, but SDL3 provides:
|
||||
# - Better device detection across platforms
|
||||
# - Improved rumble support
|
||||
# - More consistent button mapping
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventJoypadButton:
|
||||
if event.button_index == JOY_BUTTON_A and event.pressed:
|
||||
confirm_selection()
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Not testing both mouse and keyboard focus paths (dual-focus in 4.6)
|
||||
- Assuming `grab_focus()` affects mouse focus (it only affects keyboard/gamepad in 4.6)
|
||||
- Using string literals instead of `StringName` (`&"action"`) for action names in hot paths
|
||||
101
docs/engine-reference/godot/modules/navigation.md
Normal file
101
docs/engine-reference/godot/modules/navigation.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Godot Navigation — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.5 Changes
|
||||
- **Dedicated 2D navigation server**: No longer a proxy to 3D NavigationServer
|
||||
- Reduces export binary size for 2D-only games
|
||||
- API remains the same for both 2D and 3D
|
||||
|
||||
### 4.3 Changes (in training data)
|
||||
- **`NavigationRegion2D`**: Removed `avoidance_layers` and `constrain_avoidance` properties
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### NavigationAgent3D (Preferred for Most Cases)
|
||||
```gdscript
|
||||
@onready var nav_agent: NavigationAgent3D = %NavigationAgent3D
|
||||
|
||||
func _ready() -> void:
|
||||
nav_agent.path_desired_distance = 0.5
|
||||
nav_agent.target_desired_distance = 1.0
|
||||
nav_agent.velocity_computed.connect(_on_velocity_computed)
|
||||
|
||||
func navigate_to(target: Vector3) -> void:
|
||||
nav_agent.target_position = target
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if nav_agent.is_navigation_finished():
|
||||
return
|
||||
var next_pos: Vector3 = nav_agent.get_next_path_position()
|
||||
var direction: Vector3 = global_position.direction_to(next_pos)
|
||||
nav_agent.velocity = direction * move_speed
|
||||
|
||||
func _on_velocity_computed(safe_velocity: Vector3) -> void:
|
||||
velocity = safe_velocity
|
||||
move_and_slide()
|
||||
```
|
||||
|
||||
### NavigationAgent2D
|
||||
```gdscript
|
||||
@onready var nav_agent: NavigationAgent2D = %NavigationAgent2D
|
||||
|
||||
func navigate_to(target: Vector2) -> void:
|
||||
nav_agent.target_position = target
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if nav_agent.is_navigation_finished():
|
||||
return
|
||||
var next_pos: Vector2 = nav_agent.get_next_path_position()
|
||||
var direction: Vector2 = global_position.direction_to(next_pos)
|
||||
velocity = direction * move_speed
|
||||
move_and_slide()
|
||||
```
|
||||
|
||||
### Low-Level Path Query (3D)
|
||||
```gdscript
|
||||
# Direct server query for custom pathfinding logic
|
||||
var query := NavigationPathQueryParameters3D.new()
|
||||
query.map = get_world_3d().navigation_map
|
||||
query.start_position = global_position
|
||||
query.target_position = target_pos
|
||||
query.navigation_layers = navigation_layers
|
||||
|
||||
var result := NavigationPathQueryResult3D.new()
|
||||
NavigationServer3D.query_path(query, result)
|
||||
var path: PackedVector3Array = result.path
|
||||
```
|
||||
|
||||
### Avoidance
|
||||
```gdscript
|
||||
# Enable RVO2-based local avoidance
|
||||
nav_agent.avoidance_enabled = true
|
||||
nav_agent.radius = 0.5
|
||||
nav_agent.max_speed = move_speed
|
||||
nav_agent.neighbor_distance = 10.0
|
||||
|
||||
# Use velocity_computed signal for avoidance-safe movement
|
||||
nav_agent.velocity_computed.connect(_on_velocity_computed)
|
||||
|
||||
# Set velocity each frame (avoidance needs this)
|
||||
nav_agent.velocity = desired_velocity
|
||||
```
|
||||
|
||||
### Navigation Layers
|
||||
```gdscript
|
||||
# Use layers to separate walkable areas by agent type
|
||||
# Layer 1: Ground units
|
||||
# Layer 2: Flying units
|
||||
# Layer 3: Swimming units
|
||||
nav_agent.navigation_layers = 1 # Ground only
|
||||
nav_agent.navigation_layers = 1 | 2 # Ground + Flying
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Calling `get_next_path_position()` without checking `is_navigation_finished()`
|
||||
- Not setting `velocity` on the agent when avoidance is enabled (required for RVO2)
|
||||
- Using `NavigationRegion2D.avoidance_layers` (removed in 4.3)
|
||||
- Forgetting to bake navigation mesh after modifying geometry
|
||||
- Not setting `navigation_layers` (defaults to all layers)
|
||||
76
docs/engine-reference/godot/modules/networking.md
Normal file
76
docs/engine-reference/godot/modules/networking.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Godot Networking — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **Networking section in breaking changes**: See the official migration guide for
|
||||
specifics at the 4.5→4.6 level
|
||||
|
||||
### 4.5 Changes
|
||||
- **No major networking API breaks** — core multiplayer API remains stable
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### High-Level Multiplayer
|
||||
```gdscript
|
||||
# Server
|
||||
func host_game(port: int = 9999) -> void:
|
||||
var peer := ENetMultiplayerPeer.new()
|
||||
peer.create_server(port)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
multiplayer.peer_connected.connect(_on_peer_connected)
|
||||
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
|
||||
|
||||
# Client
|
||||
func join_game(address: String, port: int = 9999) -> void:
|
||||
var peer := ENetMultiplayerPeer.new()
|
||||
peer.create_client(address, port)
|
||||
multiplayer.multiplayer_peer = peer
|
||||
```
|
||||
|
||||
### RPCs
|
||||
```gdscript
|
||||
# Server-authoritative pattern
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func request_action(action_data: Dictionary) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
# Validate on server, then broadcast
|
||||
_execute_action.rpc(action_data)
|
||||
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _execute_action(action_data: Dictionary) -> void:
|
||||
# All peers execute the validated action
|
||||
pass
|
||||
```
|
||||
|
||||
### MultiplayerSpawner and MultiplayerSynchronizer
|
||||
```gdscript
|
||||
# Use MultiplayerSpawner for automatic node replication
|
||||
# Use MultiplayerSynchronizer for property synchronization
|
||||
|
||||
# MultiplayerSynchronizer setup:
|
||||
# 1. Add as child of the node to sync
|
||||
# 2. Configure replication properties in editor
|
||||
# 3. Set visibility filters for relevancy
|
||||
```
|
||||
|
||||
### SceneMultiplayer Configuration
|
||||
```gdscript
|
||||
func _ready() -> void:
|
||||
var scene_mp := multiplayer as SceneMultiplayer
|
||||
scene_mp.auth_callback = _authenticate_peer
|
||||
scene_mp.server_relay = false # Direct peer connections
|
||||
|
||||
func _authenticate_peer(id: int, data: PackedByteArray) -> void:
|
||||
# Custom authentication logic
|
||||
pass
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Not using `"any_peer"` for client-to-server RPCs (defaults to authority only)
|
||||
- Trusting client data without server-side validation
|
||||
- Using `"unreliable"` for game state changes (use for position updates only)
|
||||
- Not setting multiplayer authority (`set_multiplayer_authority()`) on spawned nodes
|
||||
76
docs/engine-reference/godot/modules/physics.md
Normal file
76
docs/engine-reference/godot/modules/physics.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Godot Physics — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **Jolt Physics is the DEFAULT 3D engine** for new projects
|
||||
- Existing projects keep their current physics engine setting
|
||||
- Better determinism, stability, and performance than GodotPhysics3D
|
||||
- Some HingeJoint3D properties (`damp`) only work with GodotPhysics3D
|
||||
- 2D physics UNCHANGED (still Godot Physics 2D)
|
||||
|
||||
### 4.5 Changes
|
||||
- **3D physics interpolation rearchitected**: Moved from RenderingServer to SceneTree
|
||||
- User-facing API unchanged, but internal behavior may differ in edge cases
|
||||
|
||||
## Physics Engine Selection (4.6)
|
||||
|
||||
```
|
||||
Project Settings → Physics → 3D → Physics Engine:
|
||||
- Jolt Physics (DEFAULT for new projects)
|
||||
- GodotPhysics3D (legacy, still available)
|
||||
```
|
||||
|
||||
### Jolt vs GodotPhysics3D
|
||||
|
||||
| Feature | Jolt (default) | GodotPhysics3D |
|
||||
|---------|---------------|----------------|
|
||||
| Determinism | Better | Inconsistent |
|
||||
| Stability | Better | Adequate |
|
||||
| Performance | Better for complex scenes | Adequate |
|
||||
| HingeJoint3D `damp` | NOT supported | Supported |
|
||||
| Runtime warnings | Yes, for unsupported properties | No |
|
||||
| Collision margins | May behave differently | Original behavior |
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### Basic Physics Setup (unchanged)
|
||||
```gdscript
|
||||
# CharacterBody3D movement — API unchanged across engines
|
||||
extends CharacterBody3D
|
||||
|
||||
@export var speed: float = 5.0
|
||||
@export var jump_velocity: float = 4.5
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not is_on_floor():
|
||||
velocity += get_gravity() * delta
|
||||
|
||||
if Input.is_action_just_pressed("jump") and is_on_floor():
|
||||
velocity.y = jump_velocity
|
||||
|
||||
var input_dir: Vector2 = Input.get_vector("left", "right", "forward", "back")
|
||||
var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
|
||||
move_and_slide()
|
||||
```
|
||||
|
||||
### Raycasting (unchanged)
|
||||
```gdscript
|
||||
var space_state: PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
|
||||
var query := PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collision_mask = collision_mask
|
||||
var result: Dictionary = space_state.intersect_ray(query)
|
||||
if result:
|
||||
var hit_point: Vector3 = result.position
|
||||
var hit_normal: Vector3 = result.normal
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Assuming GodotPhysics3D is the default (Jolt since 4.6)
|
||||
- Using HingeJoint3D `damp` property without checking physics engine (Jolt ignores it)
|
||||
- Not testing collision edge cases when switching between physics engines
|
||||
59
docs/engine-reference/godot/modules/rendering.md
Normal file
59
docs/engine-reference/godot/modules/rendering.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Godot Rendering — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **D3D12 is the default rendering backend on Windows** (was Vulkan)
|
||||
- **Glow processes before tonemapping** (was after) — uses screen blending mode
|
||||
- **AgX tonemapper**: new white point and contrast controls
|
||||
- **SSR overhauled**: better realism, visual stability, and performance
|
||||
|
||||
### 4.5 Changes
|
||||
- **Shader Baker**: Pre-compiles shaders to reduce startup time
|
||||
- **SMAA 1x**: New anti-aliasing option (sharper than FXAA, cheaper than TAA)
|
||||
- **Stencil buffer support**: Enables selective geometry masking/portal effects
|
||||
- **Bent normal maps**: Directional occlusion encoded in normal map textures
|
||||
- **Specular occlusion**: Ambient occlusion now correctly affects reflections
|
||||
|
||||
### 4.4 Changes
|
||||
- **`RenderingDevice.draw_list_begin`**: Many parameters removed; optional `breadcrumb` added
|
||||
- **Shader texture types**: Changed from `Texture2D` to `Texture` base type
|
||||
- **Particles `.restart()`**: Added optional `keep_seed` parameter
|
||||
|
||||
### 4.3 Changes (in training data)
|
||||
- **Compositor node**: `Compositor` + `CompositorEffect` for post-processing chains
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### Post-Processing (4.3+)
|
||||
```gdscript
|
||||
# Use Compositor node — NOT manual viewport shader chains
|
||||
# Add Compositor as child of WorldEnvironment or Camera3D
|
||||
# Create CompositorEffect resources for each post-process step
|
||||
```
|
||||
|
||||
### Anti-Aliasing Options (4.6)
|
||||
```
|
||||
Project Settings → Rendering → Anti Aliasing:
|
||||
- MSAA 2D/3D: Hardware MSAA (quality but expensive)
|
||||
- Screen Space AA: FXAA (fast, blurry) or SMAA (sharp, moderate cost) # SMAA new in 4.5
|
||||
- TAA: Temporal (best quality, ghosting on fast motion)
|
||||
```
|
||||
|
||||
### Rendering Backend Selection (4.6)
|
||||
```
|
||||
Project Settings → Rendering → Renderer:
|
||||
- Forward+ (default): Full featured, desktop-focused
|
||||
- Mobile: Optimized for mobile/low-end, limited features
|
||||
- Compatibility: OpenGL 3.3 / WebGL 2, broadest hardware support
|
||||
|
||||
Windows default backend: D3D12 (was Vulkan pre-4.6)
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Assuming Vulkan is the default backend on Windows (D3D12 since 4.6)
|
||||
- Using manual viewport chains instead of Compositor for post-processing
|
||||
- Using `Texture2D` in shader uniform types (use `Texture` since 4.4)
|
||||
- Not using Shader Baker for projects with many shader variants
|
||||
82
docs/engine-reference/godot/modules/ui.md
Normal file
82
docs/engine-reference/godot/modules/ui.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Godot UI — Quick Reference
|
||||
|
||||
Last verified: 2026-02-12 | Engine: Godot 4.6
|
||||
|
||||
## What Changed Since ~4.3 (LLM Cutoff)
|
||||
|
||||
### 4.6 Changes
|
||||
- **Dual-focus system**: Mouse/touch focus is now SEPARATE from keyboard/gamepad focus
|
||||
- Visual feedback differs by input method
|
||||
- Custom focus implementations may need updating
|
||||
- **TabContainer**: Tab properties editable directly in Inspector
|
||||
- **TileMapLayer scene tile rotation**: Scene tiles can be rotated like atlas tiles
|
||||
|
||||
### 4.5 Changes
|
||||
- **FoldableContainer**: New accordion-style UI node for collapsible sections
|
||||
- **Recursive Control behavior**: Disable mouse/focus for entire node hierarchies
|
||||
with a single property
|
||||
- **Screen reader support**: Control nodes work with AccessKit
|
||||
- **Live translation preview**: Test different locales in-editor
|
||||
- **`RichTextLabel.push_meta`**: Added optional `tooltip` parameter (from 4.4)
|
||||
|
||||
### 4.4 Changes
|
||||
- **`GraphEdit.connect_node`**: Added optional `keep_alive` parameter
|
||||
|
||||
## Current API Patterns
|
||||
|
||||
### Theme and Style (4.6)
|
||||
```gdscript
|
||||
# Editor uses new "Modern" theme by default
|
||||
# For game UI, use custom themes as before:
|
||||
var theme := Theme.new()
|
||||
theme.set_color(&"font_color", &"Label", Color.WHITE)
|
||||
theme.set_font_size(&"font_size", &"Label", 24)
|
||||
```
|
||||
|
||||
### Focus Management (4.6 — CHANGED)
|
||||
```gdscript
|
||||
# Keyboard/gamepad focus (grab_focus still works)
|
||||
func _ready() -> void:
|
||||
%StartButton.grab_focus()
|
||||
|
||||
# IMPORTANT: In 4.6, mouse hover is separate from keyboard focus
|
||||
# Both can be active simultaneously on different controls
|
||||
# Test your UI with BOTH mouse and keyboard/gamepad
|
||||
|
||||
# Focus neighbors (unchanged)
|
||||
%Button1.focus_neighbor_bottom = %Button2.get_path()
|
||||
%Button1.focus_neighbor_right = %Button3.get_path()
|
||||
```
|
||||
|
||||
### FoldableContainer (4.5 — NEW)
|
||||
```gdscript
|
||||
# Accordion-style collapsible container
|
||||
# Add as parent of content you want to make collapsible
|
||||
# Children show/hide when header is clicked
|
||||
# Configure via editor properties or code
|
||||
```
|
||||
|
||||
### Recursive Disable (4.5 — NEW)
|
||||
```gdscript
|
||||
# Disable all mouse/focus interactions for a hierarchy
|
||||
# Useful for disabling entire menu sections
|
||||
%SettingsPanel.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
# In 4.5+, this can propagate recursively to children
|
||||
```
|
||||
|
||||
### Localization-Ready UI (best practice)
|
||||
```gdscript
|
||||
# Use tr() for all visible strings
|
||||
label.text = tr("MENU_START_GAME")
|
||||
|
||||
# Use auto-wrap for labels (text length varies by language)
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
|
||||
# Test with live translation preview in editor (4.5+)
|
||||
```
|
||||
|
||||
## Common Mistakes
|
||||
- Assuming `grab_focus()` affects mouse focus (keyboard/gamepad only in 4.6)
|
||||
- Not testing UI with both mouse and gamepad after upgrading to 4.6
|
||||
- Hardcoding strings instead of using `tr()` for localization
|
||||
- Not using `FoldableContainer` for collapsible UI (new in 4.5, cleaner than custom)
|
||||
250
docs/engine-reference/unity/PLUGINS.md
Normal file
250
docs/engine-reference/unity/PLUGINS.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# Unity 6.3 LTS — Optional Packages & Systems
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
This document indexes **optional packages and systems** available in Unity 6.3 LTS.
|
||||
These are NOT part of the core engine but are commonly used for specific game types.
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Guide
|
||||
|
||||
**✅ Detailed Documentation Available** - See `plugins/` directory for comprehensive guides
|
||||
**🟡 Brief Overview Only** - Links to official docs, use WebSearch for details
|
||||
**⚠️ Preview** - May have breaking changes in future versions
|
||||
**📦 Package Required** - Install via Package Manager
|
||||
|
||||
---
|
||||
|
||||
## Production-Ready Packages (Detailed Docs Available)
|
||||
|
||||
### ✅ Cinemachine
|
||||
- **Purpose:** Virtual camera system (dynamic cameras, cutscenes, camera blending)
|
||||
- **When to use:** 3rd person games, cinematics, complex camera behavior
|
||||
- **Knowledge Gap:** Cinemachine 3.0+ (Unity 6) has major API changes vs 2.x
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.cinemachine` (Package Manager)
|
||||
- **Detailed Docs:** [plugins/cinemachine.md](plugins/cinemachine.md)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.cinemachine@3.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### ✅ Addressables
|
||||
- **Purpose:** Advanced asset management (async loading, remote content, memory control)
|
||||
- **When to use:** Large projects, DLC, remote content delivery
|
||||
- **Knowledge Gap:** Unity 6 improvements, better performance
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.addressables` (Package Manager)
|
||||
- **Detailed Docs:** [plugins/addressables.md](plugins/addressables.md)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.addressables@2.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### ✅ DOTS / Entities (ECS)
|
||||
- **Purpose:** Data-Oriented Technology Stack (high-performance ECS for massive scale)
|
||||
- **When to use:** Games with 1000s of entities, RTS, simulations
|
||||
- **Knowledge Gap:** Entities 1.3+ (Unity 6) is production-ready, major rewrite from 0.x
|
||||
- **Status:** Production-Ready (as of Unity 6.3 LTS)
|
||||
- **Package:** `com.unity.entities` (Package Manager)
|
||||
- **Detailed Docs:** [plugins/dots-entities.md](plugins/dots-entities.md)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
## Other Production-Ready Packages (Brief Overview)
|
||||
|
||||
### 🟡 Input System (Already Covered)
|
||||
- **Purpose:** Modern input handling (rebindable, cross-platform)
|
||||
- **Status:** Production-Ready (default in Unity 6)
|
||||
- **Package:** `com.unity.inputsystem`
|
||||
- **Docs:** See [modules/input.md](../modules/input.md)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 UI Toolkit (Already Covered)
|
||||
- **Purpose:** Modern runtime UI (HTML/CSS-like, performant)
|
||||
- **Status:** Production-Ready (Unity 6)
|
||||
- **Package:** Built-in
|
||||
- **Docs:** See [modules/ui.md](../modules/ui.md)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.ui@2.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Visual Effect Graph (VFX Graph)
|
||||
- **Purpose:** GPU-accelerated particle system (millions of particles)
|
||||
- **When to use:** Large-scale VFX, fire, smoke, magic, explosions
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.visualeffectgraph` (URP/HDRP only)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@17.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Shader Graph
|
||||
- **Purpose:** Visual shader editor (node-based shader creation)
|
||||
- **When to use:** Custom shaders without HLSL coding
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.shadergraph` (URP/HDRP)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.shadergraph@17.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Timeline
|
||||
- **Purpose:** Cinematic sequencing (cutscenes, scripted events)
|
||||
- **When to use:** Story-driven games, cinematics, scripted sequences
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.timeline` (built-in)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.timeline@1.8/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Animation Rigging
|
||||
- **Purpose:** Runtime IK, procedural animation
|
||||
- **When to use:** Foot IK, aim offsets, procedural limb placement
|
||||
- **Status:** Production-Ready (Unity 6)
|
||||
- **Package:** `com.unity.animation.rigging`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 ProBuilder
|
||||
- **Purpose:** In-editor 3D modeling (level prototyping, greyboxing)
|
||||
- **When to use:** Rapid prototyping, level blockout
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.probuilder`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.probuilder@6.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Netcode for GameObjects
|
||||
- **Purpose:** Official Unity multiplayer networking
|
||||
- **When to use:** Multiplayer games (client-server architecture)
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.netcode.gameobjects`
|
||||
- **Official:** https://docs-multiplayer.unity3d.com/netcode/current/about/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Burst Compiler
|
||||
- **Purpose:** LLVM-based compiler for C# Jobs (massive performance boost)
|
||||
- **When to use:** Performance-critical code, DOTS, Jobs System
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.burst` (auto-installed with DOTS)
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Jobs System
|
||||
- **Purpose:** Multi-threaded job scheduling (CPU parallelism)
|
||||
- **When to use:** Performance optimization, parallel processing
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** Built-in
|
||||
- **Official:** https://docs.unity3d.com/Manual/JobSystem.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Mathematics
|
||||
- **Purpose:** SIMD math library (optimized for Burst)
|
||||
- **When to use:** DOTS, high-performance math
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.mathematics`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.mathematics@1.3/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### 🟡 ML-Agents (Machine Learning)
|
||||
- **Purpose:** Train AI with reinforcement learning
|
||||
- **When to use:** Advanced AI training, procedural behavior
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.ml-agents`
|
||||
- **Official:** https://github.com/Unity-Technologies/ml-agents
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Recorder
|
||||
- **Purpose:** Capture gameplay footage, screenshots, animation clips
|
||||
- **When to use:** Trailers, replays, debug recording
|
||||
- **Status:** Production-Ready
|
||||
- **Package:** `com.unity.recorder`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.recorder@5.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
## Preview/Experimental Packages (Use with Caution)
|
||||
|
||||
### ⚠️ Splines
|
||||
- **Purpose:** Runtime spline creation and editing
|
||||
- **When to use:** Roads, paths, procedural content
|
||||
- **Status:** Production-Ready (Unity 6)
|
||||
- **Package:** `com.unity.splines`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.splines@2.6/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Muse (AI Assistant)
|
||||
- **Purpose:** AI-powered asset creation (textures, sprites, animations)
|
||||
- **Status:** Preview (Unity 6)
|
||||
- **Package:** `com.unity.muse.*`
|
||||
- **Official:** https://unity.com/products/muse
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Sentis (Neural Network Inference)
|
||||
- **Purpose:** Run neural networks in Unity (AI inference)
|
||||
- **Status:** Preview
|
||||
- **Package:** `com.unity.sentis`
|
||||
- **Official:** https://docs.unity3d.com/Packages/com.unity.sentis@2.0/manual/index.html
|
||||
|
||||
---
|
||||
|
||||
## Deprecated Packages (Avoid for New Projects)
|
||||
|
||||
### ❌ UGUI (Canvas UI)
|
||||
- **Deprecated:** Still supported, but UI Toolkit recommended
|
||||
- **Use Instead:** UI Toolkit
|
||||
|
||||
---
|
||||
|
||||
### ❌ Legacy Particle System
|
||||
- **Deprecated:** Use Visual Effect Graph (VFX Graph)
|
||||
- **Use Instead:** VFX Graph
|
||||
|
||||
---
|
||||
|
||||
### ❌ Legacy Animation
|
||||
- **Deprecated:** Use Animator (Mecanim)
|
||||
- **Use Instead:** Animator Controller
|
||||
|
||||
---
|
||||
|
||||
## On-Demand WebSearch Strategy
|
||||
|
||||
For packages NOT listed above, use the following approach when users ask:
|
||||
|
||||
1. **WebSearch** for latest documentation: `"Unity 6.3 [package name]"`
|
||||
2. Verify if package is:
|
||||
- Post-cutoff (beyond May 2025 training data)
|
||||
- Preview vs Production-Ready
|
||||
- Still supported in Unity 6.3 LTS
|
||||
3. Optionally cache findings in `plugins/[package-name].md` for future reference
|
||||
|
||||
---
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
**I need virtual cameras** → **Cinemachine**
|
||||
**I need async asset loading / DLC** → **Addressables**
|
||||
**I need 1000s of entities (RTS, sim)** → **DOTS/Entities**
|
||||
**I need modern input** → **Input System** (see modules/input.md)
|
||||
**I need GPU particles** → **Visual Effect Graph**
|
||||
**I need visual shaders** → **Shader Graph**
|
||||
**I need cinematics** → **Timeline**
|
||||
**I need runtime IK** → **Animation Rigging**
|
||||
**I need level prototyping** → **ProBuilder**
|
||||
**I need multiplayer** → **Netcode for GameObjects**
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-02-13
|
||||
**Engine Version:** Unity 6.3 LTS
|
||||
**LLM Knowledge Cutoff:** May 2025
|
||||
57
docs/engine-reference/unity/VERSION.md
Normal file
57
docs/engine-reference/unity/VERSION.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Unity Engine — Version Reference
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine Version** | Unity 6.3 LTS |
|
||||
| **Release Date** | December 2025 |
|
||||
| **Project Pinned** | 2026-02-13 |
|
||||
| **Last Docs Verified** | 2026-02-13 |
|
||||
| **LLM Knowledge Cutoff** | May 2025 |
|
||||
|
||||
## Knowledge Gap Warning
|
||||
|
||||
The LLM's training data likely covers Unity up to ~2022 LTS (2022.3). The entire
|
||||
Unity 6 release series (formerly Unity 2023 Tech Stream) introduced significant
|
||||
changes that the model does NOT know about. Always cross-reference this directory
|
||||
before suggesting Unity API calls.
|
||||
|
||||
## Post-Cutoff Version Timeline
|
||||
|
||||
| Version | Release | Risk Level | Key Theme |
|
||||
|---------|---------|------------|-----------|
|
||||
| 6.0 | Oct 2024 | HIGH | Unity 6 rebrand, new rendering features, Entities 1.3, DOTS improvements |
|
||||
| 6.1 | Nov 2024 | MEDIUM | Bug fixes, stability improvements |
|
||||
| 6.2 | Dec 2024 | MEDIUM | Performance optimizations, new input system improvements |
|
||||
| 6.3 LTS | Dec 2025 | HIGH | First LTS since 6.0, production-ready DOTS, enhanced graphics features |
|
||||
|
||||
## Major Changes from 2022 LTS to Unity 6.3 LTS
|
||||
|
||||
### Breaking Changes
|
||||
- **Entities/DOTS**: Major API overhaul in Entities 1.0+, complete redesign of ECS patterns
|
||||
- **Input System**: Legacy Input Manager deprecated, new Input System is default
|
||||
- **Rendering**: URP/HDRP significant upgrades, SRP Batcher improvements
|
||||
- **Addressables**: Asset management workflow changes
|
||||
- **Scripting**: C# 9 support, new API patterns
|
||||
|
||||
### New Features (Post-Cutoff)
|
||||
- **DOTS**: Production-ready Entity Component System (Entities 1.3+)
|
||||
- **Graphics**: Enhanced URP/HDRP pipelines, GPU Resident Drawer
|
||||
- **Multiplayer**: Netcode for GameObjects improvements
|
||||
- **UI Toolkit**: Production-ready for runtime UI (replaces UGUI for new projects)
|
||||
- **Async Asset Loading**: Improved Addressables performance
|
||||
- **Web**: WebGPU support
|
||||
|
||||
### Deprecated Systems
|
||||
- **Legacy Input Manager**: Use new Input System package
|
||||
- **Legacy Particle System**: Use Visual Effect Graph
|
||||
- **UGUI**: Still supported, but UI Toolkit recommended for new projects
|
||||
- **Old ECS (GameObjectEntity)**: Replaced by modern DOTS/Entities
|
||||
|
||||
## Verified Sources
|
||||
|
||||
- Official docs: https://docs.unity3d.com/6000.0/Documentation/Manual/index.html
|
||||
- Unity 6 release: https://unity.com/releases/unity-6
|
||||
- Unity 6.3 LTS announcement: https://unity.com/blog/unity-6-3-lts-is-now-available
|
||||
- Migration guide: https://docs.unity3d.com/6000.0/Documentation/Manual/upgrade-guides.html
|
||||
- Unity 6 support: https://unity.com/releases/unity-6/support
|
||||
- C# API reference: https://docs.unity3d.com/6000.0/Documentation/ScriptReference/index.html
|
||||
154
docs/engine-reference/unity/breaking-changes.md
Normal file
154
docs/engine-reference/unity/breaking-changes.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Unity 6.3 LTS — Breaking Changes
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
This document tracks breaking API changes and behavioral differences between Unity 2022 LTS
|
||||
(likely in model training) and Unity 6.3 LTS (current version). Organized by risk level.
|
||||
|
||||
## HIGH RISK — Will Break Existing Code
|
||||
|
||||
### Entities/DOTS API Complete Overhaul
|
||||
**Versions:** Entities 1.0+ (Unity 6.0+)
|
||||
|
||||
```csharp
|
||||
// ❌ OLD (pre-Unity 6, GameObjectEntity pattern)
|
||||
public class HealthComponent : ComponentData {
|
||||
public float Value;
|
||||
}
|
||||
|
||||
// ✅ NEW (Unity 6+, IComponentData)
|
||||
public struct HealthComponent : IComponentData {
|
||||
public float Value;
|
||||
}
|
||||
|
||||
// ❌ OLD: ComponentSystem
|
||||
public class DamageSystem : ComponentSystem { }
|
||||
|
||||
// ✅ NEW: ISystem (unmanaged, Burst-compatible)
|
||||
public partial struct DamageSystem : ISystem {
|
||||
public void OnCreate(ref SystemState state) { }
|
||||
public void OnUpdate(ref SystemState state) { }
|
||||
}
|
||||
```
|
||||
|
||||
**Migration:** Follow Unity's ECS migration guide. Major architectural changes required.
|
||||
|
||||
---
|
||||
|
||||
### Input System — Legacy Input Deprecated
|
||||
**Versions:** Unity 6.0+
|
||||
|
||||
```csharp
|
||||
// ❌ OLD: Input class (deprecated)
|
||||
if (Input.GetKeyDown(KeyCode.Space)) { }
|
||||
|
||||
// ✅ NEW: Input System package
|
||||
using UnityEngine.InputSystem;
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) { }
|
||||
```
|
||||
|
||||
**Migration:** Install Input System package, replace all `Input.*` calls with new API.
|
||||
|
||||
---
|
||||
|
||||
### URP/HDRP Renderer Feature API Changes
|
||||
**Versions:** Unity 6.0+
|
||||
|
||||
```csharp
|
||||
// ❌ OLD: ScriptableRenderPass.Execute signature
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
|
||||
|
||||
// ✅ NEW: Uses RenderGraph API
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
```
|
||||
|
||||
**Migration:** Update custom render passes to use RenderGraph API.
|
||||
|
||||
---
|
||||
|
||||
## MEDIUM RISK — Behavioral Changes
|
||||
|
||||
### Addressables — Asset Loading Returns
|
||||
**Versions:** Unity 6.2+
|
||||
|
||||
Asset loading failures now throw exceptions by default instead of returning null.
|
||||
Add proper exception handling or use `TryLoad` variants.
|
||||
|
||||
```csharp
|
||||
// ❌ OLD: Silent null on failure
|
||||
var handle = Addressables.LoadAssetAsync<Sprite>("key");
|
||||
var sprite = handle.Result; // null if failed
|
||||
|
||||
// ✅ NEW: Throws on failure, use try/catch or TryLoad
|
||||
try {
|
||||
var handle = Addressables.LoadAssetAsync<Sprite>("key");
|
||||
var sprite = await handle.Task;
|
||||
} catch (Exception e) {
|
||||
Debug.LogError($"Failed to load: {e}");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Physics — Default Solver Iterations Changed
|
||||
**Versions:** Unity 6.0+
|
||||
|
||||
Default solver iterations increased for better stability.
|
||||
Check `Physics.defaultSolverIterations` if you rely on old behavior.
|
||||
|
||||
---
|
||||
|
||||
## LOW RISK — Deprecations (Still Functional)
|
||||
|
||||
### UGUI (Legacy UI)
|
||||
**Status:** Deprecated but supported
|
||||
**Replacement:** UI Toolkit
|
||||
|
||||
UGUI still works but UI Toolkit is recommended for new projects.
|
||||
|
||||
---
|
||||
|
||||
### Legacy Particle System
|
||||
**Status:** Deprecated
|
||||
**Replacement:** Visual Effect Graph (VFX Graph)
|
||||
|
||||
---
|
||||
|
||||
### Old Animation System
|
||||
**Status:** Deprecated
|
||||
**Replacement:** Animator Controller (Mecanim)
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Breaking Changes
|
||||
|
||||
### WebGL
|
||||
- **Unity 6.0+**: WebGPU is now the default (WebGL 2.0 fallback available)
|
||||
- Update shaders for WebGPU compatibility
|
||||
|
||||
### Android
|
||||
- **Unity 6.0+**: Minimum API level raised to 24 (Android 7.0)
|
||||
|
||||
### iOS
|
||||
- **Unity 6.0+**: Minimum deployment target raised to iOS 13
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When upgrading from 2022 LTS to Unity 6.3 LTS:
|
||||
|
||||
- [ ] Audit all DOTS/ECS code (complete rewrite likely needed)
|
||||
- [ ] Replace `Input` class with Input System package
|
||||
- [ ] Update custom render passes to RenderGraph API
|
||||
- [ ] Add exception handling to Addressables calls
|
||||
- [ ] Test physics behavior (solver iterations changed)
|
||||
- [ ] Consider migrating UGUI to UI Toolkit for new UI
|
||||
- [ ] Update WebGL shaders for WebGPU
|
||||
- [ ] Verify minimum platform versions (Android/iOS)
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/upgrade-guides.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/upgrade-guide.html
|
||||
334
docs/engine-reference/unity/current-best-practices.md
Normal file
334
docs/engine-reference/unity/current-best-practices.md
Normal file
@@ -0,0 +1,334 @@
|
||||
# Unity 6.3 LTS — Current Best Practices
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
Modern Unity 6 patterns that may not be in the LLM's training data.
|
||||
These are production-ready recommendations as of Unity 6.3 LTS.
|
||||
|
||||
---
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Use Unity 6.3 LTS for Production
|
||||
- **Tech Stream** (6.4+): Latest features, less stable
|
||||
- **LTS** (6.3): Production-ready, 2-year support (until Dec 2027)
|
||||
|
||||
### Choose the Right Render Pipeline
|
||||
- **URP (Universal)**: Mobile, cross-platform, good performance ✅ Recommended for most games
|
||||
- **HDRP (High Definition)**: High-end PC/console, photorealistic
|
||||
- **Built-in**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Scripting
|
||||
|
||||
### Use C# 9+ Features (Unity 6 Supports C# 9)
|
||||
|
||||
```csharp
|
||||
// ✅ Record types for data
|
||||
public record PlayerData(string Name, int Level, float Health);
|
||||
|
||||
// ✅ Init-only properties
|
||||
public class Config {
|
||||
public string GameMode { get; init; }
|
||||
}
|
||||
|
||||
// ✅ Pattern matching
|
||||
var result = enemy switch {
|
||||
Boss boss => boss.Enrage(),
|
||||
Minion minion => minion.Flee(),
|
||||
_ => null
|
||||
};
|
||||
```
|
||||
|
||||
### Async/Await for Asset Loading
|
||||
|
||||
```csharp
|
||||
// ✅ Modern async pattern
|
||||
public async Task<GameObject> LoadEnemyAsync(string key) {
|
||||
var handle = Addressables.LoadAssetAsync<GameObject>(key);
|
||||
return await handle.Task;
|
||||
}
|
||||
```
|
||||
|
||||
### Use Source Generators for Serialization (Unity 6+)
|
||||
|
||||
```csharp
|
||||
// ✅ Source-generated serialization (faster, less reflection)
|
||||
[GenerateSerializer]
|
||||
public partial struct PlayerStats : IComponentData {
|
||||
public int Health;
|
||||
public int Mana;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DOTS/ECS (Production-Ready in Unity 6.3 LTS)
|
||||
|
||||
### Use ISystem (Not ComponentSystem)
|
||||
|
||||
```csharp
|
||||
// ✅ Modern unmanaged ISystem (Burst-compatible)
|
||||
public partial struct MovementSystem : ISystem {
|
||||
public void OnCreate(ref SystemState state) { }
|
||||
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
foreach (var (transform, speed) in
|
||||
SystemAPI.Query<RefRW<LocalTransform>, RefRO<MoveSpeed>>()) {
|
||||
transform.ValueRW.Position += speed.ValueRO.Value * SystemAPI.Time.DeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Use IJobEntity for Parallel Jobs
|
||||
|
||||
```csharp
|
||||
// ✅ IJobEntity (replaces IJobForEach)
|
||||
[BurstCompile]
|
||||
public partial struct DamageJob : IJobEntity {
|
||||
public float DeltaTime;
|
||||
|
||||
void Execute(ref Health health, in DamageOverTime dot) {
|
||||
health.Value -= dot.DamagePerSecond * DeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule it
|
||||
var job = new DamageJob { DeltaTime = SystemAPI.Time.DeltaTime };
|
||||
job.ScheduleParallel();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input
|
||||
|
||||
### Use Input System Package (Not Legacy Input)
|
||||
|
||||
```csharp
|
||||
// ✅ Input Actions (rebindable, cross-platform)
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class PlayerInput : MonoBehaviour {
|
||||
private PlayerControls controls;
|
||||
|
||||
void Awake() {
|
||||
controls = new PlayerControls();
|
||||
controls.Gameplay.Jump.performed += ctx => Jump();
|
||||
}
|
||||
|
||||
void OnEnable() => controls.Enable();
|
||||
void OnDisable() => controls.Disable();
|
||||
}
|
||||
```
|
||||
|
||||
Create Input Actions asset in editor, generate C# class via inspector.
|
||||
|
||||
---
|
||||
|
||||
## UI
|
||||
|
||||
### Use UI Toolkit for Runtime UI (Production-Ready in Unity 6)
|
||||
|
||||
```csharp
|
||||
// ✅ UI Toolkit (replaces UGUI for new projects)
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class MainMenu : MonoBehaviour {
|
||||
void OnEnable() {
|
||||
var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
|
||||
var playButton = root.Q<Button>("play-button");
|
||||
playButton.clicked += StartGame;
|
||||
|
||||
var scoreLabel = root.Q<Label>("score");
|
||||
scoreLabel.text = $"High Score: {PlayerPrefs.GetInt("HighScore")}";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**UXML** (UI structure) + **USS** (styling) = HTML/CSS-like workflow.
|
||||
|
||||
---
|
||||
|
||||
## Asset Management
|
||||
|
||||
### Use Addressables (Not Resources)
|
||||
|
||||
```csharp
|
||||
// ✅ Addressables (async, memory-efficient)
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
public async Task SpawnEnemyAsync(string enemyKey) {
|
||||
var handle = Addressables.InstantiateAsync(enemyKey);
|
||||
var enemy = await handle.Task;
|
||||
|
||||
// Cleanup: release when destroyed
|
||||
Addressables.ReleaseInstance(enemy);
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:** Async loading, remote content delivery, better memory control.
|
||||
|
||||
---
|
||||
|
||||
## Rendering
|
||||
|
||||
### Use RenderGraph API for Custom Passes (URP/HDRP)
|
||||
|
||||
```csharp
|
||||
// ✅ RenderGraph API (Unity 6+)
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
||||
using (var builder = renderGraph.AddRasterRenderPass<PassData>("My Pass", out var passData)) {
|
||||
// Setup pass
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext context) => {
|
||||
// Execute commands
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Replaces:** Old `CommandBuffer.Execute()` pattern.
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### Use Burst Compiler + Jobs System
|
||||
|
||||
```csharp
|
||||
// ✅ Burst-compiled job (massive performance gain)
|
||||
[BurstCompile]
|
||||
struct ParticleUpdateJob : IJobParallelFor {
|
||||
public NativeArray<float3> Positions;
|
||||
public NativeArray<float3> Velocities;
|
||||
public float DeltaTime;
|
||||
|
||||
public void Execute(int index) {
|
||||
Positions[index] += Velocities[index] * DeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule
|
||||
var job = new ParticleUpdateJob {
|
||||
Positions = positions,
|
||||
Velocities = velocities,
|
||||
DeltaTime = Time.deltaTime
|
||||
};
|
||||
job.Schedule(positions.Length, 64).Complete();
|
||||
```
|
||||
|
||||
**20-100x faster** than equivalent C# code.
|
||||
|
||||
---
|
||||
|
||||
### Use GPU Instancing for Repeated Objects
|
||||
|
||||
```csharp
|
||||
// ✅ GPU Instancing (thousands of objects, minimal draw calls)
|
||||
Graphics.RenderMeshInstanced(
|
||||
new RenderParams(material),
|
||||
mesh,
|
||||
0,
|
||||
matrices // NativeArray<Matrix4x4>
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Use NativeContainers (Not Managed Arrays in Jobs)
|
||||
|
||||
```csharp
|
||||
// ✅ NativeArray (no GC, Burst-compatible)
|
||||
NativeArray<int> data = new NativeArray<int>(1000, Allocator.TempJob);
|
||||
// ... use in job
|
||||
data.Dispose(); // Manual cleanup required
|
||||
|
||||
// ✅ Or use using statement
|
||||
using var data = new NativeArray<int>(1000, Allocator.TempJob);
|
||||
// Auto-disposed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Multiplayer
|
||||
|
||||
### Use Netcode for GameObjects (Official)
|
||||
|
||||
```csharp
|
||||
// ✅ Unity's official netcode
|
||||
using Unity.Netcode;
|
||||
|
||||
public class Player : NetworkBehaviour {
|
||||
private NetworkVariable<int> health = new NetworkVariable<int>(100);
|
||||
|
||||
[ServerRpc]
|
||||
public void TakeDamageServerRpc(int damage) {
|
||||
health.Value -= damage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Replaces:** UNet (deprecated), MLAPI (renamed to Netcode for GameObjects).
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Use Unity Test Framework (NUnit-based)
|
||||
|
||||
```csharp
|
||||
// ✅ Play Mode Test
|
||||
[UnityTest]
|
||||
public IEnumerator Player_TakesDamage_HealthDecreases() {
|
||||
var player = new GameObject().AddComponent<Player>();
|
||||
player.Health = 100;
|
||||
|
||||
player.TakeDamage(25);
|
||||
yield return null; // Wait one frame
|
||||
|
||||
Assert.AreEqual(75, player.Health);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Use Logging Best Practices
|
||||
|
||||
```csharp
|
||||
// ✅ Structured logging (Unity 6+)
|
||||
using UnityEngine;
|
||||
|
||||
Debug.Log($"Player {playerName} scored {score} points");
|
||||
|
||||
// ✅ Conditional compilation for debug code
|
||||
#if UNITY_EDITOR || DEVELOPMENT_BUILD
|
||||
Debug.DrawRay(transform.position, direction, Color.red);
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary: Unity 6 Tech Stack
|
||||
|
||||
| Feature | Use This (2026) | Avoid This (Legacy) |
|
||||
|---------|------------------|----------------------|
|
||||
| **Input** | Input System package | `Input` class |
|
||||
| **UI** | UI Toolkit | UGUI (Canvas) |
|
||||
| **ECS** | ISystem + IJobEntity | ComponentSystem |
|
||||
| **Rendering** | URP + RenderGraph | Built-in pipeline |
|
||||
| **Assets** | Addressables | Resources |
|
||||
| **Jobs** | Burst + IJobParallelFor | Coroutines for heavy work |
|
||||
| **Multiplayer** | Netcode for GameObjects | UNet |
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/BestPracticeGuides.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/index.html
|
||||
156
docs/engine-reference/unity/deprecated-apis.md
Normal file
156
docs/engine-reference/unity/deprecated-apis.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# Unity 6.3 LTS — Deprecated APIs
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
Quick lookup table for deprecated APIs and their replacements.
|
||||
Format: **Don't use X** → **Use Y instead**
|
||||
|
||||
---
|
||||
|
||||
## Input
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `Input.GetKey()` | `Keyboard.current[Key.X].isPressed` | New Input System |
|
||||
| `Input.GetKeyDown()` | `Keyboard.current[Key.X].wasPressedThisFrame` | New Input System |
|
||||
| `Input.GetMouseButton()` | `Mouse.current.leftButton.isPressed` | New Input System |
|
||||
| `Input.GetAxis()` | `InputAction` callbacks | New Input System |
|
||||
| `Input.mousePosition` | `Mouse.current.position.ReadValue()` | New Input System |
|
||||
|
||||
**Migration:** Install `com.unity.inputsystem` package.
|
||||
|
||||
---
|
||||
|
||||
## UI
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `Canvas` (UGUI) | `UIDocument` (UI Toolkit) | UI Toolkit is now production-ready |
|
||||
| `Text` component | `TextMeshPro` or UI Toolkit `Label` | Better rendering, fewer draw calls |
|
||||
| `Image` component | UI Toolkit `VisualElement` with background | More flexible styling |
|
||||
|
||||
**Migration:** UGUI still works, but UI Toolkit is recommended for new projects.
|
||||
|
||||
---
|
||||
|
||||
## DOTS/Entities
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `ComponentSystem` | `ISystem` (unmanaged) | Entities 1.0+ complete rewrite |
|
||||
| `JobComponentSystem` | `ISystem` with `IJobEntity` | Burst-compatible |
|
||||
| `GameObjectEntity` | Pure ECS workflow | No GameObject conversion |
|
||||
| `EntityManager.CreateEntity()` (old signature) | `EntityManager.CreateEntity(EntityArchetype)` | Explicit archetype |
|
||||
| `ComponentDataFromEntity<T>` | `ComponentLookup<T>` | Entities 1.0+ rename |
|
||||
|
||||
**Migration:** See Entities package migration guide. Major refactor required.
|
||||
|
||||
---
|
||||
|
||||
## Rendering
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `CommandBuffer.DrawMesh()` | RenderGraph API | URP/HDRP render passes |
|
||||
| `OnPreRender()` / `OnPostRender()` | `RenderPipelineManager` callbacks | SRP compatibility |
|
||||
| `Camera.SetReplacementShader()` | Custom render pass | Not supported in SRP |
|
||||
|
||||
---
|
||||
|
||||
## Physics
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `Physics.RaycastAll()` | `Physics.RaycastNonAlloc()` | Avoid GC allocations |
|
||||
| `Rigidbody.velocity` (direct write) | `Rigidbody.AddForce()` | Better physics stability |
|
||||
|
||||
---
|
||||
|
||||
## Asset Loading
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `Resources.Load()` | Addressables | Better memory control, async loading |
|
||||
| Synchronous asset loading | `Addressables.LoadAssetAsync()` | Non-blocking |
|
||||
|
||||
---
|
||||
|
||||
## Animation
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Legacy Animation component | Animator Controller | Mecanim system |
|
||||
| `Animation.Play()` | `Animator.Play()` | State machine control |
|
||||
|
||||
---
|
||||
|
||||
## Particles
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Legacy Particle System | Visual Effect Graph | GPU-accelerated, more performant |
|
||||
|
||||
---
|
||||
|
||||
## Scripting
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `WWW` class | `UnityWebRequest` | Modern async networking |
|
||||
| `Application.LoadLevel()` | `SceneManager.LoadScene()` | Scene management |
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific
|
||||
|
||||
### WebGL
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| WebGL 1.0 | WebGL 2.0 or WebGPU | Unity 6+ defaults to WebGPU |
|
||||
|
||||
---
|
||||
|
||||
## Quick Migration Patterns
|
||||
|
||||
### Input Example
|
||||
```csharp
|
||||
// ❌ Deprecated
|
||||
if (Input.GetKeyDown(KeyCode.Space)) {
|
||||
Jump();
|
||||
}
|
||||
|
||||
// ✅ New Input System
|
||||
using UnityEngine.InputSystem;
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) {
|
||||
Jump();
|
||||
}
|
||||
```
|
||||
|
||||
### Asset Loading Example
|
||||
```csharp
|
||||
// ❌ Deprecated
|
||||
var prefab = Resources.Load<GameObject>("Enemies/Goblin");
|
||||
|
||||
// ✅ Addressables
|
||||
var handle = Addressables.LoadAssetAsync<GameObject>("Enemies/Goblin");
|
||||
await handle.Task;
|
||||
var prefab = handle.Result;
|
||||
```
|
||||
|
||||
### UI Example
|
||||
```csharp
|
||||
// ❌ Deprecated (UGUI)
|
||||
GetComponent<Text>().text = "Score: 100";
|
||||
|
||||
// ✅ TextMeshPro
|
||||
GetComponent<TextMeshProUGUI>().text = "Score: 100";
|
||||
|
||||
// ✅ UI Toolkit
|
||||
rootVisualElement.Q<Label>("score-label").text = "Score: 100";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/deprecated-features.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/Migration.html
|
||||
289
docs/engine-reference/unity/modules/animation.md
Normal file
289
docs/engine-reference/unity/modules/animation.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# Unity 6.3 — Animation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 animation improvements, Timeline enhancements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 animation systems:
|
||||
- **Animator Controller (Mecanim)**: State machine-based (RECOMMENDED)
|
||||
- **Timeline**: Cinematic sequences, cutscenes
|
||||
- **Animation Rigging**: Procedural runtime animation
|
||||
- **Legacy Animation**: Deprecated, avoid
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Animation Rigging Package (Production-Ready in Unity 6)
|
||||
|
||||
```csharp
|
||||
// Install: Package Manager > Animation Rigging
|
||||
// Runtime IK, aim constraints, procedural animation
|
||||
```
|
||||
|
||||
### Timeline Improvements
|
||||
- Better performance
|
||||
- More track types
|
||||
- Improved signal system
|
||||
|
||||
---
|
||||
|
||||
## Animator Controller (Mecanim)
|
||||
|
||||
### Basic Setup
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Animator Controller
|
||||
// Add to GameObject: Add Component > Animator
|
||||
// Assign Controller: Animator > Controller = YourAnimatorController
|
||||
```
|
||||
|
||||
### State Transitions
|
||||
|
||||
```csharp
|
||||
Animator animator = GetComponent<Animator>();
|
||||
|
||||
// ✅ Trigger transition
|
||||
animator.SetTrigger("Jump");
|
||||
|
||||
// ✅ Bool parameter
|
||||
animator.SetBool("IsRunning", true);
|
||||
|
||||
// ✅ Float parameter (blend trees)
|
||||
animator.SetFloat("Speed", currentSpeed);
|
||||
|
||||
// ✅ Integer parameter
|
||||
animator.SetInteger("WeaponType", 2);
|
||||
```
|
||||
|
||||
### Animation Layers
|
||||
- **Base Layer**: Default animations (locomotion)
|
||||
- **Override Layers**: Replace base layer (e.g., weapon swap)
|
||||
- **Additive Layers**: Add on top of base (e.g., breathing, aim offset)
|
||||
|
||||
```csharp
|
||||
// Set layer weight (0-1)
|
||||
animator.SetLayerWeight(1, 0.5f); // 50% blend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blend Trees
|
||||
|
||||
### 1D Blend Tree (Speed blending)
|
||||
|
||||
```csharp
|
||||
// Idle (Speed = 0) → Walk (Speed = 0.5) → Run (Speed = 1.0)
|
||||
animator.SetFloat("Speed", moveSpeed);
|
||||
```
|
||||
|
||||
### 2D Blend Tree (Directional movement)
|
||||
|
||||
```csharp
|
||||
// X-axis: Strafe (-1 to 1)
|
||||
// Y-axis: Forward/Back (-1 to 1)
|
||||
animator.SetFloat("MoveX", input.x);
|
||||
animator.SetFloat("MoveY", input.y);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Events
|
||||
|
||||
### Trigger Events from Animation Clips
|
||||
|
||||
```csharp
|
||||
// Add in Animation window: Right-click timeline > Add Animation Event
|
||||
// Must have matching method on GameObject:
|
||||
|
||||
public void OnFootstep() {
|
||||
// Play footstep sound
|
||||
AudioSource.PlayClipAtPoint(footstepClip, transform.position);
|
||||
}
|
||||
|
||||
public void OnAttackHit() {
|
||||
// Deal damage
|
||||
DealDamageInFrontOfPlayer();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Root Motion
|
||||
|
||||
### Character Movement via Animation
|
||||
|
||||
```csharp
|
||||
Animator animator = GetComponent<Animator>();
|
||||
animator.applyRootMotion = true; // Move character based on animation
|
||||
|
||||
void OnAnimatorMove() {
|
||||
// Custom root motion handling
|
||||
transform.position += animator.deltaPosition;
|
||||
transform.rotation *= animator.deltaRotation;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Rigging (Unity 6+)
|
||||
|
||||
### IK (Inverse Kinematics)
|
||||
|
||||
```csharp
|
||||
// Install: Package Manager > Animation Rigging
|
||||
// Add: Rig Builder component + Rig GameObject
|
||||
|
||||
// Two Bone IK (Arm/Leg)
|
||||
// - Add Two Bone IK Constraint
|
||||
// - Assign Tip (hand/foot), Mid (elbow/knee), Root (shoulder/hip)
|
||||
// - Set Target (where hand/foot should reach)
|
||||
|
||||
// Runtime control:
|
||||
TwoBoneIKConstraint ikConstraint = rig.GetComponentInChildren<TwoBoneIKConstraint>();
|
||||
ikConstraint.data.target = targetTransform;
|
||||
ikConstraint.weight = 1f; // 0-1 blend
|
||||
```
|
||||
|
||||
### Aim Constraint (Look At)
|
||||
|
||||
```csharp
|
||||
// Character looks at target
|
||||
MultiAimConstraint aimConstraint = rig.GetComponentInChildren<MultiAimConstraint>();
|
||||
aimConstraint.data.sourceObjects[0] = new WeightedTransform(targetTransform, 1f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timeline (Cutscenes)
|
||||
|
||||
### Basic Timeline Setup
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Timeline
|
||||
// Add to GameObject: Add Component > Playable Director
|
||||
// Assign Timeline: Playable Director > Playable = YourTimeline
|
||||
|
||||
// Play from script:
|
||||
PlayableDirector director = GetComponent<PlayableDirector>();
|
||||
director.Play();
|
||||
```
|
||||
|
||||
### Timeline Tracks
|
||||
- **Activation Track**: Enable/disable GameObjects
|
||||
- **Animation Track**: Play animations on Animator
|
||||
- **Audio Track**: Synchronized audio playback
|
||||
- **Cinemachine Track**: Camera movement
|
||||
- **Signal Track**: Trigger events at specific times
|
||||
|
||||
### Signal System (Events)
|
||||
|
||||
```csharp
|
||||
// Create Signal Asset: Assets > Create > Signals > Signal
|
||||
// Add Signal Emitter to Timeline track
|
||||
// Add Signal Receiver component to GameObject
|
||||
|
||||
public class CutsceneEvents : MonoBehaviour {
|
||||
public void OnDialogueStart() {
|
||||
// Triggered by signal
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Playback Control
|
||||
|
||||
### Play Animation Directly (No State Machine)
|
||||
|
||||
```csharp
|
||||
// ✅ CrossFade (smooth transition)
|
||||
animator.CrossFade("Attack", 0.2f); // 0.2s transition
|
||||
|
||||
// ✅ Play (instant)
|
||||
animator.Play("Idle");
|
||||
|
||||
// ❌ Avoid: Legacy Animation component
|
||||
Animation anim = GetComponent<Animation>(); // DEPRECATED
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Curves
|
||||
|
||||
### Custom Property Animation
|
||||
|
||||
```csharp
|
||||
// In Animation window: Add Property > Custom Component > Your Script > Your Float
|
||||
|
||||
public class WeaponTrail : MonoBehaviour {
|
||||
public float trailIntensity; // Animated by clip
|
||||
|
||||
void Update() {
|
||||
// Intensity controlled by animation curve
|
||||
trailRenderer.startWidth = trailIntensity;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Culling
|
||||
- `Animator > Culling Mode`:
|
||||
- **Always Animate**: Always update (expensive)
|
||||
- **Cull Update Transforms**: Stop updating bones when off-screen (RECOMMENDED)
|
||||
- **Cull Completely**: Stop all animation when off-screen
|
||||
|
||||
### LOD (Level of Detail)
|
||||
- Simpler animations for distant characters
|
||||
- Reduce skeleton bone count for LOD meshes
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Check if Animation Finished
|
||||
|
||||
```csharp
|
||||
AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);
|
||||
if (stateInfo.IsName("Attack") && stateInfo.normalizedTime >= 1.0f) {
|
||||
// Attack animation finished
|
||||
}
|
||||
```
|
||||
|
||||
### Override Animation Speed
|
||||
|
||||
```csharp
|
||||
animator.speed = 1.5f; // 150% speed
|
||||
```
|
||||
|
||||
### Get Current Animation Name
|
||||
|
||||
```csharp
|
||||
AnimatorClipInfo[] clipInfo = animator.GetCurrentAnimatorClipInfo(0);
|
||||
string currentClip = clipInfo[0].clip.name;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Animator Window
|
||||
- `Window > Animation > Animator`
|
||||
- Visualize state machine, see active state
|
||||
|
||||
### Animation Window
|
||||
- `Window > Animation > Animation`
|
||||
- Edit animation clips, add events
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/AnimationOverview.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.animation.rigging@1.3/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.timeline@1.8/manual/index.html
|
||||
284
docs/engine-reference/unity/modules/audio.md
Normal file
284
docs/engine-reference/unity/modules/audio.md
Normal file
@@ -0,0 +1,284 @@
|
||||
# Unity 6.3 — Audio Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 audio mixer improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 audio systems:
|
||||
- **AudioSource**: Play sounds on GameObjects
|
||||
- **Audio Mixer**: Mix, effect processing, dynamic mixing
|
||||
- **Spatial Audio**: 3D positioned sound
|
||||
|
||||
---
|
||||
|
||||
## Basic Audio Playback
|
||||
|
||||
### AudioSource Component
|
||||
|
||||
```csharp
|
||||
AudioSource audioSource = GetComponent<AudioSource>();
|
||||
|
||||
// ✅ Play
|
||||
audioSource.Play();
|
||||
|
||||
// ✅ Play with delay
|
||||
audioSource.PlayDelayed(0.5f); // 0.5 seconds
|
||||
|
||||
// ✅ Play one-shot (doesn't interrupt current sound)
|
||||
audioSource.PlayOneShot(clip);
|
||||
|
||||
// ✅ Stop
|
||||
audioSource.Stop();
|
||||
|
||||
// ✅ Pause/Resume
|
||||
audioSource.Pause();
|
||||
audioSource.UnPause();
|
||||
```
|
||||
|
||||
### Play Sound at Position (Static Method)
|
||||
|
||||
```csharp
|
||||
// ✅ Quick 3D sound playback (auto-destroys when done)
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position);
|
||||
|
||||
// ✅ With volume
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position, 0.7f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3D Spatial Audio
|
||||
|
||||
### AudioSource 3D Settings
|
||||
|
||||
```csharp
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
|
||||
// Spatial Blend: 0 = 2D, 1 = 3D
|
||||
source.spatialBlend = 1.0f; // Fully 3D
|
||||
|
||||
// Doppler effect (pitch shift based on velocity)
|
||||
source.dopplerLevel = 1.0f;
|
||||
|
||||
// Distance attenuation
|
||||
source.minDistance = 1f; // Full volume within this distance
|
||||
source.maxDistance = 50f; // Inaudible beyond this distance
|
||||
source.rolloffMode = AudioRolloffMode.Logarithmic; // Natural falloff
|
||||
```
|
||||
|
||||
### Volume Rolloff Curves
|
||||
- **Logarithmic**: Natural, realistic (RECOMMENDED)
|
||||
- **Linear**: Steady decrease
|
||||
- **Custom**: Define your own curve
|
||||
|
||||
---
|
||||
|
||||
## Audio Mixer (Advanced Mixing)
|
||||
|
||||
### Setup Audio Mixer
|
||||
|
||||
1. `Assets > Create > Audio Mixer`
|
||||
2. Open mixer: `Window > Audio > Audio Mixer`
|
||||
3. Create groups: Master > SFX, Music, Dialogue
|
||||
|
||||
### Assign AudioSource to Mixer Group
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public AudioMixerGroup sfxGroup;
|
||||
|
||||
void Start() {
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
source.outputAudioMixerGroup = sfxGroup; // Route to SFX group
|
||||
}
|
||||
```
|
||||
|
||||
### Control Mixer from Code
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Audio;
|
||||
|
||||
public AudioMixer audioMixer;
|
||||
|
||||
// ✅ Set volume (exposed parameter)
|
||||
audioMixer.SetFloat("MusicVolume", -10f); // dB (-80 to 0)
|
||||
|
||||
// ✅ Get volume
|
||||
audioMixer.GetFloat("MusicVolume", out float volume);
|
||||
|
||||
// Convert linear (0-1) to dB
|
||||
float volumeDB = Mathf.Log10(volumeLinear) * 20f;
|
||||
audioMixer.SetFloat("MusicVolume", volumeDB);
|
||||
```
|
||||
|
||||
### Expose Mixer Parameters
|
||||
In Audio Mixer window:
|
||||
1. Right-click parameter (e.g., Volume)
|
||||
2. "Expose 'Volume' to script"
|
||||
3. Rename in "Exposed Parameters" tab (e.g., "MusicVolume")
|
||||
|
||||
---
|
||||
|
||||
## Audio Effects
|
||||
|
||||
### Add Effects to Mixer Groups
|
||||
|
||||
In Audio Mixer:
|
||||
- Click group (e.g., SFX)
|
||||
- Click "Add Effect"
|
||||
- Choose: Reverb, Echo, Low Pass, High Pass, Distortion, etc.
|
||||
|
||||
### Duck Music During Dialogue (Sidechain)
|
||||
|
||||
```csharp
|
||||
// Setup in Audio Mixer:
|
||||
// 1. Create "Duck Volume" snapshot
|
||||
// 2. Lower music volume in that snapshot
|
||||
// 3. Transition to snapshot when dialogue plays
|
||||
|
||||
public AudioMixerSnapshot normalSnapshot;
|
||||
public AudioMixerSnapshot duckedSnapshot;
|
||||
|
||||
public void PlayDialogue(AudioClip clip) {
|
||||
duckedSnapshot.TransitionTo(0.5f); // 0.5s transition
|
||||
audioSource.PlayOneShot(clip);
|
||||
Invoke(nameof(RestoreMusic), clip.length);
|
||||
}
|
||||
|
||||
void RestoreMusic() {
|
||||
normalSnapshot.TransitionTo(1.0f); // 1s transition back
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Performance
|
||||
|
||||
### Optimize Audio Loading
|
||||
|
||||
```csharp
|
||||
// Audio Import Settings (Inspector):
|
||||
// - Load Type:
|
||||
// - Decompress On Load: Small clips (SFX), loads fully into memory
|
||||
// - Compressed In Memory: Medium clips, decompressed at runtime (RECOMMENDED)
|
||||
// - Streaming: Large clips (music), streamed from disk
|
||||
|
||||
// Compression Format:
|
||||
// - PCM: Uncompressed, highest quality, largest size
|
||||
// - ADPCM: 3.5x compression, good for SFX (RECOMMENDED for SFX)
|
||||
// - Vorbis/MP3: High compression, good for music (RECOMMENDED for music)
|
||||
```
|
||||
|
||||
### Preload Audio
|
||||
|
||||
```csharp
|
||||
// Preload audio clip before playing (avoid stutter)
|
||||
audioSource.clip.LoadAudioData();
|
||||
|
||||
// Check if loaded
|
||||
if (audioSource.clip.loadState == AudioDataLoadState.Loaded) {
|
||||
audioSource.Play();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Music Systems
|
||||
|
||||
### Crossfade Between Tracks
|
||||
|
||||
```csharp
|
||||
public IEnumerator CrossfadeMusic(AudioSource from, AudioSource to, float duration) {
|
||||
float elapsed = 0f;
|
||||
to.Play();
|
||||
|
||||
while (elapsed < duration) {
|
||||
elapsed += Time.deltaTime;
|
||||
float t = elapsed / duration;
|
||||
|
||||
from.volume = Mathf.Lerp(1f, 0f, t);
|
||||
to.volume = Mathf.Lerp(0f, 1f, t);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
from.Stop();
|
||||
}
|
||||
```
|
||||
|
||||
### Seamless Music Looping
|
||||
|
||||
```csharp
|
||||
// Audio Import Settings:
|
||||
// - Check "Loop" for seamless music loops
|
||||
audioSource.loop = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Random Pitch Variation (Avoid Repetition)
|
||||
|
||||
```csharp
|
||||
void PlaySoundWithVariation(AudioClip clip) {
|
||||
AudioSource source = GetComponent<AudioSource>();
|
||||
source.pitch = Random.Range(0.9f, 1.1f); // ±10% pitch variation
|
||||
source.PlayOneShot(clip);
|
||||
}
|
||||
```
|
||||
|
||||
### Footstep Sounds (Random from Array)
|
||||
|
||||
```csharp
|
||||
public AudioClip[] footstepClips;
|
||||
|
||||
void PlayFootstep() {
|
||||
AudioClip clip = footstepClips[Random.Range(0, footstepClips.Length)];
|
||||
AudioSource.PlayClipAtPoint(clip, transform.position, 0.5f);
|
||||
}
|
||||
```
|
||||
|
||||
### Check if Sound is Playing
|
||||
|
||||
```csharp
|
||||
if (audioSource.isPlaying) {
|
||||
// Sound is currently playing
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Listener
|
||||
|
||||
### Single Listener Rule
|
||||
- Only ONE `AudioListener` should be active at a time
|
||||
- Usually attached to Main Camera
|
||||
|
||||
```csharp
|
||||
// Disable extra listeners
|
||||
AudioListener listener = GetComponent<AudioListener>();
|
||||
listener.enabled = false;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Audio Window
|
||||
- `Window > Audio > Audio Mixer`
|
||||
- Visualize levels, test snapshots
|
||||
|
||||
### Audio Settings
|
||||
- `Edit > Project Settings > Audio`
|
||||
- Global volume, DSP buffer size, speaker mode
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/Audio.html
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/AudioMixer.html
|
||||
356
docs/engine-reference/unity/modules/input.md
Normal file
356
docs/engine-reference/unity/modules/input.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# Unity 6.3 — Input Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 uses new Input System (legacy Input deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 input systems:
|
||||
- **Input System Package** (RECOMMENDED): Cross-platform, rebindable, modern
|
||||
- **Legacy Input Manager**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Legacy Input Deprecated in Unity 6
|
||||
|
||||
```csharp
|
||||
// ❌ DEPRECATED: Input class
|
||||
if (Input.GetKeyDown(KeyCode.Space)) { }
|
||||
|
||||
// ✅ NEW: Input System package
|
||||
using UnityEngine.InputSystem;
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) { }
|
||||
```
|
||||
|
||||
**Migration Required:** Install `com.unity.inputsystem` package.
|
||||
|
||||
---
|
||||
|
||||
## Input System Package Setup
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Search "Input System"
|
||||
3. Install package
|
||||
4. Restart Unity when prompted
|
||||
|
||||
### Enable New Input System
|
||||
`Edit > Project Settings > Player > Active Input Handling`:
|
||||
- **Input System Package (New)** ✅ Recommended
|
||||
- **Both** (for migration period)
|
||||
|
||||
---
|
||||
|
||||
## Input Actions (Recommended Pattern)
|
||||
|
||||
### Create Input Actions Asset
|
||||
|
||||
1. `Assets > Create > Input Actions`
|
||||
2. Name it (e.g., "PlayerControls")
|
||||
3. Open asset, define actions:
|
||||
|
||||
```
|
||||
Action Maps:
|
||||
Gameplay
|
||||
Actions:
|
||||
- Move (Value, Vector2)
|
||||
- Jump (Button)
|
||||
- Fire (Button)
|
||||
- Look (Value, Vector2)
|
||||
```
|
||||
|
||||
4. **Generate C# Class**: Check "Generate C# Class" in Inspector
|
||||
5. Click "Apply"
|
||||
|
||||
### Use Generated Input Class
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public class PlayerController : MonoBehaviour {
|
||||
private PlayerControls controls;
|
||||
|
||||
void Awake() {
|
||||
controls = new PlayerControls();
|
||||
|
||||
// Subscribe to actions
|
||||
controls.Gameplay.Jump.performed += ctx => Jump();
|
||||
controls.Gameplay.Fire.performed += ctx => Fire();
|
||||
}
|
||||
|
||||
void OnEnable() => controls.Enable();
|
||||
void OnDisable() => controls.Disable();
|
||||
|
||||
void Update() {
|
||||
// Read continuous input
|
||||
Vector2 move = controls.Gameplay.Move.ReadValue<Vector2>();
|
||||
transform.Translate(new Vector3(move.x, 0, move.y) * Time.deltaTime);
|
||||
|
||||
Vector2 look = controls.Gameplay.Look.ReadValue<Vector2>();
|
||||
// Apply camera rotation
|
||||
}
|
||||
|
||||
void Jump() {
|
||||
Debug.Log("Jump!");
|
||||
}
|
||||
|
||||
void Fire() {
|
||||
Debug.Log("Fire!");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Direct Device Access (Quick & Dirty)
|
||||
|
||||
### Keyboard
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
// Current state
|
||||
if (Keyboard.current.spaceKey.isPressed) { }
|
||||
|
||||
// Just pressed this frame
|
||||
if (Keyboard.current.spaceKey.wasPressedThisFrame) { }
|
||||
|
||||
// Just released this frame
|
||||
if (Keyboard.current.spaceKey.wasReleasedThisFrame) { }
|
||||
}
|
||||
```
|
||||
|
||||
### Mouse
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
// Mouse position
|
||||
Vector2 mousePos = Mouse.current.position.ReadValue();
|
||||
|
||||
// Mouse delta (movement)
|
||||
Vector2 mouseDelta = Mouse.current.delta.ReadValue();
|
||||
|
||||
// Mouse buttons
|
||||
if (Mouse.current.leftButton.wasPressedThisFrame) { }
|
||||
if (Mouse.current.rightButton.isPressed) { }
|
||||
|
||||
// Scroll wheel
|
||||
Vector2 scroll = Mouse.current.scroll.ReadValue();
|
||||
}
|
||||
```
|
||||
|
||||
### Gamepad
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
void Update() {
|
||||
Gamepad gamepad = Gamepad.current;
|
||||
if (gamepad == null) return; // No gamepad connected
|
||||
|
||||
// Buttons
|
||||
if (gamepad.buttonSouth.wasPressedThisFrame) { } // A/Cross
|
||||
if (gamepad.buttonWest.wasPressedThisFrame) { } // X/Square
|
||||
|
||||
// Sticks
|
||||
Vector2 leftStick = gamepad.leftStick.ReadValue();
|
||||
Vector2 rightStick = gamepad.rightStick.ReadValue();
|
||||
|
||||
// Triggers
|
||||
float leftTrigger = gamepad.leftTrigger.ReadValue();
|
||||
float rightTrigger = gamepad.rightTrigger.ReadValue();
|
||||
|
||||
// D-Pad
|
||||
Vector2 dpad = gamepad.dpad.ReadValue();
|
||||
}
|
||||
```
|
||||
|
||||
### Touch (Mobile)
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.EnhancedTouch;
|
||||
|
||||
void OnEnable() {
|
||||
EnhancedTouchSupport.Enable();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
foreach (var touch in UnityEngine.InputSystem.EnhancedTouch.Touch.activeTouches) {
|
||||
Debug.Log($"Touch at {touch.screenPosition}");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Action Callbacks
|
||||
|
||||
### Action Callbacks (Event-Driven)
|
||||
|
||||
```csharp
|
||||
// started: Input began (e.g., trigger pressed slightly)
|
||||
controls.Gameplay.Fire.started += ctx => Debug.Log("Fire started");
|
||||
|
||||
// performed: Input action triggered (e.g., button fully pressed)
|
||||
controls.Gameplay.Fire.performed += ctx => Debug.Log("Fire performed");
|
||||
|
||||
// canceled: Input released or interrupted
|
||||
controls.Gameplay.Fire.canceled += ctx => Debug.Log("Fire canceled");
|
||||
```
|
||||
|
||||
### Context Data
|
||||
|
||||
```csharp
|
||||
controls.Gameplay.Move.performed += ctx => {
|
||||
Vector2 value = ctx.ReadValue<Vector2>();
|
||||
float duration = ctx.duration; // How long input held
|
||||
InputControl control = ctx.control; // Which device/control triggered it
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Schemes & Device Switching
|
||||
|
||||
### Define Control Schemes in Input Actions Asset
|
||||
|
||||
```
|
||||
Control Schemes:
|
||||
- Keyboard&Mouse (Keyboard, Mouse)
|
||||
- Gamepad (Gamepad)
|
||||
- Touch (Touchscreen)
|
||||
```
|
||||
|
||||
### Auto-Switch on Device Change
|
||||
|
||||
```csharp
|
||||
controls.Gameplay.Move.performed += ctx => {
|
||||
if (ctx.control.device is Keyboard) {
|
||||
Debug.Log("Using keyboard");
|
||||
} else if (ctx.control.device is Gamepad) {
|
||||
Debug.Log("Using gamepad");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rebinding (Runtime Key Mapping)
|
||||
|
||||
### Interactive Rebind
|
||||
|
||||
```csharp
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
public void RebindJumpKey() {
|
||||
var rebindOperation = controls.Gameplay.Jump.PerformInteractiveRebinding()
|
||||
.WithControlsExcluding("Mouse") // Exclude mouse bindings
|
||||
.OnComplete(operation => {
|
||||
Debug.Log("Rebind complete");
|
||||
operation.Dispose();
|
||||
})
|
||||
.Start();
|
||||
}
|
||||
```
|
||||
|
||||
### Save/Load Bindings
|
||||
|
||||
```csharp
|
||||
// Save
|
||||
string rebinds = controls.SaveBindingOverridesAsJson();
|
||||
PlayerPrefs.SetString("InputBindings", rebinds);
|
||||
|
||||
// Load
|
||||
string rebinds = PlayerPrefs.GetString("InputBindings");
|
||||
controls.LoadBindingOverridesFromJson(rebinds);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Action Types
|
||||
|
||||
### Button (Press/Release)
|
||||
- Single press/release
|
||||
- Example: Jump, Fire
|
||||
|
||||
### Value (Continuous)
|
||||
- Continuous value (float, Vector2)
|
||||
- Example: Move, Look, Aim
|
||||
|
||||
### Pass-Through (Immediate)
|
||||
- No processing, immediate value
|
||||
- Example: Mouse position
|
||||
|
||||
---
|
||||
|
||||
## Processors (Input Modifiers)
|
||||
|
||||
### Scale
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Scale
|
||||
// Multiply input by value (e.g., invert Y-axis)
|
||||
```
|
||||
|
||||
### Invert
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Invert
|
||||
// Flip input sign
|
||||
```
|
||||
|
||||
### Dead Zone
|
||||
|
||||
```csharp
|
||||
// In Input Actions asset: Action > Properties > Processors > Add > Stick Deadzone
|
||||
// Ignore small stick movements
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PlayerInput Component (Simplified Setup)
|
||||
|
||||
### Automatic Input Setup
|
||||
|
||||
```csharp
|
||||
// Add Component: Player Input
|
||||
// Assign Input Actions asset
|
||||
// Behavior: Send Messages / Invoke Unity Events / Invoke C# Events
|
||||
|
||||
// Send Messages example:
|
||||
public class Player : MonoBehaviour {
|
||||
public void OnMove(InputValue value) {
|
||||
Vector2 move = value.Get<Vector2>();
|
||||
// Handle movement
|
||||
}
|
||||
|
||||
public void OnJump(InputValue value) {
|
||||
if (value.isPressed) {
|
||||
Jump();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Input Debugger
|
||||
- `Window > Analysis > Input Debugger`
|
||||
- See active devices, input values, action states
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.inputsystem@1.11/manual/QuickStartGuide.html
|
||||
330
docs/engine-reference/unity/modules/navigation.md
Normal file
330
docs/engine-reference/unity/modules/navigation.md
Normal file
@@ -0,0 +1,330 @@
|
||||
# Unity 6.3 — Navigation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 NavMesh improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 navigation systems:
|
||||
- **NavMesh**: Built-in pathfinding for AI agents
|
||||
- **NavMeshComponents**: Package for runtime NavMesh building
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Basics
|
||||
|
||||
### Bake Navigation Mesh
|
||||
|
||||
1. Mark walkable surfaces:
|
||||
- Select GameObject (floor/terrain)
|
||||
- Inspector > Navigation > Object tab
|
||||
- Check "Navigation Static"
|
||||
|
||||
2. Bake NavMesh:
|
||||
- `Window > AI > Navigation`
|
||||
- Bake tab
|
||||
- Click "Bake"
|
||||
|
||||
3. Configure settings:
|
||||
- **Agent Radius**: How wide the agent is (0.5m default)
|
||||
- **Agent Height**: How tall the agent is (2m default)
|
||||
- **Max Slope**: Maximum walkable slope (45° default)
|
||||
- **Step Height**: Maximum climbable step (0.4m default)
|
||||
|
||||
---
|
||||
|
||||
## NavMeshAgent (AI Movement)
|
||||
|
||||
### Basic Agent Setup
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class Enemy : MonoBehaviour {
|
||||
private NavMeshAgent agent;
|
||||
public Transform target;
|
||||
|
||||
void Start() {
|
||||
agent = GetComponent<NavMeshAgent>();
|
||||
}
|
||||
|
||||
void Update() {
|
||||
// ✅ Move to target
|
||||
agent.SetDestination(target.position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### NavMeshAgent Properties
|
||||
|
||||
```csharp
|
||||
NavMeshAgent agent = GetComponent<NavMeshAgent>();
|
||||
|
||||
// Speed
|
||||
agent.speed = 3.5f;
|
||||
|
||||
// Acceleration
|
||||
agent.acceleration = 8f;
|
||||
|
||||
// Stopping distance
|
||||
agent.stoppingDistance = 2f; // Stop 2m before destination
|
||||
|
||||
// Auto-braking (slow down at destination)
|
||||
agent.autoBraking = true;
|
||||
|
||||
// Rotation speed
|
||||
agent.angularSpeed = 120f; // Degrees per second
|
||||
|
||||
// Obstacle avoidance
|
||||
agent.obstacleAvoidanceType = ObstacleAvoidanceType.HighQualityObstacleAvoidance;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Check Path Status
|
||||
|
||||
```csharp
|
||||
void Update() {
|
||||
agent.SetDestination(target.position);
|
||||
|
||||
// Check if agent has a path
|
||||
if (agent.hasPath) {
|
||||
// Check if path is complete
|
||||
if (agent.pathStatus == NavMeshPathStatus.PathComplete) {
|
||||
Debug.Log("Valid path");
|
||||
} else if (agent.pathStatus == NavMeshPathStatus.PathPartial) {
|
||||
Debug.Log("Partial path (destination unreachable)");
|
||||
} else {
|
||||
Debug.Log("Invalid path");
|
||||
}
|
||||
}
|
||||
|
||||
// Check if agent reached destination
|
||||
if (!agent.pathPending && agent.remainingDistance <= agent.stoppingDistance) {
|
||||
Debug.Log("Reached destination");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Calculate Path (Don't Move Yet)
|
||||
|
||||
```csharp
|
||||
NavMeshPath path = new NavMeshPath();
|
||||
agent.CalculatePath(targetPosition, path);
|
||||
|
||||
if (path.status == NavMeshPathStatus.PathComplete) {
|
||||
// Valid path exists
|
||||
agent.SetPath(path); // Apply the path
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Areas (Walkable Costs)
|
||||
|
||||
### Define Areas
|
||||
`Window > AI > Navigation > Areas tab`
|
||||
- **Walkable**: Cost 1 (default)
|
||||
- **Not Walkable**: Unwalkable
|
||||
- **Jump**: Cost 2 (prefer other routes)
|
||||
- **Custom**: Define your own
|
||||
|
||||
### Assign Area Costs
|
||||
|
||||
```csharp
|
||||
// Prefer shorter paths over low-cost paths
|
||||
agent.areaMask = NavMesh.AllAreas; // Walk on all areas
|
||||
|
||||
// Only walk on "Walkable" area (avoid "Jump")
|
||||
agent.areaMask = 1 << NavMesh.GetAreaFromName("Walkable");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMesh Obstacles (Dynamic Obstacles)
|
||||
|
||||
### NavMeshObstacle Component
|
||||
|
||||
```csharp
|
||||
// Add: GameObject > Add Component > NavMesh Obstacle
|
||||
|
||||
// Carve: Create hole in NavMesh (agents avoid)
|
||||
// Don't Carve: Agent pushes through (local avoidance)
|
||||
```
|
||||
|
||||
### Dynamic Carving (Moving Obstacles)
|
||||
|
||||
```csharp
|
||||
NavMeshObstacle obstacle = GetComponent<NavMeshObstacle>();
|
||||
obstacle.carving = true; // Create dynamic hole in NavMesh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Off-Mesh Links (Jumps, Teleports)
|
||||
|
||||
### Create Off-Mesh Link
|
||||
|
||||
1. `GameObject > Create Empty` (at jump start)
|
||||
2. Add `Off Mesh Link` component
|
||||
3. Set Start/End transforms
|
||||
4. Configure:
|
||||
- **Bi-Directional**: Can traverse both ways
|
||||
- **Cost Override**: Path cost for this link
|
||||
|
||||
### Detect Off-Mesh Link Traversal
|
||||
|
||||
```csharp
|
||||
void Update() {
|
||||
// Check if agent is on an off-mesh link
|
||||
if (agent.isOnOffMeshLink) {
|
||||
// Manually traverse (e.g., play jump animation)
|
||||
StartCoroutine(TraverseOffMeshLink());
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator TraverseOffMeshLink() {
|
||||
OffMeshLinkData data = agent.currentOffMeshLinkData;
|
||||
Vector3 startPos = agent.transform.position;
|
||||
Vector3 endPos = data.endPos;
|
||||
|
||||
float duration = 0.5f;
|
||||
float elapsed = 0f;
|
||||
|
||||
while (elapsed < duration) {
|
||||
agent.transform.position = Vector3.Lerp(startPos, endPos, elapsed / duration);
|
||||
elapsed += Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
agent.CompleteOffMeshLink(); // Resume normal pathfinding
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NavMeshComponents Package (Runtime Baking)
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Add from Git URL: `com.unity.ai.navigation`
|
||||
|
||||
### Runtime NavMesh Baking
|
||||
|
||||
```csharp
|
||||
using Unity.AI.Navigation;
|
||||
|
||||
public class NavMeshBuilder : MonoBehaviour {
|
||||
public NavMeshSurface surface;
|
||||
|
||||
void Start() {
|
||||
// Bake NavMesh at runtime
|
||||
surface.BuildNavMesh();
|
||||
}
|
||||
|
||||
void UpdateNavMesh() {
|
||||
// Update NavMesh after terrain changes
|
||||
surface.UpdateNavMesh(surface.navMeshData);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Patrol Between Waypoints
|
||||
|
||||
```csharp
|
||||
public Transform[] waypoints;
|
||||
private int currentWaypoint = 0;
|
||||
|
||||
void Update() {
|
||||
if (!agent.pathPending && agent.remainingDistance < 0.5f) {
|
||||
// Reached waypoint, move to next
|
||||
currentWaypoint = (currentWaypoint + 1) % waypoints.Length;
|
||||
agent.SetDestination(waypoints[currentWaypoint].position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Chase Player
|
||||
|
||||
```csharp
|
||||
public Transform player;
|
||||
public float chaseRange = 10f;
|
||||
|
||||
void Update() {
|
||||
float distance = Vector3.Distance(transform.position, player.position);
|
||||
|
||||
if (distance <= chaseRange) {
|
||||
agent.SetDestination(player.position);
|
||||
} else {
|
||||
agent.ResetPath(); // Stop moving
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Flee from Player
|
||||
|
||||
```csharp
|
||||
public Transform player;
|
||||
public float fleeRange = 5f;
|
||||
|
||||
void Update() {
|
||||
float distance = Vector3.Distance(transform.position, player.position);
|
||||
|
||||
if (distance <= fleeRange) {
|
||||
// Run away from player
|
||||
Vector3 fleeDirection = transform.position - player.position;
|
||||
Vector3 fleeTarget = transform.position + fleeDirection.normalized * 10f;
|
||||
|
||||
agent.SetDestination(fleeTarget);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### NavMesh Visualization
|
||||
- `Window > AI > Navigation > Bake tab`
|
||||
- Check "Show NavMesh" to visualize walkable areas
|
||||
|
||||
### Agent Path Gizmos
|
||||
|
||||
```csharp
|
||||
void OnDrawGizmos() {
|
||||
if (agent != null && agent.hasPath) {
|
||||
Gizmos.color = Color.green;
|
||||
Vector3[] corners = agent.path.corners;
|
||||
|
||||
for (int i = 0; i < corners.Length - 1; i++) {
|
||||
Gizmos.DrawLine(corners[i], corners[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **Limit Obstacle Avoidance Quality**: Use `LowQualityObstacleAvoidance` for distant agents
|
||||
- **Update Frequency**: Don't call `SetDestination()` every frame if target hasn't moved
|
||||
- **Area Masks**: Limit walkable areas to reduce pathfinding search space
|
||||
- **NavMesh Tiles**: Use tiled NavMesh for large worlds (NavMeshComponents package)
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/Navigation.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ai.navigation@2.0/manual/index.html
|
||||
351
docs/engine-reference/unity/modules/networking.md
Normal file
351
docs/engine-reference/unity/modules/networking.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# Unity 6.3 — Networking Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 uses Netcode for GameObjects (UNet deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 networking options:
|
||||
- **Netcode for GameObjects** (RECOMMENDED): Official Unity multiplayer framework
|
||||
- **Mirror**: Community-driven (UNet successor)
|
||||
- **Photon**: Third-party service (PUN2)
|
||||
- **Custom**: Low-level sockets
|
||||
|
||||
**UNet (Legacy)**: Deprecated, do not use.
|
||||
|
||||
---
|
||||
|
||||
## Netcode for GameObjects
|
||||
|
||||
### Installation
|
||||
1. `Window > Package Manager`
|
||||
2. Search "Netcode for GameObjects"
|
||||
3. Install `com.unity.netcode.gameobjects`
|
||||
|
||||
---
|
||||
|
||||
## Basic Setup
|
||||
|
||||
### NetworkManager
|
||||
|
||||
```csharp
|
||||
// Add to scene: GameObject > Add Component > NetworkManager
|
||||
|
||||
// Or create custom NetworkManager:
|
||||
using Unity.Netcode;
|
||||
|
||||
public class CustomNetworkManager : MonoBehaviour {
|
||||
void Start() {
|
||||
NetworkManager.Singleton.StartHost(); // Server + client
|
||||
// OR
|
||||
NetworkManager.Singleton.StartServer(); // Dedicated server
|
||||
// OR
|
||||
NetworkManager.Singleton.StartClient(); // Client only
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkObject (Networked GameObjects)
|
||||
|
||||
### Mark GameObject as Networked
|
||||
|
||||
1. Add `NetworkObject` component to GameObject
|
||||
2. Must be in root of prefab (not nested)
|
||||
3. Register prefab in `NetworkManager > NetworkPrefabs List`
|
||||
|
||||
### Spawn Network Objects
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class GameManager : NetworkBehaviour {
|
||||
public GameObject playerPrefab;
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
public void SpawnPlayerServerRpc(ulong clientId) {
|
||||
GameObject player = Instantiate(playerPrefab);
|
||||
player.GetComponent<NetworkObject>().SpawnAsPlayerObject(clientId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkBehaviour (Networked Scripts)
|
||||
|
||||
### NetworkBehaviour Base Class
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class Player : NetworkBehaviour {
|
||||
// Called when spawned on network
|
||||
public override void OnNetworkSpawn() {
|
||||
if (IsOwner) {
|
||||
// Only run on owner's client
|
||||
GetComponent<Camera>().enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Update() {
|
||||
if (!IsOwner) return; // Only owner can control
|
||||
|
||||
// Handle input
|
||||
if (Input.GetKey(KeyCode.W)) {
|
||||
MoveServerRpc(Vector3.forward);
|
||||
}
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
void MoveServerRpc(Vector3 direction) {
|
||||
// Runs on server
|
||||
transform.position += direction * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Variables (Synchronized State)
|
||||
|
||||
### NetworkVariable<T>
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
public class Player : NetworkBehaviour {
|
||||
// ✅ Auto-synced across clients
|
||||
private NetworkVariable<int> health = new NetworkVariable<int>(100);
|
||||
|
||||
public override void OnNetworkSpawn() {
|
||||
// Subscribe to value changes
|
||||
health.OnValueChanged += OnHealthChanged;
|
||||
}
|
||||
|
||||
void OnHealthChanged(int oldValue, int newValue) {
|
||||
Debug.Log($"Health changed: {oldValue} -> {newValue}");
|
||||
UpdateHealthUI(newValue);
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
public void TakeDamageServerRpc(int damage) {
|
||||
// Only server can modify NetworkVariable
|
||||
health.Value -= damage;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### NetworkVariable Permissions
|
||||
|
||||
```csharp
|
||||
// Server can write, clients read-only (default)
|
||||
private NetworkVariable<int> score = new NetworkVariable<int>();
|
||||
|
||||
// Owner can write
|
||||
private NetworkVariable<int> ammo = new NetworkVariable<int>(
|
||||
default,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Owner
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RPCs (Remote Procedure Calls)
|
||||
|
||||
### ServerRpc (Client → Server)
|
||||
|
||||
```csharp
|
||||
// Client calls, server executes
|
||||
[ServerRpc]
|
||||
void FireWeaponServerRpc() {
|
||||
// Runs on server
|
||||
Debug.Log("Server: Weapon fired");
|
||||
}
|
||||
|
||||
// Call from client:
|
||||
if (IsOwner && Input.GetKeyDown(KeyCode.Space)) {
|
||||
FireWeaponServerRpc();
|
||||
}
|
||||
```
|
||||
|
||||
### ClientRpc (Server → All Clients)
|
||||
|
||||
```csharp
|
||||
// Server calls, all clients execute
|
||||
[ClientRpc]
|
||||
void PlayExplosionClientRpc(Vector3 position) {
|
||||
// Runs on all clients
|
||||
Instantiate(explosionPrefab, position, Quaternion.identity);
|
||||
}
|
||||
|
||||
// Call from server:
|
||||
[ServerRpc]
|
||||
void ExplodeServerRpc(Vector3 position) {
|
||||
// Server logic
|
||||
DealDamageToNearbyPlayers(position);
|
||||
|
||||
// Notify all clients
|
||||
PlayExplosionClientRpc(position);
|
||||
}
|
||||
```
|
||||
|
||||
### RPC Parameters
|
||||
|
||||
```csharp
|
||||
// ✅ Supported: Primitives, structs, strings, arrays
|
||||
[ServerRpc]
|
||||
void SetNameServerRpc(string playerName) { }
|
||||
|
||||
[ClientRpc]
|
||||
void UpdateScoresClientRpc(int[] scores) { }
|
||||
|
||||
// ❌ Not supported: MonoBehaviour, GameObject (use NetworkObjectReference)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Ownership
|
||||
|
||||
### Check Ownership
|
||||
|
||||
```csharp
|
||||
if (IsOwner) {
|
||||
// This client owns this NetworkObject
|
||||
}
|
||||
|
||||
if (IsServer) {
|
||||
// Running on server
|
||||
}
|
||||
|
||||
if (IsClient) {
|
||||
// Running on client
|
||||
}
|
||||
|
||||
if (IsLocalPlayer) {
|
||||
// This is the local player object
|
||||
}
|
||||
```
|
||||
|
||||
### Transfer Ownership
|
||||
|
||||
```csharp
|
||||
// Server transfers ownership
|
||||
NetworkObject netObj = GetComponent<NetworkObject>();
|
||||
netObj.ChangeOwnership(newOwnerClientId);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## NetworkObjectReference (Pass GameObjects in RPCs)
|
||||
|
||||
```csharp
|
||||
using Unity.Netcode;
|
||||
|
||||
[ServerRpc]
|
||||
void AttackTargetServerRpc(NetworkObjectReference targetRef) {
|
||||
if (targetRef.TryGet(out NetworkObject target)) {
|
||||
// Got the target object
|
||||
target.GetComponent<Health>().TakeDamage(10);
|
||||
}
|
||||
}
|
||||
|
||||
// Call:
|
||||
NetworkObject targetNetObj = target.GetComponent<NetworkObject>();
|
||||
AttackTargetServerRpc(targetNetObj);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Client-Server Architecture
|
||||
|
||||
### Server-Authoritative Pattern (RECOMMENDED)
|
||||
|
||||
```csharp
|
||||
public class Player : NetworkBehaviour {
|
||||
private NetworkVariable<Vector3> position = new NetworkVariable<Vector3>();
|
||||
|
||||
void Update() {
|
||||
if (IsOwner) {
|
||||
// Client: Send input to server
|
||||
Vector3 input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
|
||||
MoveServerRpc(input);
|
||||
}
|
||||
|
||||
// All clients: Sync to networked position
|
||||
transform.position = position.Value;
|
||||
}
|
||||
|
||||
[ServerRpc]
|
||||
void MoveServerRpc(Vector3 input) {
|
||||
// Server: Validate and apply movement
|
||||
position.Value += input * Time.deltaTime * moveSpeed;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Transport
|
||||
|
||||
### Unity Transport (Default)
|
||||
|
||||
```csharp
|
||||
// Configured in NetworkManager:
|
||||
// - Transport: Unity Transport
|
||||
// - Address: 127.0.0.1 (localhost) or server IP
|
||||
// - Port: 7777 (default)
|
||||
```
|
||||
|
||||
### Connection Events
|
||||
|
||||
```csharp
|
||||
void Start() {
|
||||
NetworkManager.Singleton.OnClientConnectedCallback += OnClientConnected;
|
||||
NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnected;
|
||||
}
|
||||
|
||||
void OnClientConnected(ulong clientId) {
|
||||
Debug.Log($"Client {clientId} connected");
|
||||
}
|
||||
|
||||
void OnClientDisconnected(ulong clientId) {
|
||||
Debug.Log($"Client {clientId} disconnected");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Reduce Network Traffic
|
||||
- Use `NetworkVariable` for state that changes infrequently
|
||||
- Batch multiple changes before syncing
|
||||
- Use delta compression for large data
|
||||
|
||||
### Prediction & Reconciliation
|
||||
- Run movement locally for responsiveness
|
||||
- Reconcile with server authoritative state
|
||||
- Use interpolation for smooth movement
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Network Profiler
|
||||
- `Window > Analysis > Network Profiler`
|
||||
- Monitor bandwidth, RPC calls, variable updates
|
||||
|
||||
### Network Simulator (Test Latency/Packet Loss)
|
||||
- `NetworkManager > Network Simulator`
|
||||
- Add artificial lag and packet loss for testing
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs-multiplayer.unity3d.com/netcode/current/about/
|
||||
- https://docs-multiplayer.unity3d.com/netcode/current/learn/bossroom/
|
||||
268
docs/engine-reference/unity/modules/physics.md
Normal file
268
docs/engine-reference/unity/modules/physics.md
Normal file
@@ -0,0 +1,268 @@
|
||||
# Unity 6.3 — Physics Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 physics improvements, solver changes
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 uses **PhysX 5.1** (improved from PhysX 4.x in 2022 LTS):
|
||||
- Better solver stability
|
||||
- Improved performance
|
||||
- Enhanced collision detection
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### Default Solver Iterations Increased
|
||||
Unity 6 increased default solver iterations for better stability:
|
||||
|
||||
```csharp
|
||||
// Default changed from 6 to 8 iterations
|
||||
Physics.defaultSolverIterations = 8; // Check if relying on old behavior
|
||||
```
|
||||
|
||||
### Enhanced Collision Detection
|
||||
|
||||
```csharp
|
||||
// ✅ Unity 6: Improved Continuous Collision Detection (CCD)
|
||||
rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
||||
// Better handling of fast-moving objects
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Physics Components
|
||||
|
||||
### Rigidbody
|
||||
|
||||
```csharp
|
||||
// ✅ Best practice: Use AddForce, not direct velocity writes
|
||||
Rigidbody rb = GetComponent<Rigidbody>();
|
||||
rb.AddForce(Vector3.forward * 10f, ForceMode.Impulse);
|
||||
|
||||
// ❌ Avoid: Direct velocity assignment (can cause instability)
|
||||
rb.velocity = new Vector3(0, 10, 0); // Only use when necessary
|
||||
```
|
||||
|
||||
### Colliders
|
||||
|
||||
```csharp
|
||||
// Primitive colliders: Box, Sphere, Capsule (cheapest)
|
||||
// Mesh colliders: Expensive, use only for static geometry
|
||||
|
||||
// ✅ Compound colliders (multiple primitives) > single mesh collider
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raycasting
|
||||
|
||||
### Efficient Raycasting (Avoid Allocations)
|
||||
|
||||
```csharp
|
||||
// ✅ Non-allocating raycast
|
||||
if (Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance)) {
|
||||
Debug.Log($"Hit: {hit.collider.name}");
|
||||
}
|
||||
|
||||
// ✅ Multiple hits (non-allocating)
|
||||
RaycastHit[] results = new RaycastHit[10];
|
||||
int hitCount = Physics.RaycastNonAlloc(origin, direction, results, maxDistance);
|
||||
for (int i = 0; i < hitCount; i++) {
|
||||
Debug.Log($"Hit {i}: {results[i].collider.name}");
|
||||
}
|
||||
|
||||
// ❌ Avoid: RaycastAll (allocates array every call)
|
||||
RaycastHit[] hits = Physics.RaycastAll(origin, direction); // GC allocation!
|
||||
```
|
||||
|
||||
### LayerMask for Selective Raycasting
|
||||
|
||||
```csharp
|
||||
// ✅ Use LayerMask to filter collisions
|
||||
int layerMask = 1 << LayerMask.NameToLayer("Enemy");
|
||||
Physics.Raycast(origin, direction, out RaycastHit hit, maxDistance, layerMask);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Physics Queries
|
||||
|
||||
### OverlapSphere (Check for nearby objects)
|
||||
|
||||
```csharp
|
||||
// ✅ Non-allocating version
|
||||
Collider[] results = new Collider[10];
|
||||
int count = Physics.OverlapSphereNonAlloc(center, radius, results);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// Process results[i]
|
||||
}
|
||||
```
|
||||
|
||||
### SphereCast (Thick raycast)
|
||||
|
||||
```csharp
|
||||
// Useful for character controllers
|
||||
if (Physics.SphereCast(origin, radius, direction, out RaycastHit hit, maxDistance)) {
|
||||
// Hit something with a sphere-shaped ray
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collision Events
|
||||
|
||||
### OnCollisionEnter / Stay / Exit
|
||||
|
||||
```csharp
|
||||
void OnCollisionEnter(Collision collision) {
|
||||
// Triggered when collision starts
|
||||
Debug.Log($"Collided with {collision.gameObject.name}");
|
||||
|
||||
// Access contact points
|
||||
foreach (ContactPoint contact in collision.contacts) {
|
||||
Debug.DrawRay(contact.point, contact.normal, Color.red, 2f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### OnTriggerEnter / Stay / Exit
|
||||
|
||||
```csharp
|
||||
void OnTriggerEnter(Collider other) {
|
||||
// Trigger collider (Is Trigger = true)
|
||||
if (other.CompareTag("Pickup")) {
|
||||
Destroy(other.gameObject);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Character Controllers
|
||||
|
||||
### CharacterController Component
|
||||
|
||||
```csharp
|
||||
CharacterController controller = GetComponent<CharacterController>();
|
||||
|
||||
// ✅ Move with collision detection
|
||||
Vector3 move = transform.forward * speed * Time.deltaTime;
|
||||
controller.Move(move);
|
||||
|
||||
// Apply gravity manually
|
||||
if (!controller.isGrounded) {
|
||||
velocity.y += Physics.gravity.y * Time.deltaTime;
|
||||
}
|
||||
controller.Move(velocity * Time.deltaTime);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Physics Materials
|
||||
|
||||
### Friction & Bounciness
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Physic Material
|
||||
// Assign to collider: Collider > Material
|
||||
|
||||
// PhysicMaterial settings:
|
||||
// - Dynamic Friction: 0.6 (sliding friction)
|
||||
// - Static Friction: 0.6 (starting friction)
|
||||
// - Bounciness: 0.0 - 1.0
|
||||
// - Friction Combine: Average, Minimum, Maximum, Multiply
|
||||
// - Bounce Combine: Average, Minimum, Maximum, Multiply
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Joints
|
||||
|
||||
### Fixed Joint (Attach two rigidbodies)
|
||||
|
||||
```csharp
|
||||
FixedJoint joint = gameObject.AddComponent<FixedJoint>();
|
||||
joint.connectedBody = otherRigidbody;
|
||||
```
|
||||
|
||||
### Hinge Joint (Door, wheel)
|
||||
|
||||
```csharp
|
||||
HingeJoint hinge = gameObject.AddComponent<HingeJoint>();
|
||||
hinge.axis = Vector3.up; // Rotation axis
|
||||
hinge.useLimits = true;
|
||||
hinge.limits = new JointLimits { min = -90, max = 90 };
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Physics Layer Collision Matrix
|
||||
`Edit > Project Settings > Physics > Layer Collision Matrix`
|
||||
- Disable unnecessary collision checks between layers
|
||||
- Massive performance gain
|
||||
|
||||
### Fixed Timestep
|
||||
`Edit > Project Settings > Time > Fixed Timestep`
|
||||
- Default: 0.02 (50 FPS physics)
|
||||
- Lower = more accurate, higher CPU cost
|
||||
- Match game's target framerate if possible
|
||||
|
||||
### Simplified Collision Geometry
|
||||
- Use primitive colliders (box, sphere, capsule) over mesh colliders
|
||||
- Bake mesh colliders at build time, not runtime
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Ground Check (Character Controller)
|
||||
|
||||
```csharp
|
||||
bool IsGrounded() {
|
||||
float rayLength = 0.1f;
|
||||
return Physics.Raycast(transform.position, Vector3.down, rayLength);
|
||||
}
|
||||
```
|
||||
|
||||
### Apply Explosion Force
|
||||
|
||||
```csharp
|
||||
void ApplyExplosion(Vector3 explosionPos, float radius, float force) {
|
||||
Collider[] colliders = Physics.OverlapSphere(explosionPos, radius);
|
||||
foreach (Collider hit in colliders) {
|
||||
Rigidbody rb = hit.GetComponent<Rigidbody>();
|
||||
if (rb != null) {
|
||||
rb.AddExplosionForce(force, explosionPos, radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Physics Debugger (Unity 6+)
|
||||
- `Window > Analysis > Physics Debugger`
|
||||
- Visualize colliders, contacts, queries
|
||||
|
||||
### Gizmos
|
||||
|
||||
```csharp
|
||||
void OnDrawGizmos() {
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, detectionRadius);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/PhysicsOverview.html
|
||||
- https://docs.unity3d.com/ScriptReference/Physics.html
|
||||
238
docs/engine-reference/unity/modules/rendering.md
Normal file
238
docs/engine-reference/unity/modules/rendering.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Unity 6.3 — Rendering Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** LLM trained on Unity 2022 LTS; Unity 6 has major rendering changes
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6.3 LTS uses **Scriptable Render Pipelines (SRP)** as the modern rendering architecture:
|
||||
- **URP (Universal Render Pipeline)**: Cross-platform, mobile-friendly (RECOMMENDED)
|
||||
- **HDRP (High Definition Render Pipeline)**: High-end PC/console, photorealistic
|
||||
- **Built-in Pipeline**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Key Changes from 2022 LTS
|
||||
|
||||
### RenderGraph API (Unity 6+)
|
||||
Custom render passes now use RenderGraph instead of CommandBuffer:
|
||||
|
||||
```csharp
|
||||
// ✅ Unity 6+ (RenderGraph)
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) {
|
||||
using var builder = renderGraph.AddRasterRenderPass<PassData>("MyPass", out var passData);
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext ctx) => {
|
||||
// Rendering commands
|
||||
});
|
||||
}
|
||||
|
||||
// ❌ Old (CommandBuffer - still works but deprecated)
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData data) { }
|
||||
```
|
||||
|
||||
### GPU Resident Drawer (Unity 6+)
|
||||
Automatic batching for massive draw call reduction:
|
||||
|
||||
```csharp
|
||||
// Enable in URP Asset settings:
|
||||
// Rendering > GPU Resident Drawer = Enabled
|
||||
// Automatically batches thousands of objects with minimal CPU overhead
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## URP Quick Reference
|
||||
|
||||
### Creating a URP Asset
|
||||
1. `Assets > Create > Rendering > URP Asset (with Universal Renderer)`
|
||||
2. Assign to `Project Settings > Graphics > Scriptable Render Pipeline Settings`
|
||||
|
||||
### URP Renderer Features
|
||||
Add custom render passes:
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
public class OutlineRendererFeature : ScriptableRendererFeature {
|
||||
OutlineRenderPass pass;
|
||||
|
||||
public override void Create() {
|
||||
pass = new OutlineRenderPass();
|
||||
}
|
||||
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData data) {
|
||||
renderer.EnqueuePass(pass);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Materials & Shaders
|
||||
|
||||
### Shader Graph (Visual Shader Editor)
|
||||
Unity 6 Shader Graph is production-ready for all shader types:
|
||||
|
||||
```csharp
|
||||
// Create: Assets > Create > Shader Graph > URP > Lit Shader Graph
|
||||
// No code needed, visual node-based editing
|
||||
```
|
||||
|
||||
### HLSL Custom Shaders (URP)
|
||||
|
||||
```hlsl
|
||||
// URP Lit shader template
|
||||
Shader "Custom/URPLit" {
|
||||
Properties {
|
||||
_BaseColor ("Base Color", Color) = (1,1,1,1)
|
||||
}
|
||||
SubShader {
|
||||
Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" }
|
||||
|
||||
Pass {
|
||||
Name "ForwardLit"
|
||||
Tags { "LightMode"="UniversalForward" }
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Attributes {
|
||||
float4 positionOS : POSITION;
|
||||
};
|
||||
|
||||
struct Varyings {
|
||||
float4 positionCS : SV_POSITION;
|
||||
};
|
||||
|
||||
Varyings vert(Attributes input) {
|
||||
Varyings output;
|
||||
output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
|
||||
return output;
|
||||
}
|
||||
|
||||
half4 frag(Varyings input) : SV_Target {
|
||||
return half4(1, 0, 0, 1); // Red
|
||||
}
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lighting
|
||||
|
||||
### Baked Lighting (Unity 6 Progressive Lightmapper)
|
||||
|
||||
```csharp
|
||||
// Mark objects as static: Inspector > Static > Contribute GI
|
||||
// Bake: Window > Rendering > Lighting > Generate Lighting
|
||||
```
|
||||
|
||||
### Real-Time Lights (URP)
|
||||
|
||||
```csharp
|
||||
// Main Light (Directional): Auto-handled by URP
|
||||
// Additional Lights: Limited by "Additional Lights" setting in URP Asset
|
||||
|
||||
// Check light count in shader:
|
||||
int lightCount = GetAdditionalLightsCount();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Processing
|
||||
|
||||
### Volume System (Unity 6+)
|
||||
|
||||
```csharp
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
// Add Volume component to GameObject
|
||||
// Add Volume Profile asset
|
||||
// Configure effects: Bloom, Color Grading, Depth of Field, etc.
|
||||
|
||||
// Script access:
|
||||
Volume volume = GetComponent<Volume>();
|
||||
if (volume.profile.TryGet<Bloom>(out var bloom)) {
|
||||
bloom.intensity.value = 2.5f;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### SRP Batcher (Auto-batching)
|
||||
|
||||
```csharp
|
||||
// Enable: URP Asset > Advanced > SRP Batcher = Enabled
|
||||
// Batches draws with same shader variant (minimal CPU overhead)
|
||||
```
|
||||
|
||||
### GPU Instancing
|
||||
|
||||
```csharp
|
||||
// Material: Enable "Enable GPU Instancing" checkbox
|
||||
// Batches identical meshes (same material + mesh)
|
||||
|
||||
Graphics.RenderMeshInstanced(
|
||||
new RenderParams(material),
|
||||
mesh,
|
||||
0,
|
||||
matrices // NativeArray<Matrix4x4>
|
||||
);
|
||||
```
|
||||
|
||||
### Occlusion Culling
|
||||
|
||||
```csharp
|
||||
// Window > Rendering > Occlusion Culling
|
||||
// Bake occlusion data for static geometry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Custom Camera Rendering
|
||||
|
||||
```csharp
|
||||
// Get URP camera data
|
||||
var cameraData = frameData.Get<UniversalCameraData>();
|
||||
var camera = cameraData.camera;
|
||||
|
||||
// Access render targets
|
||||
var colorTarget = cameraData.renderer.cameraColorTargetHandle;
|
||||
```
|
||||
|
||||
### Screen-Space Effects
|
||||
|
||||
```csharp
|
||||
// Create ScriptableRendererFeature
|
||||
// Inject pass at specific point: AfterRenderingOpaques, AfterRenderingTransparents, etc.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Frame Debugger
|
||||
- `Window > Analysis > Frame Debugger`
|
||||
- Step through draw calls, inspect state
|
||||
|
||||
### Rendering Debugger (Unity 6+)
|
||||
- `Window > Analysis > Rendering Debugger`
|
||||
- Live view of URP settings, overdraw, lighting
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@17.0/manual/index.html
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/render-pipelines.html
|
||||
377
docs/engine-reference/unity/modules/ui.md
Normal file
377
docs/engine-reference/unity/modules/ui.md
Normal file
@@ -0,0 +1,377 @@
|
||||
# Unity 6.3 — UI Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** Unity 6 UI Toolkit is production-ready for runtime UI
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Unity 6 UI systems:
|
||||
- **UI Toolkit** (RECOMMENDED): Modern, performant, HTML/CSS-like (production-ready in Unity 6)
|
||||
- **UGUI (Canvas)**: Legacy system, still supported but not recommended for new projects
|
||||
- **IMGUI**: Editor-only, deprecated for runtime UI
|
||||
|
||||
---
|
||||
|
||||
## UI Toolkit (Modern UI)
|
||||
|
||||
### Setup UI Document
|
||||
|
||||
1. Create UXML (UI structure):
|
||||
- `Assets > Create > UI Toolkit > UI Document`
|
||||
2. Create USS (styling):
|
||||
- `Assets > Create > UI Toolkit > StyleSheet`
|
||||
3. Add to scene:
|
||||
- `GameObject > UI Toolkit > UI Document`
|
||||
- Assign UXML to `UIDocument > Source Asset`
|
||||
|
||||
---
|
||||
|
||||
### UXML (UI Structure)
|
||||
|
||||
```xml
|
||||
<!-- MainMenu.uxml -->
|
||||
<ui:UXML xmlns:ui="UnityEngine.UIElements">
|
||||
<ui:VisualElement class="container">
|
||||
<ui:Label text="Main Menu" class="title" />
|
||||
<ui:Button name="play-button" text="Play" />
|
||||
<ui:Button name="settings-button" text="Settings" />
|
||||
<ui:Button name="quit-button" text="Quit" />
|
||||
</ui:VisualElement>
|
||||
</ui:UXML>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### USS (Styling)
|
||||
|
||||
```css
|
||||
/* MainMenu.uss */
|
||||
.container {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgb(30, 30, 30);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48px;
|
||||
color: white;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 200px;
|
||||
height: 50px;
|
||||
margin: 10px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
Button:hover {
|
||||
background-color: rgb(100, 150, 200);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### C# Scripting (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
public class MainMenu : MonoBehaviour {
|
||||
void OnEnable() {
|
||||
var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
|
||||
// Query elements by name
|
||||
var playButton = root.Q<Button>("play-button");
|
||||
var settingsButton = root.Q<Button>("settings-button");
|
||||
var quitButton = root.Q<Button>("quit-button");
|
||||
|
||||
// Register callbacks
|
||||
playButton.clicked += OnPlayClicked;
|
||||
settingsButton.clicked += OnSettingsClicked;
|
||||
quitButton.clicked += Application.Quit;
|
||||
}
|
||||
|
||||
void OnPlayClicked() {
|
||||
Debug.Log("Play clicked");
|
||||
// Load game scene
|
||||
}
|
||||
|
||||
void OnSettingsClicked() {
|
||||
Debug.Log("Settings clicked");
|
||||
// Open settings menu
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Common UI Elements
|
||||
|
||||
```csharp
|
||||
// Label (text display)
|
||||
var label = root.Q<Label>("score-label");
|
||||
label.text = "Score: 100";
|
||||
|
||||
// Button
|
||||
var button = root.Q<Button>("submit-button");
|
||||
button.clicked += OnSubmit;
|
||||
|
||||
// TextField (text input)
|
||||
var textField = root.Q<TextField>("name-input");
|
||||
string playerName = textField.value;
|
||||
|
||||
// Toggle (checkbox)
|
||||
var toggle = root.Q<Toggle>("music-toggle");
|
||||
bool isMusicEnabled = toggle.value;
|
||||
|
||||
// Slider
|
||||
var slider = root.Q<Slider>("volume-slider");
|
||||
float volume = slider.value; // 0-1
|
||||
|
||||
// DropdownField (dropdown menu)
|
||||
var dropdown = root.Q<DropdownField>("difficulty-dropdown");
|
||||
dropdown.choices = new List<string> { "Easy", "Normal", "Hard" };
|
||||
dropdown.value = "Normal";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Dynamic UI Creation (No UXML)
|
||||
|
||||
```csharp
|
||||
void CreateUI() {
|
||||
var root = GetComponent<UIDocument>().rootVisualElement;
|
||||
|
||||
// Create elements
|
||||
var container = new VisualElement();
|
||||
container.AddToClassList("container");
|
||||
|
||||
var label = new Label("Hello, UI Toolkit!");
|
||||
var button = new Button(() => Debug.Log("Clicked")) { text = "Click Me" };
|
||||
|
||||
container.Add(label);
|
||||
container.Add(button);
|
||||
root.Add(container);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### USS Flexbox Layout
|
||||
|
||||
```css
|
||||
/* Horizontal layout */
|
||||
.horizontal {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
/* Vertical layout (default) */
|
||||
.vertical {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Center children */
|
||||
.centered {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Spacing */
|
||||
.spaced {
|
||||
justify-content: space-between;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UGUI (Legacy Canvas UI)
|
||||
|
||||
### Basic Setup (Still Works in Unity 6)
|
||||
|
||||
```csharp
|
||||
// GameObject > UI > Canvas (creates Canvas, EventSystem)
|
||||
|
||||
// UI Elements:
|
||||
// - Text (use TextMeshPro instead)
|
||||
// - Button
|
||||
// - Image
|
||||
// - Slider
|
||||
// - Toggle
|
||||
// - InputField
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### UGUI Scripting
|
||||
|
||||
```csharp
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro; // TextMeshPro
|
||||
|
||||
public class LegacyUI : MonoBehaviour {
|
||||
public TextMeshProUGUI scoreText;
|
||||
public Button playButton;
|
||||
public Slider volumeSlider;
|
||||
|
||||
void Start() {
|
||||
// Update text
|
||||
scoreText.text = "Score: 100";
|
||||
|
||||
// Button click
|
||||
playButton.onClick.AddListener(OnPlayClicked);
|
||||
|
||||
// Slider value changed
|
||||
volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
|
||||
}
|
||||
|
||||
void OnPlayClicked() {
|
||||
Debug.Log("Play clicked");
|
||||
}
|
||||
|
||||
void OnVolumeChanged(float value) {
|
||||
AudioListener.volume = value;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TextMeshPro (Better Text Rendering)
|
||||
|
||||
```csharp
|
||||
// Install: Window > TextMeshPro > Import TMP Essential Resources
|
||||
|
||||
// Use TMP_Text instead of Unity's Text component
|
||||
using TMPro;
|
||||
|
||||
public TextMeshProUGUI tmpText;
|
||||
tmpText.text = "High Quality Text";
|
||||
tmpText.fontSize = 24;
|
||||
tmpText.color = Color.white;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Canvas Settings (UGUI)
|
||||
|
||||
### Render Modes
|
||||
|
||||
```csharp
|
||||
// Screen Space - Overlay: UI rendered on top of everything (no camera needed)
|
||||
// Screen Space - Camera: UI rendered by specific camera (allows effects)
|
||||
// World Space: UI in 3D world (e.g., floating health bars)
|
||||
```
|
||||
|
||||
### Canvas Scaler (Responsive UI)
|
||||
|
||||
```csharp
|
||||
// UI Scale Mode:
|
||||
// - Constant Pixel Size: UI elements have fixed pixel size
|
||||
// - Scale With Screen Size: UI scales based on reference resolution (RECOMMENDED)
|
||||
// - Constant Physical Size: UI elements have fixed physical size (cm)
|
||||
|
||||
// Example: Scale With Screen Size
|
||||
// Reference Resolution: 1920x1080
|
||||
// Screen Match Mode: Match Width Or Height (0.5 = balanced)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Layout Groups (UGUI)
|
||||
|
||||
### Horizontal Layout Group
|
||||
|
||||
```csharp
|
||||
// Auto-arranges children horizontally
|
||||
// Add: GameObject > Add Component > Horizontal Layout Group
|
||||
```
|
||||
|
||||
### Vertical Layout Group
|
||||
|
||||
```csharp
|
||||
// Auto-arranges children vertically
|
||||
```
|
||||
|
||||
### Grid Layout Group
|
||||
|
||||
```csharp
|
||||
// Arranges children in a grid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance (UI Toolkit vs UGUI)
|
||||
|
||||
### UI Toolkit Advantages
|
||||
- ✅ Faster rendering (retained mode)
|
||||
- ✅ Better for complex UIs with many elements
|
||||
- ✅ Easier styling (CSS-like)
|
||||
- ✅ Better for dynamic UIs
|
||||
|
||||
### UGUI Advantages
|
||||
- ✅ More mature, widely documented
|
||||
- ✅ Better integration with Unity Editor
|
||||
- ✅ Easier for beginners
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Health Bar (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
var healthBar = root.Q<VisualElement>("health-bar");
|
||||
healthBar.style.width = new StyleLength(new Length(healthPercent, LengthUnit.Percent));
|
||||
```
|
||||
|
||||
### Health Bar (UGUI)
|
||||
|
||||
```csharp
|
||||
public Image healthBarImage;
|
||||
|
||||
void UpdateHealth(float percent) {
|
||||
healthBarImage.fillAmount = percent; // 0-1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Fade In/Out (UI Toolkit)
|
||||
|
||||
```csharp
|
||||
IEnumerator FadeIn(VisualElement element, float duration) {
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration) {
|
||||
elapsed += Time.deltaTime;
|
||||
element.style.opacity = Mathf.Lerp(0f, 1f, elapsed / duration);
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### UI Toolkit Debugger
|
||||
- `Window > UI Toolkit > Debugger`
|
||||
- Inspect element hierarchy, styles, layout
|
||||
|
||||
### UGUI Event System Debugger
|
||||
- Select EventSystem in Hierarchy
|
||||
- Inspector shows active input module, raycast info
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/6000.0/Documentation/Manual/UIElements.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ui@2.0/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.ugui@2.0/manual/index.html
|
||||
378
docs/engine-reference/unity/plugins/addressables.md
Normal file
378
docs/engine-reference/unity/plugins/addressables.md
Normal file
@@ -0,0 +1,378 @@
|
||||
# Unity 6.3 — Addressables
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready
|
||||
**Package:** `com.unity.addressables` (Package Manager)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Addressables** is Unity's advanced asset management system that replaces `Resources.Load()`
|
||||
with async loading, remote content delivery, and better memory control.
|
||||
|
||||
**Use Addressables for:**
|
||||
- Async asset loading (non-blocking)
|
||||
- DLC and remote content
|
||||
- Memory optimization (load/unload on demand)
|
||||
- Asset dependency management
|
||||
- Large projects with many assets
|
||||
|
||||
**DON'T use Addressables for:**
|
||||
- Tiny projects (overhead not worth it)
|
||||
- Assets needed immediately at startup (use direct references)
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Install via Package Manager
|
||||
|
||||
1. `Window > Package Manager`
|
||||
2. Unity Registry > Search "Addressables"
|
||||
3. Install `Addressables`
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **Addressable Assets**
|
||||
- Assets marked as "Addressable" (assigned unique keys)
|
||||
- Can be loaded by key at runtime
|
||||
|
||||
### 2. **Asset Groups**
|
||||
- Organize assets (e.g., "UI", "Weapons", "Level1")
|
||||
- Groups determine build settings (local vs remote)
|
||||
|
||||
### 3. **Async Loading**
|
||||
- All loading is async (non-blocking)
|
||||
- Returns `AsyncOperationHandle`
|
||||
|
||||
### 4. **Reference Counting**
|
||||
- Addressables tracks asset usage
|
||||
- Must manually release assets when done
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Mark Assets as Addressable
|
||||
|
||||
1. Select asset in Project window
|
||||
2. Inspector > Check "Addressable"
|
||||
3. Assign key (e.g., "Enemies/Goblin")
|
||||
|
||||
**OR via script:**
|
||||
```csharp
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor.AddressableAssets;
|
||||
using UnityEditor.AddressableAssets.Settings;
|
||||
|
||||
AddressableAssetSettings.AddAssetEntry(guid, "MyAssetKey", "Default Local Group");
|
||||
#endif
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Create Groups
|
||||
|
||||
`Window > Asset Management > Addressables > Groups`
|
||||
|
||||
- **Default Local Group**: Bundled with build
|
||||
- **Remote Group**: Hosted on server (CDN)
|
||||
|
||||
---
|
||||
|
||||
## Basic Loading
|
||||
|
||||
### Load Asset Async
|
||||
|
||||
```csharp
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
public class AssetLoader : MonoBehaviour {
|
||||
async void Start() {
|
||||
// ✅ Load asset asynchronously
|
||||
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Enemies/Goblin");
|
||||
await handle.Task;
|
||||
|
||||
if (handle.Status == AsyncOperationStatus.Succeeded) {
|
||||
GameObject prefab = handle.Result;
|
||||
Instantiate(prefab);
|
||||
} else {
|
||||
Debug.LogError("Failed to load asset");
|
||||
}
|
||||
|
||||
// ⚠️ IMPORTANT: Release when done
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Load and Instantiate
|
||||
|
||||
```csharp
|
||||
async void SpawnEnemy() {
|
||||
// ✅ Load and instantiate in one step
|
||||
AsyncOperationHandle<GameObject> handle = Addressables.InstantiateAsync("Enemies/Goblin");
|
||||
await handle.Task;
|
||||
|
||||
GameObject enemy = handle.Result;
|
||||
// Use enemy...
|
||||
|
||||
// ✅ Release when destroying
|
||||
Addressables.ReleaseInstance(enemy);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Load Multiple Assets
|
||||
|
||||
```csharp
|
||||
async void LoadAllWeapons() {
|
||||
// Load all assets with label "Weapons"
|
||||
AsyncOperationHandle<IList<GameObject>> handle = Addressables.LoadAssetsAsync<GameObject>("Weapons", null);
|
||||
await handle.Task;
|
||||
|
||||
foreach (var weapon in handle.Result) {
|
||||
Debug.Log($"Loaded: {weapon.name}");
|
||||
}
|
||||
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Asset Labels (Tags)
|
||||
|
||||
### Assign Labels
|
||||
|
||||
1. `Window > Asset Management > Addressables > Groups`
|
||||
2. Select asset > Inspector > Labels > Add label (e.g., "Level1", "UI")
|
||||
|
||||
### Load by Label
|
||||
|
||||
```csharp
|
||||
// Load all assets with label "Level1"
|
||||
Addressables.LoadAssetsAsync<GameObject>("Level1", null);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Remote Content (DLC)
|
||||
|
||||
### Setup Remote Groups
|
||||
|
||||
1. Create new group: `Window > Addressables > Groups > Create New Group > Packed Assets`
|
||||
2. Group Settings:
|
||||
- **Build Path**: `ServerData/[BuildTarget]`
|
||||
- **Load Path**: `http://yourcdn.com/content/[BuildTarget]`
|
||||
|
||||
### Build Remote Content
|
||||
|
||||
1. `Window > Asset Management > Addressables > Build > New Build > Default Build Script`
|
||||
2. Upload `ServerData/` folder to CDN
|
||||
3. Game loads assets from remote server
|
||||
|
||||
---
|
||||
|
||||
## Preloading / Caching
|
||||
|
||||
### Download Dependencies
|
||||
|
||||
```csharp
|
||||
async void PreloadLevel() {
|
||||
// Download all assets in group without loading into memory
|
||||
AsyncOperationHandle handle = Addressables.DownloadDependenciesAsync("Level1");
|
||||
await handle.Task;
|
||||
|
||||
// Now "Level1" assets are cached, load instantly
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
```
|
||||
|
||||
### Check Download Size
|
||||
|
||||
```csharp
|
||||
async void CheckDownloadSize() {
|
||||
AsyncOperationHandle<long> handle = Addressables.GetDownloadSizeAsync("Level1");
|
||||
await handle.Task;
|
||||
|
||||
long sizeInBytes = handle.Result;
|
||||
Debug.Log($"Download size: {sizeInBytes / (1024 * 1024)} MB");
|
||||
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Management
|
||||
|
||||
### Release Assets
|
||||
|
||||
```csharp
|
||||
// ✅ Always release when done
|
||||
Addressables.Release(handle);
|
||||
|
||||
// ✅ For instantiated objects
|
||||
Addressables.ReleaseInstance(gameObject);
|
||||
```
|
||||
|
||||
### Check Reference Count
|
||||
|
||||
```csharp
|
||||
// Addressables uses reference counting
|
||||
// Asset is unloaded when refCount == 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Asset References (Inspector-Assigned)
|
||||
|
||||
### Use AssetReference
|
||||
|
||||
```csharp
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
public class EnemySpawner : MonoBehaviour {
|
||||
// ✅ Assign in Inspector (drag & drop)
|
||||
public AssetReference enemyPrefab;
|
||||
|
||||
async void SpawnEnemy() {
|
||||
AsyncOperationHandle<GameObject> handle = enemyPrefab.InstantiateAsync();
|
||||
await handle.Task;
|
||||
|
||||
GameObject enemy = handle.Result;
|
||||
// Use enemy...
|
||||
|
||||
enemyPrefab.ReleaseInstance(enemy);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scenes
|
||||
|
||||
### Load Addressable Scene
|
||||
|
||||
```csharp
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
async void LoadScene() {
|
||||
AsyncOperationHandle<SceneInstance> handle = Addressables.LoadSceneAsync("MainMenu", LoadSceneMode.Additive);
|
||||
await handle.Task;
|
||||
|
||||
SceneInstance sceneInstance = handle.Result;
|
||||
// Scene loaded
|
||||
|
||||
// Unload scene
|
||||
await Addressables.UnloadSceneAsync(handle).Task;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Lazy Loading (Load on Demand)
|
||||
|
||||
```csharp
|
||||
Dictionary<string, AsyncOperationHandle<GameObject>> loadedAssets = new();
|
||||
|
||||
async Task<GameObject> GetAsset(string key) {
|
||||
if (!loadedAssets.ContainsKey(key)) {
|
||||
var handle = Addressables.LoadAssetAsync<GameObject>(key);
|
||||
await handle.Task;
|
||||
loadedAssets[key] = handle;
|
||||
}
|
||||
return loadedAssets[key].Result;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Cleanup on Scene Unload
|
||||
|
||||
```csharp
|
||||
void OnDestroy() {
|
||||
// Release all handles
|
||||
foreach (var handle in loadedAssets.Values) {
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
loadedAssets.Clear();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Content Catalog Updates (Live Updates)
|
||||
|
||||
### Check for Catalog Updates
|
||||
|
||||
```csharp
|
||||
async void CheckForUpdates() {
|
||||
AsyncOperationHandle<List<string>> handle = Addressables.CheckForCatalogUpdates();
|
||||
await handle.Task;
|
||||
|
||||
if (handle.Result.Count > 0) {
|
||||
Debug.Log("Updates available");
|
||||
await Addressables.UpdateCatalogs(handle.Result).Task;
|
||||
}
|
||||
|
||||
Addressables.Release(handle);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- **Preload** frequently used assets at startup
|
||||
- **Release** assets immediately when not needed
|
||||
- Use **labels** to batch-load related assets
|
||||
- **Cache** remote content for offline use
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Addressables Event Viewer
|
||||
|
||||
`Window > Asset Management > Addressables > Event Viewer`
|
||||
|
||||
- Shows all load/release operations
|
||||
- Memory usage per asset
|
||||
- Reference counts
|
||||
|
||||
### Addressables Profiler
|
||||
|
||||
`Window > Asset Management > Addressables > Profiler`
|
||||
|
||||
- Real-time asset usage
|
||||
- Bundle loading stats
|
||||
|
||||
---
|
||||
|
||||
## Migration from Resources
|
||||
|
||||
```csharp
|
||||
// ❌ OLD: Resources.Load (synchronous, blocks frame)
|
||||
GameObject prefab = Resources.Load<GameObject>("Enemies/Goblin");
|
||||
|
||||
// ✅ NEW: Addressables (async, non-blocking)
|
||||
var handle = await Addressables.LoadAssetAsync<GameObject>("Enemies/Goblin").Task;
|
||||
GameObject prefab = handle.Result;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.addressables@2.0/manual/index.html
|
||||
- https://learn.unity.com/tutorial/addressables
|
||||
348
docs/engine-reference/unity/plugins/cinemachine.md
Normal file
348
docs/engine-reference/unity/plugins/cinemachine.md
Normal file
@@ -0,0 +1,348 @@
|
||||
# Unity 6.3 — Cinemachine
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready
|
||||
**Package:** `com.unity.cinemachine` v3.0+ (Package Manager)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Cinemachine** is Unity's virtual camera system that enables professional, dynamic camera
|
||||
behavior without manual scripting. It's the industry standard for Unity camera work.
|
||||
|
||||
**Use Cinemachine for:**
|
||||
- 3rd person follow cameras
|
||||
- Cutscenes and cinematics
|
||||
- Camera blending and transitions
|
||||
- Dynamic camera framing
|
||||
- Screen shake and camera effects
|
||||
|
||||
**⚠️ Knowledge Gap:** Cinemachine 3.0 (Unity 6) is a major rewrite from 2.x.
|
||||
Many API names and components changed.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Install via Package Manager
|
||||
|
||||
1. `Window > Package Manager`
|
||||
2. Unity Registry > Search "Cinemachine"
|
||||
3. Install `Cinemachine` (version 3.0+)
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **Virtual Cameras**
|
||||
- Define camera behavior (position, rotation, lens)
|
||||
- Multiple virtual cameras can exist; only one is "live" at a time
|
||||
|
||||
### 2. **Cinemachine Brain**
|
||||
- Component on main Camera
|
||||
- Blends between virtual cameras
|
||||
- Applies virtual camera settings to Unity Camera
|
||||
|
||||
### 3. **Priorit**ies**
|
||||
- Virtual cameras have priority values
|
||||
- Highest priority camera is active
|
||||
- Blends smoothly when priority changes
|
||||
|
||||
---
|
||||
|
||||
## Basic Setup
|
||||
|
||||
### 1. Add Cinemachine Brain to Main Camera
|
||||
|
||||
```csharp
|
||||
// Automatically added when creating first virtual camera
|
||||
// Or manually: Add Component > Cinemachine Brain
|
||||
```
|
||||
|
||||
### 2. Create Virtual Camera
|
||||
|
||||
`GameObject > Cinemachine > Cinemachine Camera`
|
||||
|
||||
This creates a **CinemachineCamera** GameObject with default settings.
|
||||
|
||||
---
|
||||
|
||||
## Virtual Camera Components
|
||||
|
||||
### CinemachineCamera (Unity 6 / Cinemachine 3.0+)
|
||||
|
||||
```csharp
|
||||
using Unity.Cinemachine;
|
||||
|
||||
public class CameraController : MonoBehaviour {
|
||||
public CinemachineCamera virtualCamera;
|
||||
|
||||
void Start() {
|
||||
// Set priority (higher = active)
|
||||
virtualCamera.Priority = 10;
|
||||
|
||||
// Set follow target
|
||||
virtualCamera.Follow = playerTransform;
|
||||
|
||||
// Set look-at target
|
||||
virtualCamera.LookAt = playerTransform;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Follow Modes (Body Component)
|
||||
|
||||
### 3rd Person Follow (Orbital Follow)
|
||||
|
||||
```csharp
|
||||
// In Inspector:
|
||||
// CinemachineCamera > Body > 3rd Person Follow
|
||||
|
||||
// Configure:
|
||||
// - Shoulder Offset: (0.5, 0, 0) for over-shoulder
|
||||
// - Camera Distance: 5.0
|
||||
// - Vertical Damping: 0.5 (smooth up/down)
|
||||
```
|
||||
|
||||
### Framing Transposer (Smooth Follow)
|
||||
|
||||
```csharp
|
||||
// CinemachineCamera > Body > Position Composer
|
||||
|
||||
// Configure:
|
||||
// - Screen Position: Center (0.5, 0.5)
|
||||
// - Dead Zone: Don't move camera if target within zone
|
||||
// - Damping: Smooth following
|
||||
```
|
||||
|
||||
### Hard Lock (Exact Follow)
|
||||
|
||||
```csharp
|
||||
// CinemachineCamera > Body > Hard Lock to Target
|
||||
// Camera exactly matches target position (no offset or damping)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Aim Modes (Aim Component)
|
||||
|
||||
### Composer (Frame Target)
|
||||
|
||||
```csharp
|
||||
// CinemachineCamera > Aim > Composer
|
||||
|
||||
// Configure:
|
||||
// - Tracked Object Offset: Aim at target's head instead of feet
|
||||
// - Screen Position: Where target appears on screen
|
||||
// - Dead Zone: Don't rotate if target within zone
|
||||
```
|
||||
|
||||
### Look At Target
|
||||
|
||||
```csharp
|
||||
// CinemachineCamera > Aim > Rotate With Follow Target
|
||||
// Camera rotation matches target rotation (e.g., first-person)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blending Between Cameras
|
||||
|
||||
### Priority-Based Blending
|
||||
|
||||
```csharp
|
||||
public CinemachineCamera normalCamera; // Priority: 10
|
||||
public CinemachineCamera aimCamera; // Priority: 5
|
||||
|
||||
void StartAiming() {
|
||||
// Set aim camera to higher priority
|
||||
aimCamera.Priority = 15; // Now active
|
||||
// Brain automatically blends from normalCamera to aimCamera
|
||||
}
|
||||
|
||||
void StopAiming() {
|
||||
aimCamera.Priority = 5; // Back to normal
|
||||
}
|
||||
```
|
||||
|
||||
### Custom Blend Times
|
||||
|
||||
```csharp
|
||||
// Create Custom Blends Asset:
|
||||
// Assets > Create > Cinemachine > Cinemachine Blender Settings
|
||||
|
||||
// In Cinemachine Brain:
|
||||
// - Custom Blends = your asset
|
||||
// - Configure blend times per camera pair
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Camera Shake
|
||||
|
||||
### Impulse Source (Trigger Shake)
|
||||
|
||||
```csharp
|
||||
using Unity.Cinemachine;
|
||||
|
||||
public class ExplosionShake : MonoBehaviour {
|
||||
public CinemachineImpulseSource impulseSource;
|
||||
|
||||
void Explode() {
|
||||
// Trigger camera shake
|
||||
impulseSource.GenerateImpulse();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Impulse Listener (Receive Shake)
|
||||
|
||||
```csharp
|
||||
// Add to CinemachineCamera:
|
||||
// Add Component > CinemachineImpulseListener
|
||||
|
||||
// Impulse listener automatically receives shake from nearby Impulse Sources
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Freelook Camera (Third Person with Mouse Look)
|
||||
|
||||
### Cinemachine Free Look
|
||||
|
||||
```csharp
|
||||
// GameObject > Cinemachine > Cinemachine Free Look
|
||||
|
||||
// Creates 3 rigs (Top, Middle, Bottom) that blend based on vertical input
|
||||
// Configure:
|
||||
// - Orbit Radius: Distance from target
|
||||
// - Height Offset: Camera height at each rig
|
||||
// - X/Y Axis: Mouse or joystick input
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## State-Driven Camera (Anim ator-Based)
|
||||
|
||||
### Cinemachine State-Driven Camera
|
||||
|
||||
```csharp
|
||||
// GameObject > Cinemachine > Cinemachine State-Driven Camera
|
||||
|
||||
// Configure:
|
||||
// - Animated Target: Character with Animator
|
||||
// - Layer: Animator layer to track
|
||||
// - State: Assign camera per animation state (Idle, Run, Jump, etc.)
|
||||
|
||||
// Camera automatically switches based on animation state
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dolly Tracks (Cutscenes)
|
||||
|
||||
### Cinemachine Dolly Track
|
||||
|
||||
```csharp
|
||||
// 1. Create Spline: GameObject > Cinemachine > Cinemachine Spline
|
||||
|
||||
// 2. Create Dolly Camera:
|
||||
// GameObject > Cinemachine > Cinemachine Camera
|
||||
// Body > Spline Dolly
|
||||
// Assign Spline
|
||||
|
||||
// 3. Animate dolly position on spline (Timeline or script)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Third-Person Follow Camera
|
||||
|
||||
```csharp
|
||||
// CinemachineCamera
|
||||
// - Follow: Player Transform
|
||||
// - Body: 3rd Person Follow (shoulder offset, distance: 5)
|
||||
// - Aim: Composer (frame player at center)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Aiming Camera (Zoom In)
|
||||
|
||||
```csharp
|
||||
// Normal Camera (Priority 10):
|
||||
// - Distance: 5.0
|
||||
|
||||
// Aim Camera (Priority 5):
|
||||
// - Distance: 2.0
|
||||
// - FOV: Narrower
|
||||
|
||||
// Script:
|
||||
void StartAiming() {
|
||||
aimCamera.Priority = 15; // Blend to aim camera
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Cutscene Camera Sequence
|
||||
|
||||
```csharp
|
||||
// Use Timeline:
|
||||
// 1. Create Timeline (Assets > Create > Timeline)
|
||||
// 2. Add Cinemachine Track
|
||||
// 3. Add virtual cameras as clips
|
||||
// 4. Timeline automatically blends between cameras
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration from Cinemachine 2.x (Unity 2021)
|
||||
|
||||
### API Changes (Unity 6 / Cinemachine 3.0)
|
||||
|
||||
```csharp
|
||||
// ❌ OLD (Cinemachine 2.x):
|
||||
CinemachineVirtualCamera vcam;
|
||||
vcam.m_Follow = target;
|
||||
|
||||
// ✅ NEW (Cinemachine 3.0+):
|
||||
CinemachineCamera vcam;
|
||||
vcam.Follow = target; // Cleaner API
|
||||
```
|
||||
|
||||
**Major Changes:**
|
||||
- `CinemachineVirtualCamera` → `CinemachineCamera`
|
||||
- `m_Follow`, `m_LookAt` → `Follow`, `LookAt` (no "m_" prefix)
|
||||
- Components renamed for clarity
|
||||
- Better performance
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Limit active virtual cameras (only activate when needed)
|
||||
- Use lower-priority cameras instead of destroying/creating
|
||||
- Disable virtual cameras when far from player
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Cinemachine Debug
|
||||
|
||||
```csharp
|
||||
// Window > Analysis > Cinemachine Debugger
|
||||
// Shows active camera, blend info, shot quality
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.cinemachine@3.0/manual/index.html
|
||||
- https://learn.unity.com/tutorial/cinemachine
|
||||
420
docs/engine-reference/unity/plugins/dots-entities.md
Normal file
420
docs/engine-reference/unity/plugins/dots-entities.md
Normal file
@@ -0,0 +1,420 @@
|
||||
# Unity 6.3 — DOTS / Entities (ECS)
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready (Entities 1.3+, Unity 6.3 LTS)
|
||||
**Package:** `com.unity.entities` (Package Manager)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**DOTS (Data-Oriented Technology Stack)** is Unity's high-performance ECS (Entity Component System)
|
||||
framework. It's designed for games with massive scale (1000s-10,000s of entities).
|
||||
|
||||
**Use DOTS for:**
|
||||
- RTS games (1000s of units)
|
||||
- Simulations (crowds, traffic, physics)
|
||||
- Procedural content generation
|
||||
- Performance-critical systems
|
||||
|
||||
**DON'T use DOTS for:**
|
||||
- Small games (overhead not worth it)
|
||||
- Gameplay requiring frequent structural changes
|
||||
- Heavy use of UnityEngine APIs (MonoBehaviour is easier)
|
||||
|
||||
**⚠️ Knowledge Gap:** Entities 1.0+ (Unity 6) is a complete rewrite from 0.x.
|
||||
Many tutorials for Entities 0.x are now outdated.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Install via Package Manager
|
||||
|
||||
1. `Window > Package Manager`
|
||||
2. Unity Registry > Search "Entities"
|
||||
3. Install:
|
||||
- `Entities` (ECS core)
|
||||
- `Burst` (LLVM compiler)
|
||||
- `Jobs` (auto-installed)
|
||||
- `Mathematics` (SIMD math)
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **Entity**
|
||||
- Lightweight ID (int)
|
||||
- No behavior, just an identifier
|
||||
|
||||
### 2. **Component**
|
||||
- Data only (no methods)
|
||||
- Struct implementing `IComponentData`
|
||||
|
||||
### 3. **System**
|
||||
- Logic that operates on components
|
||||
- Struct implementing `ISystem`
|
||||
|
||||
### 4. **Archetype**
|
||||
- Unique combination of component types
|
||||
- Entities with same components share archetype
|
||||
|
||||
---
|
||||
|
||||
## Basic ECS Pattern
|
||||
|
||||
### Define Component
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
// ✅ Component: Data only, no methods
|
||||
public struct Position : IComponentData {
|
||||
public float3 Value;
|
||||
}
|
||||
|
||||
public struct Velocity : IComponentData {
|
||||
public float3 Value;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Define System
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
using Unity.Burst;
|
||||
|
||||
// ✅ System: Logic that processes entities
|
||||
[BurstCompile]
|
||||
public partial struct MovementSystem : ISystem {
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
float deltaTime = SystemAPI.Time.DeltaTime;
|
||||
|
||||
// Query all entities with Position + Velocity
|
||||
foreach (var (transform, velocity) in
|
||||
SystemAPI.Query<RefRW<Position>, RefRO<Velocity>>()) {
|
||||
|
||||
transform.ValueRW.Value += velocity.ValueRO.Value * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Create Entities
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
using Unity.Mathematics;
|
||||
|
||||
public partial class EntitySpawner : SystemBase {
|
||||
protected override void OnUpdate() {
|
||||
var em = EntityManager;
|
||||
|
||||
// Create entity
|
||||
Entity entity = em.CreateEntity();
|
||||
|
||||
// Add components
|
||||
em.AddComponentData(entity, new Position { Value = float3.zero });
|
||||
em.AddComponentData(entity, new Velocity { Value = new float3(1, 0, 0) });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Hybrid ECS (MonoBehaviour + ECS)
|
||||
|
||||
### Baker (Convert GameObject to Entity)
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
using UnityEngine;
|
||||
|
||||
public class PlayerAuthoring : MonoBehaviour {
|
||||
public float speed;
|
||||
}
|
||||
|
||||
public class PlayerBaker : Baker<PlayerAuthoring> {
|
||||
public override void Bake(PlayerAuthoring authoring) {
|
||||
var entity = GetEntity(TransformUsageFlags.Dynamic);
|
||||
|
||||
AddComponent(entity, new Position { Value = authoring.transform.position });
|
||||
AddComponent(entity, new Velocity { Value = new float3(authoring.speed, 0, 0) });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**How it works:**
|
||||
1. Add `PlayerAuthoring` to GameObject in editor
|
||||
2. Baker automatically converts to Entity at runtime
|
||||
3. Entity has Position + Velocity components
|
||||
|
||||
---
|
||||
|
||||
## Queries
|
||||
|
||||
### Query All Entities with Components
|
||||
|
||||
```csharp
|
||||
foreach (var (position, velocity) in
|
||||
SystemAPI.Query<RefRW<Position>, RefRO<Velocity>>()) {
|
||||
|
||||
position.ValueRW.Value += velocity.ValueRO.Value * deltaTime;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Query with Entity
|
||||
|
||||
```csharp
|
||||
foreach (var (position, velocity, entity) in
|
||||
SystemAPI.Query<RefRW<Position>, RefRO<Velocity>>().WithEntityAccess()) {
|
||||
|
||||
// Access entity ID
|
||||
Debug.Log($"Entity: {entity}");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Query with Filters
|
||||
|
||||
```csharp
|
||||
// Only entities with "Enemy" tag
|
||||
foreach (var position in
|
||||
SystemAPI.Query<RefRW<Position>>().WithAll<EnemyTag>()) {
|
||||
// Process enemies only
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Jobs (Parallel Execution)
|
||||
|
||||
### IJobEntity (Parallel Foreach)
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
using Unity.Burst;
|
||||
|
||||
[BurstCompile]
|
||||
public partial struct MovementJob : IJobEntity {
|
||||
public float DeltaTime;
|
||||
|
||||
// Execute runs in parallel for each entity
|
||||
void Execute(ref Position position, in Velocity velocity) {
|
||||
position.Value += velocity.Value * DeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public partial struct MovementSystem : ISystem {
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
var job = new MovementJob {
|
||||
DeltaTime = SystemAPI.Time.DeltaTime
|
||||
};
|
||||
job.ScheduleParallel(); // Parallel execution
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Burst Compiler (Performance)
|
||||
|
||||
### Enable Burst
|
||||
|
||||
```csharp
|
||||
using Unity.Burst;
|
||||
|
||||
[BurstCompile] // 10-100x faster than regular C#
|
||||
public partial struct MySystem : ISystem {
|
||||
[BurstCompile]
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
// Burst-compiled code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Burst Restrictions:**
|
||||
- No managed references (classes, strings, etc.)
|
||||
- Only blittable types (structs, primitives, Unity.Mathematics types)
|
||||
- No exceptions
|
||||
|
||||
---
|
||||
|
||||
## Entity Command Buffers (Structural Changes)
|
||||
|
||||
### Deferred Structural Changes
|
||||
|
||||
```csharp
|
||||
using Unity.Entities;
|
||||
|
||||
public partial struct SpawnSystem : ISystem {
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
var ecb = new EntityCommandBuffer(Allocator.Temp);
|
||||
|
||||
// Defer entity creation (don't modify during iteration)
|
||||
foreach (var spawner in SystemAPI.Query<Spawner>()) {
|
||||
Entity newEntity = ecb.CreateEntity();
|
||||
ecb.AddComponent(newEntity, new Position { Value = spawner.SpawnPos });
|
||||
}
|
||||
|
||||
ecb.Playback(state.EntityManager); // Apply changes
|
||||
ecb.Dispose();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dynamic Buffers (Array-Like Components)
|
||||
|
||||
### Define Dynamic Buffer
|
||||
|
||||
```csharp
|
||||
public struct PathWaypoint : IBufferElementData {
|
||||
public float3 Position;
|
||||
}
|
||||
```
|
||||
|
||||
### Use Dynamic Buffer
|
||||
|
||||
```csharp
|
||||
// Add buffer to entity
|
||||
var buffer = EntityManager.AddBuffer<PathWaypoint>(entity);
|
||||
buffer.Add(new PathWaypoint { Position = new float3(0, 0, 0) });
|
||||
buffer.Add(new PathWaypoint { Position = new float3(10, 0, 0) });
|
||||
|
||||
// Query buffer
|
||||
foreach (var buffer in SystemAPI.Query<DynamicBuffer<PathWaypoint>>()) {
|
||||
foreach (var waypoint in buffer) {
|
||||
Debug.Log(waypoint.Position);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tags (Zero-Size Components)
|
||||
|
||||
### Define Tag
|
||||
|
||||
```csharp
|
||||
public struct EnemyTag : IComponentData { } // Empty component = tag
|
||||
```
|
||||
|
||||
### Use Tag for Filtering
|
||||
|
||||
```csharp
|
||||
// Only process entities with EnemyTag
|
||||
foreach (var position in
|
||||
SystemAPI.Query<RefRW<Position>>().WithAll<EnemyTag>()) {
|
||||
// Enemy-specific logic
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System Ordering
|
||||
|
||||
### Explicit Ordering
|
||||
|
||||
```csharp
|
||||
[UpdateBefore(typeof(PhysicsSystem))]
|
||||
public partial struct InputSystem : ISystem { }
|
||||
|
||||
[UpdateAfter(typeof(PhysicsSystem))]
|
||||
public partial struct RenderSystem : ISystem { }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### Chunk Iteration (Maximum Performance)
|
||||
|
||||
```csharp
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
var query = SystemAPI.QueryBuilder().WithAll<Position, Velocity>().Build();
|
||||
|
||||
var chunks = query.ToArchetypeChunkArray(Allocator.Temp);
|
||||
var positionType = state.GetComponentTypeHandle<Position>();
|
||||
var velocityType = state.GetComponentTypeHandle<Velocity>(true); // Read-only
|
||||
|
||||
foreach (var chunk in chunks) {
|
||||
var positions = chunk.GetNativeArray(ref positionType);
|
||||
var velocities = chunk.GetNativeArray(ref velocityType);
|
||||
|
||||
for (int i = 0; i < chunk.Count; i++) {
|
||||
positions[i] = new Position {
|
||||
Value = positions[i].Value + velocities[i].Value * deltaTime
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
chunks.Dispose();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration from MonoBehaviour
|
||||
|
||||
```csharp
|
||||
// ❌ OLD: MonoBehaviour (OOP)
|
||||
public class Enemy : MonoBehaviour {
|
||||
public float speed;
|
||||
void Update() {
|
||||
transform.position += Vector3.forward * speed * Time.deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ NEW: DOTS (ECS)
|
||||
public struct EnemyData : IComponentData {
|
||||
public float Speed;
|
||||
}
|
||||
|
||||
[BurstCompile]
|
||||
public partial struct EnemyMovementSystem : ISystem {
|
||||
public void OnUpdate(ref SystemState state) {
|
||||
float dt = SystemAPI.Time.DeltaTime;
|
||||
foreach (var (transform, enemy) in
|
||||
SystemAPI.Query<RefRW<LocalTransform>, RefRO<EnemyData>>()) {
|
||||
transform.ValueRW.Position += new float3(0, 0, enemy.ValueRO.Speed * dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Entities Hierarchy Window
|
||||
|
||||
`Window > Entities > Hierarchy`
|
||||
|
||||
- Shows all entities and their components
|
||||
- Filter by archetype, component type
|
||||
|
||||
### Entities Profiler
|
||||
|
||||
`Window > Analysis > Profiler > Entities`
|
||||
|
||||
- System execution times
|
||||
- Memory usage per archetype
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unity3d.com/Packages/com.unity.entities@1.3/manual/index.html
|
||||
- https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html
|
||||
- https://learn.unity.com/tutorial/entity-component-system
|
||||
210
docs/engine-reference/unreal/PLUGINS.md
Normal file
210
docs/engine-reference/unreal/PLUGINS.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Unreal Engine 5.7 — Optional Plugins & Systems
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
This document indexes **optional plugins and systems** available in Unreal Engine 5.7.
|
||||
These are NOT part of the core engine but are commonly used for specific game types.
|
||||
|
||||
---
|
||||
|
||||
## How to Use This Guide
|
||||
|
||||
**✅ Detailed Documentation Available** - See `plugins/` directory for comprehensive guides
|
||||
**🟡 Brief Overview Only** - Links to official docs, use WebSearch for details
|
||||
**⚠️ Experimental** - May have breaking changes in future versions
|
||||
**📦 Plugin Required** - Must be enabled in `Edit > Plugins`
|
||||
|
||||
---
|
||||
|
||||
## Production-Ready Systems (Detailed Docs Available)
|
||||
|
||||
### ✅ Gameplay Ability System (GAS)
|
||||
- **Purpose:** Modular ability system (abilities, attributes, effects, cooldowns, costs)
|
||||
- **When to use:** RPGs, MOBAs, shooters with abilities, any ability-based gameplay
|
||||
- **Knowledge Gap:** GAS stable since UE4, UE5 improvements post-cutoff
|
||||
- **Status:** Production-Ready
|
||||
- **Plugin:** `GameplayAbilities` (built-in, enable in Plugins)
|
||||
- **Detailed Docs:** [plugins/gameplay-ability-system.md](plugins/gameplay-ability-system.md)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/gameplay-ability-system-for-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### ✅ CommonUI
|
||||
- **Purpose:** Cross-platform UI framework (automatic gamepad/mouse/touch input routing)
|
||||
- **When to use:** Multi-platform games (console + PC), input-agnostic UI
|
||||
- **Knowledge Gap:** Production-ready in UE5+, major improvements post-cutoff
|
||||
- **Status:** Production-Ready
|
||||
- **Plugin:** `CommonUI` (built-in, enable in Plugins)
|
||||
- **Detailed Docs:** [plugins/common-ui.md](plugins/common-ui.md)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/commonui-plugin-for-advanced-user-interfaces-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### ✅ Gameplay Camera System
|
||||
- **Purpose:** Modular camera management (camera modes, blending, context-aware cameras)
|
||||
- **When to use:** Games needing dynamic camera behavior (3rd person, aiming, vehicles)
|
||||
- **Knowledge Gap:** NEW in UE 5.5, completely post-cutoff
|
||||
- **Status:** ⚠️ Experimental (UE 5.5-5.7)
|
||||
- **Plugin:** `GameplayCameras` (built-in, enable in Plugins)
|
||||
- **Detailed Docs:** [plugins/gameplay-camera-system.md](plugins/gameplay-camera-system.md)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/gameplay-cameras-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### ✅ PCG (Procedural Content Generation)
|
||||
- **Purpose:** Node-based procedural world generation (foliage, props, terrain details)
|
||||
- **When to use:** Open worlds, procedural levels, large-scale environment population
|
||||
- **Knowledge Gap:** Experimental in UE 5.0-5.6, production-ready in 5.7
|
||||
- **Status:** Production-Ready (as of UE 5.7)
|
||||
- **Plugin:** `PCG` (built-in, enable in Plugins)
|
||||
- **Detailed Docs:** [plugins/pcg.md](plugins/pcg.md)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/procedural-content-generation-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
## Other Production-Ready Plugins (Brief Overview)
|
||||
|
||||
### 🟡 Mass Entity
|
||||
- **Purpose:** High-performance ECS for large-scale AI/crowds (10,000+ entities)
|
||||
- **When to use:** RTS, city simulators, massive crowds, large-scale AI
|
||||
- **Status:** Production-Ready (UE 5.1+)
|
||||
- **Plugin:** `MassEntity`, `MassGameplay`, `MassCrowd`
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/mass-entity-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Niagara Fluids
|
||||
- **Purpose:** GPU fluid simulation (smoke, fire, liquids)
|
||||
- **When to use:** Realistic fire/smoke effects, water simulation
|
||||
- **Status:** Experimental → Production-Ready (UE 5.4+)
|
||||
- **Plugin:** `NiagaraFluids` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/niagara-fluids-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Water Plugin
|
||||
- **Purpose:** Ocean, river, lake rendering with buoyancy
|
||||
- **When to use:** Games with water bodies, boats, swimming
|
||||
- **Status:** Production-Ready (UE 5.0+)
|
||||
- **Plugin:** `Water` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/water-system-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Landmass Plugin
|
||||
- **Purpose:** Terrain sculpting and landscape editing
|
||||
- **When to use:** Large-scale terrain modification, procedural landscapes
|
||||
- **Status:** Production-Ready
|
||||
- **Plugin:** `Landmass` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/landmass-plugin-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Chaos Destruction
|
||||
- **Purpose:** Real-time fracture and destruction
|
||||
- **When to use:** Destructible environments (walls, buildings, objects)
|
||||
- **Status:** Production-Ready (UE 5.0+)
|
||||
- **Plugin:** `ChaosDestruction` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/destruction-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Chaos Vehicle
|
||||
- **Purpose:** Advanced vehicle physics (wheeled vehicles, suspension)
|
||||
- **When to use:** Racing games, vehicle-heavy gameplay
|
||||
- **Status:** Production-Ready (replaces PhysX Vehicles)
|
||||
- **Plugin:** `ChaosVehicles` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/chaos-vehicles-overview-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Geometry Scripting
|
||||
- **Purpose:** Runtime procedural mesh generation and editing
|
||||
- **When to use:** Dynamic mesh creation, procedural modeling
|
||||
- **Status:** Production-Ready (UE 5.1+)
|
||||
- **Plugin:** `GeometryScripting` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/geometry-scripting-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### 🟡 Motion Design Tools
|
||||
- **Purpose:** Motion graphics, procedural animation, keyframe animation
|
||||
- **When to use:** UI animations, procedural motion, keyframed sequences
|
||||
- **Status:** Experimental → Production-Ready (UE 5.4+)
|
||||
- **Plugin:** `MotionDesign` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/motion-design-mode-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
## Experimental Plugins (Use with Caution)
|
||||
|
||||
### ⚠️ AI Assistant (UE 5.7+)
|
||||
- **Purpose:** In-editor AI guidance and help
|
||||
- **Status:** Experimental
|
||||
- **Plugin:** Enable in UE 5.7 settings
|
||||
- **Official:** Announced in UE 5.7 release
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ OpenXR (VR/AR)
|
||||
- **Purpose:** Cross-platform VR/AR support
|
||||
- **When to use:** VR/AR games
|
||||
- **Status:** Production-Ready for VR, Experimental for AR
|
||||
- **Plugin:** `OpenXR` (built-in)
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/openxr-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
### ⚠️ Online Subsystem (EOS, Steam, etc.)
|
||||
- **Purpose:** Platform-agnostic online services (matchmaking, friends, achievements)
|
||||
- **When to use:** Multiplayer games with online features
|
||||
- **Status:** Production-Ready
|
||||
- **Plugin:** `OnlineSubsystem`, `OnlineSubsystemEOS`, `OnlineSubsystemSteam`
|
||||
- **Official:** https://docs.unrealengine.com/5.7/en-US/online-subsystem-in-unreal-engine/
|
||||
|
||||
---
|
||||
|
||||
## Deprecated Plugins (Avoid for New Projects)
|
||||
|
||||
### ❌ PhysX Vehicles
|
||||
- **Deprecated:** Use Chaos Vehicles instead
|
||||
- **Status:** Legacy, not recommended
|
||||
|
||||
---
|
||||
|
||||
### ❌ Old Replication Graph
|
||||
- **Deprecated:** Replaced by Iris (UE 5.1+)
|
||||
- **Status:** Use Iris for modern networking
|
||||
|
||||
---
|
||||
|
||||
## On-Demand WebSearch Strategy
|
||||
|
||||
For plugins NOT listed above, use the following approach when users ask:
|
||||
|
||||
1. **WebSearch** for latest documentation: `"Unreal Engine 5.7 [plugin name]"`
|
||||
2. Verify if plugin is:
|
||||
- Post-cutoff (beyond May 2025 training data)
|
||||
- Experimental vs Production-Ready
|
||||
- Still supported in UE 5.7
|
||||
3. Optionally cache findings in `plugins/[plugin-name].md` for future reference
|
||||
|
||||
---
|
||||
|
||||
## Quick Decision Guide
|
||||
|
||||
**I need abilities/skills/buffs** → **Gameplay Ability System (GAS)**
|
||||
**I need cross-platform UI (console + PC)** → **CommonUI**
|
||||
**I need dynamic cameras** → **Gameplay Camera System**
|
||||
**I need procedural worlds** → **PCG**
|
||||
**I need large crowds (1000s of AI)** → **Mass Entity**
|
||||
**I need destructible environments** → **Chaos Destruction**
|
||||
**I need vehicles** → **Chaos Vehicles**
|
||||
**I need water/oceans** → **Water Plugin**
|
||||
**I need VR/AR** → **OpenXR**
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-02-13
|
||||
**Engine Version:** Unreal Engine 5.7
|
||||
**LLM Knowledge Cutoff:** May 2025
|
||||
54
docs/engine-reference/unreal/VERSION.md
Normal file
54
docs/engine-reference/unreal/VERSION.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Unreal Engine — Version Reference
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Engine Version** | Unreal Engine 5.7 |
|
||||
| **Release Date** | November 2025 |
|
||||
| **Project Pinned** | 2026-02-13 |
|
||||
| **Last Docs Verified** | 2026-02-13 |
|
||||
| **LLM Knowledge Cutoff** | May 2025 |
|
||||
|
||||
## Knowledge Gap Warning
|
||||
|
||||
The LLM's training data likely covers Unreal Engine up to ~5.3. Versions 5.4, 5.5,
|
||||
5.6, and 5.7 introduced significant changes that the model does NOT know about.
|
||||
Always cross-reference this directory before suggesting Unreal API calls.
|
||||
|
||||
## Post-Cutoff Version Timeline
|
||||
|
||||
| Version | Release | Risk Level | Key Theme |
|
||||
|---------|---------|------------|-----------|
|
||||
| 5.4 | ~Mid 2025 | HIGH | Motion Design tools, animation improvements, PCG enhancements |
|
||||
| 5.5 | ~Sep 2025 | HIGH | Megalights (millions of lights), animation authoring, MegaCity demo |
|
||||
| 5.6 | ~Oct 2025 | MEDIUM | Performance optimizations, bug fixes |
|
||||
| 5.7 | Nov 2025 | HIGH | PCG production-ready, Substrate production-ready, AI assistant |
|
||||
|
||||
## Major Changes from UE 5.3 to UE 5.7
|
||||
|
||||
### Breaking Changes
|
||||
- **Substrate Material System**: New material framework (replaces legacy materials)
|
||||
- **PCG (Procedural Content Generation)**: Production-ready, major API changes
|
||||
- **Megalights**: New lighting system (millions of dynamic lights)
|
||||
- **Animation Authoring**: New rigging and animation tools
|
||||
- **AI Assistant**: In-editor AI guidance (experimental)
|
||||
|
||||
### New Features (Post-Cutoff)
|
||||
- **Megalights**: Dynamic lighting at massive scale (millions of lights)
|
||||
- **Substrate Materials**: Production-ready modular material system
|
||||
- **PCG Framework**: Procedural world generation (production-ready in 5.7)
|
||||
- **Enhanced Virtual Production**: MetaHuman integration, deeper VP workflows
|
||||
- **Animation Improvements**: Better rigging, blending, procedural animation
|
||||
- **AI Assistant**: In-editor AI help (experimental)
|
||||
|
||||
### Deprecated Systems
|
||||
- **Legacy Material System**: Migrate to Substrate for new projects
|
||||
- **Old PCG API**: Use new production-ready PCG API (5.7+)
|
||||
|
||||
## Verified Sources
|
||||
|
||||
- Official docs: https://docs.unrealengine.com/5.7/
|
||||
- UE 5.7 release notes: https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-7-release-notes
|
||||
- What's new in 5.7: https://dev.epicgames.com/documentation/en-us/unreal-engine/whats-new
|
||||
- UE 5.7 announcement: https://www.unrealengine.com/en-US/news/unreal-engine-5-7-is-now-available
|
||||
- UE 5.5 blog: https://www.unrealengine.com/en-US/blog/unreal-engine-5-5-is-now-available
|
||||
- Migration guides: https://docs.unrealengine.com/5.7/en-US/upgrading-projects/
|
||||
150
docs/engine-reference/unreal/breaking-changes.md
Normal file
150
docs/engine-reference/unreal/breaking-changes.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# Unreal Engine 5.7 — Breaking Changes
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
This document tracks breaking API changes and behavioral differences between Unreal Engine 5.3
|
||||
(likely in model training) and Unreal Engine 5.7 (current version). Organized by risk level.
|
||||
|
||||
## HIGH RISK — Will Break Existing Code
|
||||
|
||||
### Substrate Material System (Production-Ready in 5.7)
|
||||
**Versions:** UE 5.5+ (experimental), 5.7 (production-ready)
|
||||
|
||||
Substrate replaces the legacy material system with a modular, physically accurate framework.
|
||||
|
||||
```cpp
|
||||
// ❌ OLD: Legacy material nodes (still work but deprecated)
|
||||
// Standard material graph with Base Color, Metallic, Roughness, etc.
|
||||
|
||||
// ✅ NEW: Substrate material layers
|
||||
// Use Substrate nodes: Substrate Slab, Substrate Blend, etc.
|
||||
// Modular material authoring with true physical accuracy
|
||||
```
|
||||
|
||||
**Migration:** Enable Substrate in `Project Settings > Engine > Substrate` and rebuild materials using Substrate nodes.
|
||||
|
||||
---
|
||||
|
||||
### PCG (Procedural Content Generation) API Overhaul
|
||||
**Versions:** UE 5.7 (production-ready)
|
||||
|
||||
PCG framework reached production-ready status with major API changes.
|
||||
|
||||
```cpp
|
||||
// ❌ OLD: Experimental PCG API (pre-5.7)
|
||||
// Old node types, unstable API
|
||||
|
||||
// ✅ NEW: Production PCG API (5.7+)
|
||||
// Use FPCGContext, IPCGElement, new node types
|
||||
// Stable API, production-ready workflow
|
||||
```
|
||||
|
||||
**Migration:** Follow PCG migration guide in 5.7 docs. Expect significant refactoring for experimental PCG code.
|
||||
|
||||
---
|
||||
|
||||
### Megalights Rendering System
|
||||
**Versions:** UE 5.5+
|
||||
|
||||
New lighting system supports millions of dynamic lights.
|
||||
|
||||
```cpp
|
||||
// ❌ OLD: Limited dynamic lights (clustered forward shading)
|
||||
// Max ~100-200 dynamic lights before performance degrades
|
||||
|
||||
// ✅ NEW: Megalights (5.5+)
|
||||
// Millions of dynamic lights with minimal performance cost
|
||||
// Enable: Project Settings > Engine > Rendering > Megalights
|
||||
```
|
||||
|
||||
**Migration:** No code changes needed, but lighting behavior may differ. Test scenes after enabling.
|
||||
|
||||
---
|
||||
|
||||
## MEDIUM RISK — Behavioral Changes
|
||||
|
||||
### Enhanced Input System (Now Default)
|
||||
**Versions:** UE 5.1+ (recommended), 5.7 (default)
|
||||
|
||||
Enhanced Input is now the default input system.
|
||||
|
||||
```cpp
|
||||
// ❌ OLD: Legacy input bindings (deprecated)
|
||||
InputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
|
||||
|
||||
// ✅ NEW: Enhanced Input
|
||||
SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
|
||||
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
|
||||
}
|
||||
```
|
||||
|
||||
**Migration:** Replace legacy input bindings with Enhanced Input actions.
|
||||
|
||||
---
|
||||
|
||||
### Nanite Default Enabled
|
||||
**Versions:** UE 5.0+ (optional), 5.7 (encouraged)
|
||||
|
||||
Nanite virtualized geometry is now the recommended workflow for static meshes.
|
||||
|
||||
```cpp
|
||||
// Enable Nanite on static mesh:
|
||||
// Static Mesh Editor > Details > Nanite Settings > Enable Nanite Support
|
||||
```
|
||||
|
||||
**Migration:** Convert high-poly meshes to Nanite. Test performance on target platforms.
|
||||
|
||||
---
|
||||
|
||||
## LOW RISK — Deprecations (Still Functional)
|
||||
|
||||
### Legacy Material System
|
||||
**Status:** Deprecated but supported
|
||||
**Replacement:** Substrate Material System
|
||||
|
||||
Legacy materials still work, but Substrate is recommended for new projects.
|
||||
|
||||
---
|
||||
|
||||
### Old World Partition (UE4 Style)
|
||||
**Status:** Deprecated
|
||||
**Replacement:** World Partition (UE5+)
|
||||
|
||||
Use UE5's World Partition system for large worlds.
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Breaking Changes
|
||||
|
||||
### Windows
|
||||
- **UE 5.7**: DirectX 12 is now default (was DX11 in older versions)
|
||||
- Update shaders for DX12 compatibility
|
||||
|
||||
### macOS
|
||||
- **UE 5.5+**: Metal 3 required (minimum macOS 13)
|
||||
|
||||
### Mobile
|
||||
- **UE 5.7**: Minimum Android API level raised to 26 (Android 8.0)
|
||||
- Minimum iOS deployment target raised to iOS 14
|
||||
|
||||
---
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When upgrading from UE 5.3 to UE 5.7:
|
||||
|
||||
- [ ] Review Substrate materials (convert if ready for new system)
|
||||
- [ ] Audit PCG usage (update to production API if using experimental)
|
||||
- [ ] Test Megalights performance (enable and benchmark)
|
||||
- [ ] Migrate legacy input to Enhanced Input
|
||||
- [ ] Convert high-poly meshes to Nanite
|
||||
- [ ] Update shaders for DX12 (Windows) or Metal 3 (macOS)
|
||||
- [ ] Verify minimum platform versions (Android 8.0, iOS 14)
|
||||
- [ ] Test Lumen and Nanite performance on target hardware
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-7-release-notes
|
||||
- https://dev.epicgames.com/documentation/en-us/unreal-engine/upgrading-projects-to-newer-versions-of-unreal-engine
|
||||
340
docs/engine-reference/unreal/current-best-practices.md
Normal file
340
docs/engine-reference/unreal/current-best-practices.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Unreal Engine 5.7 — Current Best Practices
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
Modern UE5 patterns that may not be in the LLM's training data.
|
||||
These are production-ready recommendations as of UE 5.7.
|
||||
|
||||
---
|
||||
|
||||
## Project Setup
|
||||
|
||||
### Use UE 5.7 for New Projects
|
||||
- Latest features: Megalights, production-ready Substrate and PCG
|
||||
- Better performance and stability
|
||||
|
||||
### Choose the Right Rendering Features
|
||||
- **Lumen**: Real-time global illumination (RECOMMENDED for most projects)
|
||||
- **Nanite**: Virtualized geometry for high-poly meshes (RECOMMENDED for detailed environments)
|
||||
- **Megalights**: Millions of dynamic lights (RECOMMENDED for complex lighting)
|
||||
- **Substrate**: Modular material system (RECOMMENDED for new projects)
|
||||
|
||||
---
|
||||
|
||||
## C++ Coding
|
||||
|
||||
### Use Modern C++ Features (C++20 in UE5.7)
|
||||
|
||||
```cpp
|
||||
// ✅ Use TObjectPtr<T> (UE5 type-safe pointers)
|
||||
UPROPERTY()
|
||||
TObjectPtr<UStaticMeshComponent> MeshComp;
|
||||
|
||||
// ✅ Structured bindings
|
||||
if (auto [bSuccess, Value] = TryGetValue(); bSuccess) {
|
||||
// Use Value
|
||||
}
|
||||
|
||||
// ✅ Concepts and constraints (C++20)
|
||||
template<typename T>
|
||||
concept Damageable = requires(T t, float damage) {
|
||||
{ t.TakeDamage(damage) } -> std::same_as<void>;
|
||||
};
|
||||
```
|
||||
|
||||
### Use UPROPERTY() for Garbage Collection
|
||||
|
||||
```cpp
|
||||
// ✅ UPROPERTY ensures GC doesn't delete this
|
||||
UPROPERTY()
|
||||
TObjectPtr<AActor> MyActor;
|
||||
|
||||
// ❌ Raw pointers can become dangling
|
||||
AActor* MyActor; // Dangerous! May be garbage collected
|
||||
```
|
||||
|
||||
### Use UFUNCTION() for Blueprint Exposure
|
||||
|
||||
```cpp
|
||||
// ✅ Callable from Blueprint
|
||||
UFUNCTION(BlueprintCallable, Category="Combat")
|
||||
void TakeDamage(float Damage);
|
||||
|
||||
// ✅ Implementable in Blueprint
|
||||
UFUNCTION(BlueprintImplementableEvent, Category="Combat")
|
||||
void OnDeath();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blueprint Best Practices
|
||||
|
||||
### Use Blueprint vs C++
|
||||
|
||||
- **C++**: Core gameplay systems, performance-critical code, low-level engine interaction
|
||||
- **Blueprint**: Rapid prototyping, content creation, data-driven logic, designer workflows
|
||||
|
||||
### Blueprint Performance Tips
|
||||
|
||||
```cpp
|
||||
// ✅ Use Event Tick sparingly (expensive)
|
||||
// Prefer timers or events
|
||||
|
||||
// ✅ Use Blueprint Nativization (Blueprints → C++)
|
||||
// Project Settings > Packaging > Blueprint Nativization
|
||||
|
||||
// ✅ Cache frequently accessed components
|
||||
// Don't call GetComponent every tick
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rendering (UE 5.7)
|
||||
|
||||
### Use Lumen for Global Illumination
|
||||
|
||||
```cpp
|
||||
// Enable: Project Settings > Engine > Rendering > Dynamic Global Illumination Method = Lumen
|
||||
// Real-time GI, no lightmap baking needed (RECOMMENDED)
|
||||
```
|
||||
|
||||
### Use Nanite for High-Poly Meshes
|
||||
|
||||
```cpp
|
||||
// Enable on Static Mesh: Details > Nanite Settings > Enable Nanite Support
|
||||
// Automatically LODs millions of triangles (RECOMMENDED for detailed meshes)
|
||||
```
|
||||
|
||||
### Use Megalights for Complex Lighting (UE 5.5+)
|
||||
|
||||
```cpp
|
||||
// Enable: Project Settings > Engine > Rendering > Megalights = Enabled
|
||||
// Supports millions of dynamic lights with minimal cost
|
||||
```
|
||||
|
||||
### Use Substrate Materials (Production-Ready in 5.7)
|
||||
|
||||
```cpp
|
||||
// Enable: Project Settings > Engine > Substrate > Enable Substrate
|
||||
// Modular, physically accurate materials (RECOMMENDED for new projects)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Enhanced Input System
|
||||
|
||||
### Setup Enhanced Input
|
||||
|
||||
```cpp
|
||||
// 1. Create Input Action (IA_Jump)
|
||||
// 2. Create Input Mapping Context (IMC_Default)
|
||||
// 3. Add mapping: IA_Jump → Space Bar
|
||||
|
||||
// C++ Setup:
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
|
||||
void AMyCharacter::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
|
||||
if (APlayerController* PC = Cast<APlayerController>(GetController())) {
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer())) {
|
||||
Subsystem->AddMappingContext(DefaultMappingContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
|
||||
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
|
||||
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
|
||||
}
|
||||
|
||||
void AMyCharacter::Move(const FInputActionValue& Value) {
|
||||
FVector2D MoveVector = Value.Get<FVector2D>();
|
||||
AddMovementInput(GetActorForwardVector(), MoveVector.Y);
|
||||
AddMovementInput(GetActorRightVector(), MoveVector.X);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Ability System (GAS)
|
||||
|
||||
### Use GAS for Complex Gameplay
|
||||
|
||||
```cpp
|
||||
// ✅ Use GAS for: Abilities, buffs, damage calculation, cooldowns
|
||||
// Modular, scalable, multiplayer-ready
|
||||
|
||||
// Install: Enable "Gameplay Abilities" plugin
|
||||
|
||||
// Example Ability:
|
||||
UCLASS()
|
||||
class UGA_Fireball : public UGameplayAbility {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void ActivateAbility(...) override {
|
||||
// Ability logic
|
||||
SpawnFireball();
|
||||
CommitAbility(); // Commit cost/cooldown
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## World Partition (Large Worlds)
|
||||
|
||||
### Use World Partition for Open Worlds
|
||||
|
||||
```cpp
|
||||
// Enable: World Settings > Enable World Partition
|
||||
// Automatically streams world cells based on player location
|
||||
|
||||
// Data Layers: Organize content (e.g., "Gameplay", "Audio", "Lighting")
|
||||
// Runtime Data Layers: Load/unload at runtime
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Niagara (VFX)
|
||||
|
||||
### Use Niagara (Not Cascade)
|
||||
|
||||
```cpp
|
||||
// Create: Content Browser > Right Click > FX > Niagara System
|
||||
// GPU-accelerated, node-based particle system (RECOMMENDED)
|
||||
|
||||
// Spawn particles:
|
||||
UNiagaraComponent* NiagaraComp = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
|
||||
GetWorld(),
|
||||
ExplosionSystem,
|
||||
GetActorLocation()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MetaSounds (Audio)
|
||||
|
||||
### Use MetaSounds for Procedural Audio
|
||||
|
||||
```cpp
|
||||
// Create: Content Browser > Right Click > Sounds > MetaSound Source
|
||||
// Node-based audio, replaces Sound Cue for complex logic (RECOMMENDED)
|
||||
|
||||
// Play MetaSound:
|
||||
UAudioComponent* AudioComp = UGameplayStatics::SpawnSound2D(
|
||||
GetWorld(),
|
||||
MetaSoundSource
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Replication (Multiplayer)
|
||||
|
||||
### Server-Authoritative Pattern
|
||||
|
||||
```cpp
|
||||
// ✅ Client sends input, server validates and replicates
|
||||
UFUNCTION(Server, Reliable)
|
||||
void Server_Move(FVector Direction);
|
||||
|
||||
void AMyCharacter::Server_Move_Implementation(FVector Direction) {
|
||||
// Server validates and applies movement
|
||||
AddMovementInput(Direction);
|
||||
}
|
||||
|
||||
// ✅ Replicate important state
|
||||
UPROPERTY(Replicated)
|
||||
int32 Health;
|
||||
|
||||
void AMyCharacter::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AMyCharacter, Health);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Use Object Pooling
|
||||
|
||||
```cpp
|
||||
// ✅ Reuse objects instead of Spawn/Destroy
|
||||
TArray<AActor*> ProjectilePool;
|
||||
|
||||
AActor* GetPooledProjectile() {
|
||||
for (AActor* Proj : ProjectilePool) {
|
||||
if (!Proj->IsActive()) {
|
||||
Proj->SetActive(true);
|
||||
return Proj;
|
||||
}
|
||||
}
|
||||
// Pool exhausted, spawn new
|
||||
return SpawnNewProjectile();
|
||||
}
|
||||
```
|
||||
|
||||
### Use Instanced Static Meshes
|
||||
|
||||
```cpp
|
||||
// ✅ Hierarchical Instanced Static Mesh Component (HISM)
|
||||
// Render thousands of identical meshes in one draw call
|
||||
UHierarchicalInstancedStaticMeshComponent* HISM = CreateDefaultSubobject<UHierarchicalInstancedStaticMeshComponent>(TEXT("Trees"));
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
HISM->AddInstance(FTransform(RandomLocation));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Use Logging
|
||||
|
||||
```cpp
|
||||
// ✅ Structured logging
|
||||
UE_LOG(LogTemp, Warning, TEXT("Player health: %d"), Health);
|
||||
|
||||
// Custom log category
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogMyGame, Log, All);
|
||||
DEFINE_LOG_CATEGORY(LogMyGame);
|
||||
UE_LOG(LogMyGame, Error, TEXT("Critical error!"));
|
||||
```
|
||||
|
||||
### Use Visual Logger
|
||||
|
||||
```cpp
|
||||
// ✅ Visual debugging
|
||||
#include "VisualLogger/VisualLogger.h"
|
||||
|
||||
UE_VLOG_SEGMENT(this, LogTemp, Log, StartPos, EndPos, FColor::Red, TEXT("Raycast"));
|
||||
UE_VLOG_LOCATION(this, LogTemp, Log, TargetLocation, 50.f, FColor::Green, TEXT("Target"));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary: UE 5.7 Recommended Stack
|
||||
|
||||
| Feature | Use This (2026) | Notes |
|
||||
|---------|------------------|-------|
|
||||
| **Lighting** | Lumen + Megalights | Real-time GI, millions of lights |
|
||||
| **Geometry** | Nanite | High-poly meshes, automatic LOD |
|
||||
| **Materials** | Substrate | Modular, physically accurate |
|
||||
| **Input** | Enhanced Input | Rebindable, modular |
|
||||
| **VFX** | Niagara | GPU-accelerated |
|
||||
| **Audio** | MetaSounds | Procedural audio |
|
||||
| **World Streaming** | World Partition | Large open worlds |
|
||||
| **Gameplay** | Gameplay Ability System | Complex abilities, buffs |
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://docs.unrealengine.com/5.7/en-US/
|
||||
- https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-7-release-notes
|
||||
170
docs/engine-reference/unreal/deprecated-apis.md
Normal file
170
docs/engine-reference/unreal/deprecated-apis.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# Unreal Engine 5.7 — Deprecated APIs
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
|
||||
Quick lookup table for deprecated APIs and their replacements.
|
||||
Format: **Don't use X** → **Use Y instead**
|
||||
|
||||
---
|
||||
|
||||
## Input
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `InputComponent->BindAction()` | Enhanced Input `BindAction()` | New input system |
|
||||
| `InputComponent->BindAxis()` | Enhanced Input `BindAxis()` | New input system |
|
||||
| `PlayerController->GetInputAxisValue()` | Enhanced Input Action Values | New input system |
|
||||
|
||||
**Migration:** Install Enhanced Input plugin, create Input Actions and Input Mapping Contexts.
|
||||
|
||||
---
|
||||
|
||||
## Rendering
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Legacy material nodes | Substrate material nodes | Substrate is production-ready in 5.7 |
|
||||
| Forward shading (default) | Deferred + Lumen | Lumen is default in UE5 |
|
||||
| Old lighting workflow | Lumen Global Illumination | Real-time GI |
|
||||
|
||||
---
|
||||
|
||||
## World Building
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| UE4 World Composition | World Partition (UE5) | Streaming large worlds |
|
||||
| Level Streaming Volumes | World Partition Data Layers | Better level streaming |
|
||||
|
||||
---
|
||||
|
||||
## Animation
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Old animation retargeting | IK Rig + IK Retargeter | UE5 retargeting system |
|
||||
| Legacy control rig | Control Rig 2.0 | Production-ready rigging |
|
||||
|
||||
---
|
||||
|
||||
## Gameplay
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `UGameplayStatics::LoadStreamLevel()` | World Partition streaming | Use Data Layers |
|
||||
| Hardcoded input bindings | Enhanced Input system | Rebindable, modular input |
|
||||
|
||||
---
|
||||
|
||||
## Niagara (VFX)
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Cascade particle system | Niagara | Cascade is fully deprecated |
|
||||
|
||||
---
|
||||
|
||||
## Audio
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| Old audio mixer | MetaSounds | Procedural audio system |
|
||||
| Sound Cue (for complex logic) | MetaSounds | More powerful, node-based |
|
||||
|
||||
---
|
||||
|
||||
## Networking
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `DOREPLIFETIME()` (basic) | `DOREPLIFETIME_CONDITION()` | Conditional replication for optimization |
|
||||
|
||||
---
|
||||
|
||||
## C++ Scripting
|
||||
|
||||
| Deprecated | Replacement | Notes |
|
||||
|------------|-------------|-------|
|
||||
| `TSharedPtr<T>` for UObjects | `TObjectPtr<T>` | UE5 type-safe pointers |
|
||||
| Manual RTTI checks | `Cast<T>()` / `IsA<T>()` | Type-safe casting |
|
||||
|
||||
---
|
||||
|
||||
## Quick Migration Patterns
|
||||
|
||||
### Input Example
|
||||
```cpp
|
||||
// ❌ Deprecated
|
||||
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
|
||||
}
|
||||
|
||||
// ✅ Enhanced Input
|
||||
#include "EnhancedInputComponent.h"
|
||||
|
||||
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
|
||||
if (EIC) {
|
||||
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Material Example
|
||||
```cpp
|
||||
// ❌ Deprecated: Legacy material
|
||||
// Use standard material graph (still works but not recommended)
|
||||
|
||||
// ✅ Substrate Material
|
||||
// Enable: Project Settings > Engine > Substrate > Enable Substrate
|
||||
// Use Substrate nodes in material editor
|
||||
```
|
||||
|
||||
### World Partition Example
|
||||
```cpp
|
||||
// ❌ Deprecated: Level streaming volumes
|
||||
// Load/unload levels manually
|
||||
|
||||
// ✅ World Partition
|
||||
// Enable: World Settings > Enable World Partition
|
||||
// Use Data Layers for streaming
|
||||
```
|
||||
|
||||
### Particle System Example
|
||||
```cpp
|
||||
// ❌ Deprecated: Cascade
|
||||
UParticleSystemComponent* PSC = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particles"));
|
||||
|
||||
// ✅ Niagara
|
||||
UNiagaraComponent* NiagaraComp = CreateDefaultSubobject<UNiagaraComponent>(TEXT("Niagara"));
|
||||
```
|
||||
|
||||
### Audio Example
|
||||
```cpp
|
||||
// ❌ Deprecated: Sound Cue for complex logic
|
||||
// Use Sound Cue editor nodes
|
||||
|
||||
// ✅ MetaSounds
|
||||
// Create MetaSound Source asset, use node-based audio
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary: UE 5.7 Tech Stack
|
||||
|
||||
| Feature | Use This (2026) | Avoid This (Legacy) |
|
||||
|---------|------------------|----------------------|
|
||||
| **Input** | Enhanced Input | Legacy Input Bindings |
|
||||
| **Materials** | Substrate | Legacy Material System |
|
||||
| **Lighting** | Lumen + Megalights | Lightmaps + Limited Lights |
|
||||
| **Particles** | Niagara | Cascade |
|
||||
| **Audio** | MetaSounds | Sound Cue (for logic) |
|
||||
| **World Streaming** | World Partition | World Composition |
|
||||
| **Animation Retarget** | IK Rig + Retargeter | Old Retargeting |
|
||||
| **Geometry** | Nanite (high-poly) | Standard Static Mesh LODs |
|
||||
|
||||
---
|
||||
|
||||
**Sources:**
|
||||
- https://docs.unrealengine.com/5.7/en-US/deprecated-and-removed-features/
|
||||
- https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5-7-release-notes
|
||||
292
docs/engine-reference/unreal/modules/animation.md
Normal file
292
docs/engine-reference/unreal/modules/animation.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# Unreal Engine 5.7 — Animation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 animation authoring improvements, Control Rig 2.0
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 animation systems:
|
||||
- **Animation Blueprint**: State machine-based animation logic
|
||||
- **Control Rig**: Runtime procedural animation (production-ready in UE5)
|
||||
- **IK Rig + Retargeter**: Modern retargeting system
|
||||
- **Sequencer**: Cinematic animation
|
||||
|
||||
---
|
||||
|
||||
## Animation Blueprint
|
||||
|
||||
### Create Animation Blueprint
|
||||
|
||||
1. Content Browser > Right Click > Animation > Animation Blueprint
|
||||
2. Select parent class: `AnimInstance`
|
||||
3. Select skeleton
|
||||
|
||||
### Animation State Machine
|
||||
|
||||
```cpp
|
||||
// In Animation Blueprint Event Graph:
|
||||
// - State Machine drives animation states (Idle, Walk, Run, Jump)
|
||||
// - Blend Spaces for directional movement
|
||||
|
||||
// Access in C++:
|
||||
UAnimInstance* AnimInstance = Mesh->GetAnimInstance();
|
||||
AnimInstance->Montage_Play(AttackMontage);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Play Animation Montages
|
||||
|
||||
### Animation Montage
|
||||
|
||||
```cpp
|
||||
// Play montage
|
||||
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
|
||||
AnimInstance->Montage_Play(AttackMontage, 1.0f);
|
||||
|
||||
// Stop montage
|
||||
AnimInstance->Montage_Stop(0.2f, AttackMontage);
|
||||
|
||||
// Check if montage is playing
|
||||
bool bIsPlaying = AnimInstance->Montage_IsPlaying(AttackMontage);
|
||||
```
|
||||
|
||||
### Montage Notify Events
|
||||
|
||||
```cpp
|
||||
// Add notify event in Animation Montage (right-click timeline > Add Notify > New Notify)
|
||||
// Implement in C++:
|
||||
|
||||
UCLASS()
|
||||
class UMyAnimInstance : public UAnimInstance {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UFUNCTION()
|
||||
void AnimNotify_AttackHit() {
|
||||
// Called when notify is reached
|
||||
DealDamage();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Blend Spaces
|
||||
|
||||
### 1D Blend Space (Speed Blending)
|
||||
|
||||
```cpp
|
||||
// Create: Content Browser > Animation > Blend Space 1D
|
||||
// Horizontal Axis: Speed (0 = Idle, 1 = Walk, 2 = Run)
|
||||
// Add animations at key points
|
||||
|
||||
// Use in Anim Blueprint:
|
||||
// - Get speed from character
|
||||
// - Feed into Blend Space
|
||||
```
|
||||
|
||||
### 2D Blend Space (Directional Movement)
|
||||
|
||||
```cpp
|
||||
// Create: Content Browser > Animation > Blend Space
|
||||
// Horizontal Axis: Direction X (-1 to 1)
|
||||
// Vertical Axis: Direction Y (-1 to 1)
|
||||
// Place animations (Fwd, Back, Left, Right, diagonal)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Rig (Procedural Animation)
|
||||
|
||||
### Create Control Rig
|
||||
|
||||
1. Content Browser > Animation > Control Rig
|
||||
2. Select skeleton
|
||||
3. Build rig hierarchy (bones, controls, IK)
|
||||
|
||||
### Use Control Rig in Animation Blueprint
|
||||
|
||||
```cpp
|
||||
// Add "Control Rig" node to Anim Blueprint
|
||||
// Assign Control Rig asset
|
||||
// Procedurally modify bones at runtime
|
||||
```
|
||||
|
||||
### Control Rig in C++
|
||||
|
||||
```cpp
|
||||
// Get control rig component
|
||||
UControlRig* ControlRig = /* Get from animation instance */;
|
||||
|
||||
// Set control value
|
||||
ControlRig->SetControlValue<FVector>(TEXT("IK_Hand_R"), TargetLocation);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## IK Rig & Retargeting (UE5)
|
||||
|
||||
### Create IK Rig
|
||||
|
||||
1. Content Browser > Animation > IK Rig
|
||||
2. Select skeleton
|
||||
3. Add IK goals (hands, feet)
|
||||
4. Set up solver chains
|
||||
|
||||
### Retarget Animations
|
||||
|
||||
1. Create IK Rig for source skeleton
|
||||
2. Create IK Rig for target skeleton
|
||||
3. Create IK Retargeter asset
|
||||
4. Assign source and target IK Rigs
|
||||
5. Batch retarget animations
|
||||
|
||||
### Retargeting in C++
|
||||
|
||||
```cpp
|
||||
// Retargeting is primarily editor-based
|
||||
// Animations are retargeted once, then used normally
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Notify States
|
||||
|
||||
### Custom Notify State (Duration-Based Events)
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UAnimNotifyState_Invulnerable : public UAnimNotifyState {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration, const FAnimNotifyEventReference& EventReference) override {
|
||||
// Start invulnerability
|
||||
AMyCharacter* Character = Cast<AMyCharacter>(MeshComp->GetOwner());
|
||||
Character->bIsInvulnerable = true;
|
||||
}
|
||||
|
||||
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, const FAnimNotifyEventReference& EventReference) override {
|
||||
// End invulnerability
|
||||
AMyCharacter* Character = Cast<AMyCharacter>(MeshComp->GetOwner());
|
||||
Character->bIsInvulnerable = false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skeletal Mesh & Sockets
|
||||
|
||||
### Attach Objects to Sockets
|
||||
|
||||
```cpp
|
||||
// Create socket in Skeletal Mesh Editor (Skeleton Tree > Add Socket)
|
||||
|
||||
// Attach component to socket
|
||||
UStaticMeshComponent* Weapon = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Weapon"));
|
||||
Weapon->SetupAttachment(GetMesh(), TEXT("hand_r_socket"));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Curves
|
||||
|
||||
### Use Animation Curves
|
||||
|
||||
```cpp
|
||||
// Add curve to animation:
|
||||
// Animation Editor > Curves > Add Curve
|
||||
|
||||
// Read curve value in Anim Blueprint or C++:
|
||||
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
|
||||
float CurveValue = AnimInstance->GetCurveValue(TEXT("MyCurve"));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Root Motion
|
||||
|
||||
### Enable Root Motion
|
||||
|
||||
```cpp
|
||||
// In Animation Sequence: Asset Details > Root Motion > Enable Root Motion
|
||||
|
||||
// In Character class:
|
||||
GetCharacterMovement()->bAllowPhysicsRotationDuringAnimRootMotion = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Animation Layers (Linked Anim Graphs)
|
||||
|
||||
### Use Linked Anim Layers
|
||||
|
||||
```cpp
|
||||
// Create separate Anim Blueprints for layers (e.g., upper body, lower body)
|
||||
// Link in main Anim Blueprint: Add "Linked Anim Graph" node
|
||||
|
||||
// Dynamically switch layers:
|
||||
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
|
||||
AnimInstance->LinkAnimClassLayers(NewLayerClass);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sequencer (Cinematic Animation)
|
||||
|
||||
### Create Sequence
|
||||
|
||||
1. Content Browser > Cinematics > Level Sequence
|
||||
2. Add tracks: Camera, Character, Animation, etc.
|
||||
|
||||
### Play Sequence from C++
|
||||
|
||||
```cpp
|
||||
#include "LevelSequenceActor.h"
|
||||
#include "LevelSequencePlayer.h"
|
||||
|
||||
ALevelSequenceActor* SequenceActor = /* Spawn or find in level */;
|
||||
SequenceActor->GetSequencePlayer()->Play();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Animation Optimization
|
||||
|
||||
```cpp
|
||||
// LOD (Level of Detail) for skeletal meshes
|
||||
// Reduce bone count for distant characters
|
||||
|
||||
// Anim Blueprint optimization:
|
||||
// - Use "Anim Node Relevancy" (skip updates when not visible)
|
||||
// - Disable updates when off-screen:
|
||||
GetMesh()->VisibilityBasedAnimTickOption = EVisibilityBasedAnimTickOption::OnlyTickPoseWhenRendered;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Animation Debug Visualization
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// showdebug animation - Show animation state info
|
||||
// a.VisualizeSkeletalMeshBones 1 - Show skeleton bones
|
||||
|
||||
// Draw debug bones:
|
||||
DrawDebugCoordinateSystem(GetWorld(), BoneLocation, BoneRotation, 50.0f, false, -1.0f, 0, 2.0f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/animation-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/control-rig-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/ik-rig-in-unreal-engine/
|
||||
289
docs/engine-reference/unreal/modules/audio.md
Normal file
289
docs/engine-reference/unreal/modules/audio.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# Unreal Engine 5.7 — Audio Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 MetaSounds production-ready
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 audio systems:
|
||||
- **MetaSounds**: Node-based procedural audio (RECOMMENDED, production-ready)
|
||||
- **Sound Cues**: Legacy node-based audio (use for simple cases)
|
||||
- **Audio Component**: Play sounds on actors
|
||||
|
||||
---
|
||||
|
||||
## Basic Audio Playback
|
||||
|
||||
### Play Sound at Location
|
||||
|
||||
```cpp
|
||||
#include "Kismet/GameplayStatics.h"
|
||||
|
||||
// ✅ Play 2D sound (no spatialization)
|
||||
UGameplayStatics::PlaySound2D(GetWorld(), ExplosionSound);
|
||||
|
||||
// ✅ Play sound at location (3D spatial audio)
|
||||
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ExplosionSound, GetActorLocation());
|
||||
|
||||
// ✅ With volume and pitch
|
||||
UGameplayStatics::PlaySoundAtLocation(GetWorld(), ExplosionSound, GetActorLocation(), 0.7f, 1.2f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Component
|
||||
|
||||
### Audio Component (Persistent Sound)
|
||||
|
||||
```cpp
|
||||
// Create audio component
|
||||
UAudioComponent* AudioComp = CreateDefaultSubobject<UAudioComponent>(TEXT("Audio"));
|
||||
AudioComp->SetupAttachment(RootComponent);
|
||||
AudioComp->SetSound(LoopingAmbience);
|
||||
|
||||
// Play/Stop
|
||||
AudioComp->Play();
|
||||
AudioComp->Stop();
|
||||
|
||||
// Fade in/out
|
||||
AudioComp->FadeIn(2.0f); // 2 seconds
|
||||
AudioComp->FadeOut(1.5f, 0.0f); // 1.5s to volume 0
|
||||
|
||||
// Adjust volume/pitch
|
||||
AudioComp->SetVolumeMultiplier(0.5f);
|
||||
AudioComp->SetPitchMultiplier(1.2f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3D Spatial Audio
|
||||
|
||||
### Attenuation Settings
|
||||
|
||||
```cpp
|
||||
// Create Sound Attenuation asset:
|
||||
// Content Browser > Sounds > Sound Attenuation
|
||||
|
||||
// Configure:
|
||||
// - Attenuation Shape: Sphere, Capsule, Box, Cone
|
||||
// - Falloff Distance: Distance where sound becomes inaudible
|
||||
// - Attenuation Function: Linear, Logarithmic, Inverse, etc.
|
||||
|
||||
// Assign in C++:
|
||||
AudioComp->AttenuationSettings = AttenuationAsset;
|
||||
```
|
||||
|
||||
### Attenuation Override in Code
|
||||
|
||||
```cpp
|
||||
FSoundAttenuationSettings AttenuationOverride;
|
||||
AttenuationOverride.AttenuationShape = EAttenuationShape::Sphere;
|
||||
AttenuationOverride.FalloffDistance = 1000.0f;
|
||||
AttenuationOverride.AttenuationShapeExtents = FVector(1000.0f);
|
||||
|
||||
AudioComp->AttenuationOverrides = AttenuationOverride;
|
||||
AudioComp->bOverrideAttenuation = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MetaSounds (Procedural Audio)
|
||||
|
||||
### Create MetaSound Source
|
||||
|
||||
1. Content Browser > Sounds > MetaSound Source
|
||||
2. Open MetaSound editor
|
||||
3. Build node graph:
|
||||
- **Inputs**: Triggers, parameters
|
||||
- **Generators**: Oscillators, noise, samples
|
||||
- **Modulators**: Envelopes, LFOs
|
||||
- **Effects**: Filters, reverb, delay
|
||||
- **Output**: Audio output
|
||||
|
||||
### Play MetaSound
|
||||
|
||||
```cpp
|
||||
// Play MetaSound like any sound
|
||||
UGameplayStatics::PlaySound2D(GetWorld(), MetaSoundSource);
|
||||
|
||||
// Or with Audio Component
|
||||
AudioComp->SetSound(MetaSoundSource);
|
||||
AudioComp->Play();
|
||||
```
|
||||
|
||||
### Set MetaSound Parameters
|
||||
|
||||
```cpp
|
||||
// Define parameter in MetaSound (Input node with exposed parameter)
|
||||
// Set parameter in C++:
|
||||
AudioComp->SetFloatParameter(FName("Volume"), 0.8f);
|
||||
AudioComp->SetIntParameter(FName("OctaveShift"), 2);
|
||||
AudioComp->SetBoolParameter(FName("EnableReverb"), true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sound Cues (Legacy)
|
||||
|
||||
### Create Sound Cue
|
||||
|
||||
1. Content Browser > Sounds > Sound Cue
|
||||
2. Open Sound Cue editor
|
||||
3. Add nodes: Random, Modulator, Mixer, etc.
|
||||
|
||||
### Use Sound Cue
|
||||
|
||||
```cpp
|
||||
// Play like any sound
|
||||
UGameplayStatics::PlaySound2D(GetWorld(), SoundCue);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sound Classes & Sound Mixes
|
||||
|
||||
### Sound Class (Volume Groups)
|
||||
|
||||
```cpp
|
||||
// Create Sound Class: Content Browser > Sounds > Sound Class
|
||||
// Hierarchy: Master > Music, SFX, Dialogue
|
||||
|
||||
// Assign to sound asset:
|
||||
// Sound Wave > Sound Class = SFX
|
||||
|
||||
// Set volume in C++:
|
||||
UAudioSettings* AudioSettings = GetMutableDefault<UAudioSettings>();
|
||||
// Configure via Sound Class hierarchy
|
||||
```
|
||||
|
||||
### Sound Mix (Dynamic Mixing)
|
||||
|
||||
```cpp
|
||||
// Create Sound Mix asset
|
||||
// Define adjustments: Lower music during dialogue, etc.
|
||||
|
||||
// Push sound mix
|
||||
UGameplayStatics::PushSoundMixModifier(GetWorld(), DuckedMusicMix);
|
||||
|
||||
// Pop sound mix
|
||||
UGameplayStatics::PopSoundMixModifier(GetWorld(), DuckedMusicMix);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Occlusion & Reverb
|
||||
|
||||
### Audio Occlusion (Walls Block Sound)
|
||||
|
||||
```cpp
|
||||
// Enable in Audio Component:
|
||||
AudioComp->bEnableOcclusion = true;
|
||||
|
||||
// Requires geometry with collision
|
||||
```
|
||||
|
||||
### Reverb Volumes
|
||||
|
||||
```cpp
|
||||
// Add Audio Volume to level (Volumes > Audio Volume)
|
||||
// Configure reverb settings in Details panel
|
||||
// Audio component automatically picks up reverb when inside volume
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Footstep Sounds (Random Variation)
|
||||
|
||||
```cpp
|
||||
// Use Sound Cue with Random node, or:
|
||||
UPROPERTY(EditAnywhere, Category = "Audio")
|
||||
TArray<TObjectPtr<USoundBase>> FootstepSounds;
|
||||
|
||||
void PlayFootstep() {
|
||||
int32 Index = FMath::RandRange(0, FootstepSounds.Num() - 1);
|
||||
UGameplayStatics::PlaySoundAtLocation(GetWorld(), FootstepSounds[Index], GetActorLocation());
|
||||
}
|
||||
```
|
||||
|
||||
### Music Crossfade
|
||||
|
||||
```cpp
|
||||
UAudioComponent* MusicA;
|
||||
UAudioComponent* MusicB;
|
||||
|
||||
void CrossfadeMusic(float Duration) {
|
||||
MusicA->FadeOut(Duration, 0.0f);
|
||||
MusicB->FadeIn(Duration);
|
||||
}
|
||||
```
|
||||
|
||||
### Check if Sound is Playing
|
||||
|
||||
```cpp
|
||||
if (AudioComp->IsPlaying()) {
|
||||
// Sound is playing
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audio Concurrency
|
||||
|
||||
### Limit Concurrent Sounds
|
||||
|
||||
```cpp
|
||||
// Create Sound Concurrency asset:
|
||||
// Content Browser > Sounds > Sound Concurrency
|
||||
|
||||
// Configure:
|
||||
// - Max Count: Maximum instances of this sound
|
||||
// - Resolution Rule: Stop Oldest, Stop Quietest, etc.
|
||||
|
||||
// Assign to sound:
|
||||
// Sound Wave > Concurrency Settings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Audio Optimization
|
||||
|
||||
```cpp
|
||||
// Compression settings (Sound Wave asset):
|
||||
// - Compression Quality: 40 (balance quality/size)
|
||||
// - Streaming: Enable for large files (music)
|
||||
|
||||
// Reduce audio mixing cost:
|
||||
// - Limit concurrent sounds via Sound Concurrency
|
||||
// - Use simple attenuation shapes
|
||||
|
||||
// Disable audio for distant actors:
|
||||
if (Distance > MaxAudibleDistance) {
|
||||
AudioComp->Stop();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Audio Debug Commands
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// au.Debug.Sounds 1 - Show active sounds
|
||||
// au.3dVisualize.Enabled 1 - Visualize 3D audio
|
||||
// stat soundwaves - Show sound statistics
|
||||
// stat soundmixes - Show active sound mixes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/audio-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/metasounds-in-unreal-engine/
|
||||
288
docs/engine-reference/unreal/modules/input.md
Normal file
288
docs/engine-reference/unreal/modules/input.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# Unreal Engine 5.7 — Input Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 uses Enhanced Input as default (legacy input deprecated)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 input systems:
|
||||
- **Enhanced Input** (RECOMMENDED, default in UE5): Modular, rebindable, context-based
|
||||
- **Legacy Input**: Deprecated, avoid for new projects
|
||||
|
||||
---
|
||||
|
||||
## Enhanced Input System
|
||||
|
||||
### Setup Enhanced Input
|
||||
|
||||
1. **Enable Plugin**: `Edit > Plugins > Enhanced Input` (enabled by default in UE5)
|
||||
2. **Project Settings**: `Engine > Input > Default Classes > Default Player Input Class = EnhancedPlayerInput`
|
||||
|
||||
---
|
||||
|
||||
### Create Input Actions
|
||||
|
||||
1. Content Browser > Input > Input Action
|
||||
2. Name it (e.g., `IA_Jump`, `IA_Move`)
|
||||
3. Configure:
|
||||
- **Value Type**: Digital (bool), Axis1D (float), Axis2D (Vector2D), Axis3D (Vector)
|
||||
|
||||
Example Input Actions:
|
||||
- `IA_Jump`: Digital (bool)
|
||||
- `IA_Move`: Axis2D (Vector2D)
|
||||
- `IA_Look`: Axis2D (Vector2D)
|
||||
- `IA_Fire`: Digital (bool)
|
||||
|
||||
---
|
||||
|
||||
### Create Input Mapping Context
|
||||
|
||||
1. Content Browser > Input > Input Mapping Context
|
||||
2. Name it (e.g., `IMC_Default`)
|
||||
3. Add mappings:
|
||||
- `IA_Jump` → Space Bar
|
||||
- `IA_Move` → W/A/S/D keys (combine X/Y)
|
||||
- `IA_Look` → Mouse XY
|
||||
- `IA_Fire` → Left Mouse Button
|
||||
|
||||
---
|
||||
|
||||
### Bind Input in C++
|
||||
|
||||
```cpp
|
||||
#include "EnhancedInputComponent.h"
|
||||
#include "EnhancedInputSubsystems.h"
|
||||
#include "InputActionValue.h"
|
||||
|
||||
class AMyCharacter : public ACharacter {
|
||||
public:
|
||||
// Input Actions (assign in Blueprint)
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> MoveAction;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> LookAction;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputAction> JumpAction;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Input")
|
||||
TObjectPtr<UInputMappingContext> DefaultMappingContext;
|
||||
|
||||
protected:
|
||||
virtual void BeginPlay() override {
|
||||
Super::BeginPlay();
|
||||
|
||||
// Add Input Mapping Context
|
||||
if (APlayerController* PC = Cast<APlayerController>(Controller)) {
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer())) {
|
||||
Subsystem->AddMappingContext(DefaultMappingContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override {
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
|
||||
UEnhancedInputComponent* EIC = Cast<UEnhancedInputComponent>(PlayerInputComponent);
|
||||
if (EIC) {
|
||||
// Bind actions
|
||||
EIC->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
|
||||
EIC->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);
|
||||
|
||||
EIC->BindAction(MoveAction, ETriggerEvent::Triggered, this, &AMyCharacter::Move);
|
||||
EIC->BindAction(LookAction, ETriggerEvent::Triggered, this, &AMyCharacter::Look);
|
||||
}
|
||||
}
|
||||
|
||||
void Move(const FInputActionValue& Value) {
|
||||
FVector2D MoveVector = Value.Get<FVector2D>();
|
||||
|
||||
if (Controller) {
|
||||
AddMovementInput(GetActorForwardVector(), MoveVector.Y);
|
||||
AddMovementInput(GetActorRightVector(), MoveVector.X);
|
||||
}
|
||||
}
|
||||
|
||||
void Look(const FInputActionValue& Value) {
|
||||
FVector2D LookVector = Value.Get<FVector2D>();
|
||||
|
||||
if (Controller) {
|
||||
AddControllerYawInput(LookVector.X);
|
||||
AddControllerPitchInput(LookVector.Y);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Triggers
|
||||
|
||||
### Trigger Types
|
||||
|
||||
Input Actions can have triggers to control when they fire:
|
||||
- **Pressed**: When input starts
|
||||
- **Released**: When input ends
|
||||
- **Hold**: Hold for duration
|
||||
- **Tap**: Quick press
|
||||
- **Pulse**: Repeated firing while held
|
||||
|
||||
### Add Trigger in Editor
|
||||
|
||||
1. Open Input Action asset
|
||||
2. Triggers > Add > Select trigger type (e.g., `Hold`)
|
||||
3. Configure (e.g., Hold Time = 0.5s)
|
||||
|
||||
---
|
||||
|
||||
## Input Modifiers
|
||||
|
||||
### Modifier Types
|
||||
|
||||
Modifiers transform input values:
|
||||
- **Negate**: Flip sign (-1 ↔ 1)
|
||||
- **Dead Zone**: Ignore small inputs
|
||||
- **Scalar**: Multiply by value
|
||||
- **Smooth**: Smoothing over time
|
||||
|
||||
### Add Modifier in Editor
|
||||
|
||||
1. Open Input Action asset
|
||||
2. Modifiers > Add > Select modifier (e.g., `Negate`)
|
||||
3. Configure
|
||||
|
||||
---
|
||||
|
||||
## Input Mapping Contexts (Context Switching)
|
||||
|
||||
### Multiple Contexts
|
||||
|
||||
```cpp
|
||||
// Define contexts
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
TObjectPtr<UInputMappingContext> DefaultContext;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Input")
|
||||
TObjectPtr<UInputMappingContext> VehicleContext;
|
||||
|
||||
// Switch context
|
||||
void EnterVehicle() {
|
||||
if (APlayerController* PC = Cast<APlayerController>(Controller)) {
|
||||
if (UEnhancedInputLocalPlayerSubsystem* Subsystem =
|
||||
ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PC->GetLocalPlayer())) {
|
||||
Subsystem->RemoveMappingContext(DefaultContext);
|
||||
Subsystem->AddMappingContext(VehicleContext, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Legacy Input (Deprecated)
|
||||
|
||||
### Legacy Input Bindings
|
||||
|
||||
```cpp
|
||||
// ❌ DEPRECATED: Do not use for new projects
|
||||
|
||||
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) {
|
||||
// Legacy action binding
|
||||
PlayerInputComponent->BindAction("Jump", IE_Pressed, this, &ACharacter::Jump);
|
||||
|
||||
// Legacy axis binding
|
||||
PlayerInputComponent->BindAxis("MoveForward", this, &AMyCharacter::MoveForward);
|
||||
}
|
||||
|
||||
void MoveForward(float Value) {
|
||||
AddMovementInput(GetActorForwardVector(), Value);
|
||||
}
|
||||
```
|
||||
|
||||
**Migration:** Use Enhanced Input instead.
|
||||
|
||||
---
|
||||
|
||||
## Gamepad Input
|
||||
|
||||
### Gamepad with Enhanced Input
|
||||
|
||||
```cpp
|
||||
// Input Mapping Context:
|
||||
// - IA_Move → Gamepad Left Thumbstick
|
||||
// - IA_Look → Gamepad Right Thumbstick
|
||||
// - IA_Jump → Gamepad Face Button Bottom (A/Cross)
|
||||
|
||||
// No code changes needed, just add gamepad mappings to Input Mapping Context
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Touch Input (Mobile)
|
||||
|
||||
### Touch Input with Enhanced Input
|
||||
|
||||
```cpp
|
||||
// Input Mapping Context:
|
||||
// - IA_Move → Touch (virtual thumbstick)
|
||||
// - IA_Look → Touch (swipe)
|
||||
|
||||
// Use Touch Interface asset for virtual controls
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rebinding Input at Runtime
|
||||
|
||||
### Change Key Mapping
|
||||
|
||||
```cpp
|
||||
#include "PlayerMappableInputConfig.h"
|
||||
|
||||
// Get subsystem
|
||||
UEnhancedInputLocalPlayerSubsystem* Subsystem = /* Get subsystem */;
|
||||
|
||||
// Get player mappable keys
|
||||
FPlayerMappableKeySlot KeySlot = FPlayerMappableKeySlot(/*..*/);
|
||||
FKey NewKey = EKeys::F; // Rebind to F key
|
||||
|
||||
// Apply new mapping
|
||||
Subsystem->AddPlayerMappedKey(/*..*/);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Debugging
|
||||
|
||||
### Debug Input
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// showdebug input - Show input debug info
|
||||
|
||||
// Log input values:
|
||||
UE_LOG(LogTemp, Warning, TEXT("Move Input: %s"), *MoveVector.ToString());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Check if Key Pressed (Quick & Dirty)
|
||||
|
||||
```cpp
|
||||
// For debugging only (not recommended for gameplay)
|
||||
if (GetWorld()->GetFirstPlayerController()->IsInputKeyDown(EKeys::SpaceBar)) {
|
||||
// Space bar is down
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/enhanced-input-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/enhanced-input-action-and-input-mapping-context-in-unreal-engine/
|
||||
338
docs/engine-reference/unreal/modules/navigation.md
Normal file
338
docs/engine-reference/unreal/modules/navigation.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# Unreal Engine 5.7 — Navigation Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 navigation improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 navigation systems:
|
||||
- **Nav Mesh**: Automatic pathfinding mesh for AI
|
||||
- **AI Controller**: Controls AI movement and behavior
|
||||
- **Behavior Trees**: AI decision-making (covered in AI module)
|
||||
|
||||
---
|
||||
|
||||
## Nav Mesh Setup
|
||||
|
||||
### Add Nav Mesh Bounds Volume
|
||||
|
||||
1. Place Actors > Volumes > Nav Mesh Bounds Volume
|
||||
2. Scale to cover walkable areas
|
||||
3. Press `P` to toggle Nav Mesh visualization (green overlay)
|
||||
|
||||
### Nav Mesh Settings
|
||||
|
||||
```cpp
|
||||
// Project Settings > Engine > Navigation System
|
||||
// - Generate Navigation Only Around Navigation Invokers: Performance optimization
|
||||
// - Auto Update Enabled: Rebuild NavMesh when geometry changes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI Controller & Movement
|
||||
|
||||
### Create AI Controller
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AEnemyAIController : public AAIController {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
void BeginPlay() override {
|
||||
Super::BeginPlay();
|
||||
|
||||
// Move to location
|
||||
FVector TargetLocation = FVector(1000, 0, 0);
|
||||
MoveToLocation(TargetLocation);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Assign AI Controller to Pawn
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AEnemyCharacter : public ACharacter {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AEnemyCharacter() {
|
||||
// ✅ Assign AI Controller class
|
||||
AIControllerClass = AEnemyAIController::StaticClass();
|
||||
AutoPossessAI = EAutoPossessAI::PlacedInWorldOrSpawned;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic AI Movement
|
||||
|
||||
### Move to Location
|
||||
|
||||
```cpp
|
||||
AAIController* AIController = Cast<AAIController>(GetController());
|
||||
if (AIController) {
|
||||
FVector TargetLocation = FVector(1000, 0, 0);
|
||||
EPathFollowingRequestResult::Type Result = AIController->MoveToLocation(TargetLocation);
|
||||
|
||||
if (Result == EPathFollowingRequestResult::RequestSuccessful) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Moving to location"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Move to Actor
|
||||
|
||||
```cpp
|
||||
AActor* Target = /* Get target actor */;
|
||||
AIController->MoveToActor(Target, 100.0f); // Stop 100 units away
|
||||
```
|
||||
|
||||
### Stop Movement
|
||||
|
||||
```cpp
|
||||
AIController->StopMovement();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path Following Events
|
||||
|
||||
### On Move Completed
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AEnemyAIController : public AAIController {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
void BeginPlay() override {
|
||||
Super::BeginPlay();
|
||||
|
||||
// Bind to move completed event
|
||||
ReceiveMoveCompleted.AddDynamic(this, &AEnemyAIController::OnMoveCompleted);
|
||||
}
|
||||
|
||||
UFUNCTION()
|
||||
void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result) {
|
||||
if (Result == EPathFollowingResult::Success) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Reached destination"));
|
||||
} else {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Failed to reach destination"));
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pathfinding Queries
|
||||
|
||||
### Find Path to Location
|
||||
|
||||
```cpp
|
||||
#include "NavigationSystem.h"
|
||||
#include "NavigationPath.h"
|
||||
|
||||
UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());
|
||||
if (NavSys) {
|
||||
FVector Start = GetActorLocation();
|
||||
FVector End = TargetLocation;
|
||||
|
||||
FPathFindingQuery Query;
|
||||
Query.StartLocation = Start;
|
||||
Query.EndLocation = End;
|
||||
Query.NavData = NavSys->GetDefaultNavDataInstance();
|
||||
|
||||
FPathFindingResult Result = NavSys->FindPathSync(Query);
|
||||
|
||||
if (Result.IsSuccessful()) {
|
||||
UNavigationPath* NavPath = Result.Path.Get();
|
||||
// Use path points: NavPath->GetPathPoints()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Check if Location is Reachable
|
||||
|
||||
```cpp
|
||||
UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());
|
||||
FNavLocation OutLocation;
|
||||
bool bReachable = NavSys->ProjectPointToNavigation(TargetLocation, OutLocation);
|
||||
|
||||
if (bReachable) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Location is reachable"));
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nav Mesh Modifiers
|
||||
|
||||
### Nav Modifier Volume (Block/Allow Areas)
|
||||
|
||||
1. Place Actors > Volumes > Nav Modifier Volume
|
||||
2. Configure Area Class (e.g., NavArea_Null to block, NavArea_LowHeight for crouching)
|
||||
|
||||
---
|
||||
|
||||
## Custom Nav Areas
|
||||
|
||||
### Create Custom Nav Area
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UNavArea_Jump : public UNavArea {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UNavArea_Jump() {
|
||||
DefaultCost = 10.0f; // Higher cost = AI avoids unless necessary
|
||||
FixedAreaEnteringCost = 100.0f; // One-time cost to enter
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Use Custom Nav Area
|
||||
|
||||
```cpp
|
||||
// Assign to Nav Modifier Volume or geometry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nav Mesh Generation
|
||||
|
||||
### Rebuild Nav Mesh at Runtime
|
||||
|
||||
```cpp
|
||||
UNavigationSystemV1* NavSys = UNavigationSystemV1::GetCurrent(GetWorld());
|
||||
NavSys->Build(); // Rebuild entire NavMesh
|
||||
```
|
||||
|
||||
### Dynamic Nav Mesh (Moving Obstacles)
|
||||
|
||||
```cpp
|
||||
// Enable: Project Settings > Navigation System > Runtime Generation = Dynamic
|
||||
|
||||
// Mark actor as dynamic obstacle:
|
||||
UStaticMeshComponent* Mesh = /* Get mesh */;
|
||||
Mesh->SetCanEverAffectNavigation(true);
|
||||
Mesh->bDynamicObstacle = true;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nav Links (Off-Mesh Connections)
|
||||
|
||||
### Nav Link Proxy (Jump, Teleport)
|
||||
|
||||
1. Place Actors > Navigation > Nav Link Proxy
|
||||
2. Set up start and end points
|
||||
3. Configure:
|
||||
- **Direction**: One-way or bidirectional
|
||||
- **Smart Link**: Animate character during traversal
|
||||
|
||||
---
|
||||
|
||||
## Crowd Management
|
||||
|
||||
### Detour Crowd (Avoid Overlapping)
|
||||
|
||||
```cpp
|
||||
// Enable: Character Movement Component > Avoidance Enabled = true
|
||||
|
||||
// Configure avoidance group and flags
|
||||
UCharacterMovementComponent* MoveComp = GetCharacterMovement();
|
||||
MoveComp->SetAvoidanceGroup(1);
|
||||
MoveComp->SetGroupsToAvoid(1);
|
||||
MoveComp->SetAvoidanceEnabled(true);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Nav Mesh Optimization
|
||||
|
||||
```cpp
|
||||
// Reduce tile size for large worlds:
|
||||
// Project Settings > Navigation System > Cell Size = 19 (default)
|
||||
|
||||
// Use Navigation Invokers for dynamic generation:
|
||||
// Only generate NavMesh around players/important actors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Visualize Nav Mesh
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// show navigation - Toggle NavMesh visualization
|
||||
// p - Toggle NavMesh (editor viewport)
|
||||
|
||||
// Draw debug path:
|
||||
if (NavPath) {
|
||||
for (int i = 0; i < NavPath->GetPathPoints().Num() - 1; i++) {
|
||||
DrawDebugLine(GetWorld(), NavPath->GetPathPoints()[i], NavPath->GetPathPoints()[i + 1], FColor::Green, false, 5.0f, 0, 5.0f);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Patrol Between Waypoints
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, Category = "AI")
|
||||
TArray<AActor*> PatrolPoints;
|
||||
|
||||
int32 CurrentPatrolIndex = 0;
|
||||
|
||||
void OnMoveCompleted(FAIRequestID RequestID, EPathFollowingResult::Type Result) {
|
||||
if (Result == EPathFollowingResult::Success) {
|
||||
// Move to next waypoint
|
||||
CurrentPatrolIndex = (CurrentPatrolIndex + 1) % PatrolPoints.Num();
|
||||
MoveToActor(PatrolPoints[CurrentPatrolIndex]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Chase Player
|
||||
|
||||
```cpp
|
||||
void Tick(float DeltaTime) {
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
AAIController* AIController = Cast<AAIController>(GetController());
|
||||
APawn* PlayerPawn = GetWorld()->GetFirstPlayerController()->GetPawn();
|
||||
|
||||
if (AIController && PlayerPawn) {
|
||||
float Distance = FVector::Dist(GetActorLocation(), PlayerPawn->GetActorLocation());
|
||||
|
||||
if (Distance < 1000.0f) {
|
||||
// Chase player
|
||||
AIController->MoveToActor(PlayerPawn, 100.0f);
|
||||
} else {
|
||||
// Stop chasing
|
||||
AIController->StopMovement();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/navigation-system-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/ai-in-unreal-engine/
|
||||
409
docs/engine-reference/unreal/modules/networking.md
Normal file
409
docs/engine-reference/unreal/modules/networking.md
Normal file
@@ -0,0 +1,409 @@
|
||||
# Unreal Engine 5.7 — Networking Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 networking improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 networking:
|
||||
- **Client-Server Architecture**: Server-authoritative (RECOMMENDED)
|
||||
- **Replication**: Automatic state synchronization
|
||||
- **RPCs (Remote Procedure Calls)**: Call functions across network
|
||||
- **Relevancy**: Optimize bandwidth by only replicating relevant actors
|
||||
|
||||
---
|
||||
|
||||
## Basic Multiplayer Setup
|
||||
|
||||
### Enable Replication on Actor
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyActor : public AActor {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMyActor() {
|
||||
// ✅ Enable replication
|
||||
bReplicates = true;
|
||||
bAlwaysRelevant = true; // Always replicate to all clients
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Network Role Checks
|
||||
|
||||
```cpp
|
||||
// Check role
|
||||
if (HasAuthority()) {
|
||||
// Running on server
|
||||
}
|
||||
|
||||
if (GetLocalRole() == ROLE_AutonomousProxy) {
|
||||
// This is the owning client (local player)
|
||||
}
|
||||
|
||||
if (GetRemoteRole() == ROLE_SimulatedProxy) {
|
||||
// This is a remote client (other players)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Replicated Variables
|
||||
|
||||
### Basic Replication
|
||||
|
||||
```cpp
|
||||
UPROPERTY(Replicated)
|
||||
int32 Health;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
FVector Position;
|
||||
|
||||
// ✅ Implement GetLifetimeReplicatedProps
|
||||
void AMyActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME(AMyActor, Health);
|
||||
DOREPLIFETIME(AMyActor, Position);
|
||||
}
|
||||
```
|
||||
|
||||
### Conditional Replication
|
||||
|
||||
```cpp
|
||||
// Only replicate to owner
|
||||
DOREPLIFETIME_CONDITION(AMyCharacter, Ammo, COND_OwnerOnly);
|
||||
|
||||
// Skip owner (replicate to everyone else)
|
||||
DOREPLIFETIME_CONDITION(AMyCharacter, TeamID, COND_SkipOwner);
|
||||
|
||||
// Only when changed
|
||||
DOREPLIFETIME_CONDITION(AMyCharacter, Score, COND_InitialOnly);
|
||||
```
|
||||
|
||||
### RepNotify (Callback on Replication)
|
||||
|
||||
```cpp
|
||||
UPROPERTY(ReplicatedUsing=OnRep_Health)
|
||||
int32 Health;
|
||||
|
||||
UFUNCTION()
|
||||
void OnRep_Health() {
|
||||
// Called on clients when Health changes
|
||||
UpdateHealthUI();
|
||||
}
|
||||
|
||||
// Implement GetLifetimeReplicatedProps (same as above)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RPCs (Remote Procedure Calls)
|
||||
|
||||
### Server RPC (Client → Server)
|
||||
|
||||
```cpp
|
||||
// Client calls, server executes
|
||||
UFUNCTION(Server, Reliable)
|
||||
void Server_TakeDamage(int32 Damage);
|
||||
|
||||
void AMyCharacter::Server_TakeDamage_Implementation(int32 Damage) {
|
||||
// Runs on server only
|
||||
Health -= Damage;
|
||||
|
||||
if (Health <= 0) {
|
||||
Server_Die();
|
||||
}
|
||||
}
|
||||
|
||||
bool AMyCharacter::Server_TakeDamage_Validate(int32 Damage) {
|
||||
// Validate input (anti-cheat)
|
||||
return Damage >= 0 && Damage <= 100;
|
||||
}
|
||||
```
|
||||
|
||||
### Client RPC (Server → Client)
|
||||
|
||||
```cpp
|
||||
// Server calls, client executes
|
||||
UFUNCTION(Client, Reliable)
|
||||
void Client_ShowDeathScreen();
|
||||
|
||||
void AMyCharacter::Client_ShowDeathScreen_Implementation() {
|
||||
// Runs on client only
|
||||
ShowDeathUI();
|
||||
}
|
||||
```
|
||||
|
||||
### Multicast RPC (Server → All Clients)
|
||||
|
||||
```cpp
|
||||
// Server calls, all clients execute
|
||||
UFUNCTION(NetMulticast, Reliable)
|
||||
void Multicast_PlayExplosion(FVector Location);
|
||||
|
||||
void AMyActor::Multicast_PlayExplosion_Implementation(FVector Location) {
|
||||
// Runs on server and all clients
|
||||
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ExplosionEffect, Location);
|
||||
}
|
||||
```
|
||||
|
||||
### RPC Reliability
|
||||
|
||||
```cpp
|
||||
// Reliable: Guaranteed delivery (important events)
|
||||
UFUNCTION(Server, Reliable)
|
||||
void Server_FireWeapon();
|
||||
|
||||
// Unreliable: Best-effort delivery (frequent updates, position sync)
|
||||
UFUNCTION(Server, Unreliable)
|
||||
void Server_UpdateAim(FRotator AimRotation);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server-Authoritative Pattern (RECOMMENDED)
|
||||
|
||||
### Movement Example
|
||||
|
||||
```cpp
|
||||
class AMyCharacter : public ACharacter {
|
||||
UPROPERTY(Replicated)
|
||||
FVector ServerPosition;
|
||||
|
||||
void Tick(float DeltaTime) override {
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
if (GetLocalRole() == ROLE_AutonomousProxy) {
|
||||
// Client: Send input to server
|
||||
FVector Input = GetMovementInput();
|
||||
Server_Move(Input);
|
||||
|
||||
// Client-side prediction (move locally)
|
||||
AddMovementInput(Input);
|
||||
}
|
||||
|
||||
if (HasAuthority()) {
|
||||
// Server: Authoritative position
|
||||
ServerPosition = GetActorLocation();
|
||||
} else {
|
||||
// Client: Interpolate toward server position
|
||||
FVector NewPos = FMath::VInterpTo(GetActorLocation(), ServerPosition, DeltaTime, 5.0f);
|
||||
SetActorLocation(NewPos);
|
||||
}
|
||||
}
|
||||
|
||||
UFUNCTION(Server, Unreliable)
|
||||
void Server_Move(FVector Input);
|
||||
|
||||
void Server_Move_Implementation(FVector Input) {
|
||||
// Server validates and applies movement
|
||||
AddMovementInput(Input);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Network Relevancy (Bandwidth Optimization)
|
||||
|
||||
### Custom Relevancy
|
||||
|
||||
```cpp
|
||||
bool AMyActor::IsNetRelevantFor(const AActor* RealViewer, const AActor* ViewTarget, const FVector& SrcLocation) const {
|
||||
// Only replicate if within range
|
||||
float Distance = FVector::Dist(SrcLocation, GetActorLocation());
|
||||
return Distance < 5000.0f;
|
||||
}
|
||||
```
|
||||
|
||||
### Always Relevant Actors
|
||||
|
||||
```cpp
|
||||
AMyActor() {
|
||||
bAlwaysRelevant = true; // Replicate to all clients (e.g., GameState, PlayerController)
|
||||
bOnlyRelevantToOwner = true; // Only replicate to owner (e.g., PlayerController)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ownership
|
||||
|
||||
### Set Owner
|
||||
|
||||
```cpp
|
||||
// Assign owner (important for RPCs and relevancy)
|
||||
MyActor->SetOwner(OwningPlayerController);
|
||||
```
|
||||
|
||||
### Check Owner
|
||||
|
||||
```cpp
|
||||
if (GetOwner() == PlayerController) {
|
||||
// This actor is owned by this player
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Game Mode & Game State
|
||||
|
||||
### Game Mode (Server Only)
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyGameMode : public AGameMode {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Game mode only exists on server
|
||||
// Use for server-side logic (spawning, scoring, rules)
|
||||
};
|
||||
```
|
||||
|
||||
### Game State (Replicated to All Clients)
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyGameState : public AGameState {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// ✅ Replicate game state to all clients
|
||||
UPROPERTY(Replicated)
|
||||
int32 RedTeamScore;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
int32 BlueTeamScore;
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override {
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AMyGameState, RedTeamScore);
|
||||
DOREPLIFETIME(AMyGameState, BlueTeamScore);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Player Controller & Player State
|
||||
|
||||
### Player Controller (One per Player)
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyPlayerController : public APlayerController {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Exists on server and owning client
|
||||
// Use for player-specific logic, input handling
|
||||
};
|
||||
```
|
||||
|
||||
### Player State (Replicated Player Info)
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyPlayerState : public APlayerState {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(Replicated)
|
||||
int32 Kills;
|
||||
|
||||
UPROPERTY(Replicated)
|
||||
int32 Deaths;
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override {
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
DOREPLIFETIME(AMyPlayerState, Kills);
|
||||
DOREPLIFETIME(AMyPlayerState, Deaths);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sessions & Matchmaking
|
||||
|
||||
### Create Session
|
||||
|
||||
```cpp
|
||||
#include "OnlineSubsystem.h"
|
||||
#include "OnlineSessionSettings.h"
|
||||
|
||||
void CreateSession() {
|
||||
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
|
||||
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
|
||||
|
||||
TSharedPtr<FOnlineSessionSettings> SessionSettings = MakeShareable(new FOnlineSessionSettings());
|
||||
SessionSettings->bIsLANMatch = false;
|
||||
SessionSettings->NumPublicConnections = 4;
|
||||
SessionSettings->bShouldAdvertise = true;
|
||||
|
||||
Sessions->CreateSession(0, FName("MySession"), *SessionSettings);
|
||||
}
|
||||
```
|
||||
|
||||
### Find Sessions
|
||||
|
||||
```cpp
|
||||
void FindSessions() {
|
||||
IOnlineSubsystem* OnlineSub = IOnlineSubsystem::Get();
|
||||
IOnlineSessionPtr Sessions = OnlineSub->GetSessionInterface();
|
||||
|
||||
TSharedRef<FOnlineSessionSearch> SearchSettings = MakeShareable(new FOnlineSessionSearch());
|
||||
SearchSettings->bIsLanQuery = false;
|
||||
SearchSettings->MaxSearchResults = 20;
|
||||
|
||||
Sessions->FindSessions(0, SearchSettings);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Reduce Bandwidth
|
||||
|
||||
```cpp
|
||||
// Use unreliable RPCs for frequent updates
|
||||
UFUNCTION(Server, Unreliable)
|
||||
void Server_UpdatePosition(FVector Pos);
|
||||
|
||||
// Conditional replication (only replicate to relevant clients)
|
||||
DOREPLIFETIME_CONDITION(AMyActor, Health, COND_OwnerOnly);
|
||||
|
||||
// Limit replication frequency
|
||||
SetReplicationFrequency(10.0f); // Update 10 times per second (default 100)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Network Debugging
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// stat net - Show network stats
|
||||
// stat netplayerupdate - Show player update stats
|
||||
// NetEmulation PktLoss=10 - Simulate 10% packet loss
|
||||
// NetEmulation PktLag=100 - Simulate 100ms latency
|
||||
|
||||
// Draw debug for replication:
|
||||
UE_LOG(LogNet, Warning, TEXT("Replicating Health: %d"), Health);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/networking-and-multiplayer-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/actor-replication-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/rpcs-in-unreal-engine/
|
||||
283
docs/engine-reference/unreal/modules/physics.md
Normal file
283
docs/engine-reference/unreal/modules/physics.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# Unreal Engine 5.7 — Physics Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 Chaos Physics improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5 uses **Chaos Physics** (replaced PhysX in UE 4):
|
||||
- Better performance
|
||||
- Destruction support
|
||||
- Vehicle physics improvements
|
||||
|
||||
---
|
||||
|
||||
## Rigid Body Physics
|
||||
|
||||
### Enable Physics on Static Mesh
|
||||
|
||||
```cpp
|
||||
UStaticMeshComponent* MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
|
||||
MeshComp->SetSimulatePhysics(true);
|
||||
MeshComp->SetEnableGravity(true);
|
||||
MeshComp->SetMassOverrideInKg(NAME_None, 50.0f); // 50 kg
|
||||
```
|
||||
|
||||
### Apply Forces
|
||||
|
||||
```cpp
|
||||
// Apply impulse (instant velocity change)
|
||||
MeshComp->AddImpulse(FVector(0, 0, 1000), NAME_None, true);
|
||||
|
||||
// Apply force (continuous)
|
||||
MeshComp->AddForce(FVector(0, 0, 500));
|
||||
|
||||
// Apply torque (rotation)
|
||||
MeshComp->AddTorqueInRadians(FVector(0, 0, 100));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Collision
|
||||
|
||||
### Collision Channels
|
||||
|
||||
```cpp
|
||||
// Project Settings > Engine > Collision
|
||||
// Define custom collision channels and responses
|
||||
|
||||
// Set collision in C++
|
||||
MeshComp->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
|
||||
MeshComp->SetCollisionObjectType(ECollisionChannel::ECC_Pawn);
|
||||
MeshComp->SetCollisionResponseToAllChannels(ECR_Block);
|
||||
MeshComp->SetCollisionResponseToChannel(ECC_Camera, ECR_Ignore);
|
||||
```
|
||||
|
||||
### Collision Events
|
||||
|
||||
```cpp
|
||||
// Enable collision events
|
||||
MeshComp->SetNotifyRigidBodyCollision(true);
|
||||
|
||||
// Bind to OnComponentHit
|
||||
MeshComp->OnComponentHit.AddDynamic(this, &AMyActor::OnHit);
|
||||
|
||||
UFUNCTION()
|
||||
void AMyActor::OnHit(UPrimitiveComponent* HitComp, AActor* OtherActor,
|
||||
UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Hit %s"), *OtherActor->GetName());
|
||||
}
|
||||
```
|
||||
|
||||
### Overlap Events
|
||||
|
||||
```cpp
|
||||
// Enable overlap events
|
||||
MeshComp->SetGenerateOverlapEvents(true);
|
||||
|
||||
// Bind to OnComponentBeginOverlap
|
||||
MeshComp->OnComponentBeginOverlap.AddDynamic(this, &AMyActor::OnOverlapBegin);
|
||||
|
||||
UFUNCTION()
|
||||
void AMyActor::OnOverlapBegin(UPrimitiveComponent* OverlappedComp, AActor* OtherActor,
|
||||
UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Overlapped %s"), *OtherActor->GetName());
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Raycasting (Line Traces)
|
||||
|
||||
### Single Line Trace
|
||||
|
||||
```cpp
|
||||
FHitResult HitResult;
|
||||
FVector Start = GetActorLocation();
|
||||
FVector End = Start + GetActorForwardVector() * 1000.0f;
|
||||
|
||||
FCollisionQueryParams QueryParams;
|
||||
QueryParams.AddIgnoredActor(this);
|
||||
|
||||
// Perform trace
|
||||
bool bHit = GetWorld()->LineTraceSingleByChannel(
|
||||
HitResult,
|
||||
Start,
|
||||
End,
|
||||
ECC_Visibility,
|
||||
QueryParams
|
||||
);
|
||||
|
||||
if (bHit) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *HitResult.GetActor()->GetName());
|
||||
DrawDebugLine(GetWorld(), Start, HitResult.Location, FColor::Red, false, 2.0f);
|
||||
}
|
||||
```
|
||||
|
||||
### Multi Line Trace
|
||||
|
||||
```cpp
|
||||
TArray<FHitResult> HitResults;
|
||||
bool bHit = GetWorld()->LineTraceMultiByChannel(
|
||||
HitResults,
|
||||
Start,
|
||||
End,
|
||||
ECC_Visibility,
|
||||
QueryParams
|
||||
);
|
||||
|
||||
for (const FHitResult& Hit : HitResults) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *Hit.GetActor()->GetName());
|
||||
}
|
||||
```
|
||||
|
||||
### Sweep (Thick Trace)
|
||||
|
||||
```cpp
|
||||
FHitResult HitResult;
|
||||
FCollisionShape Sphere = FCollisionShape::MakeSphere(50.0f);
|
||||
|
||||
bool bHit = GetWorld()->SweepSingleByChannel(
|
||||
HitResult,
|
||||
Start,
|
||||
End,
|
||||
FQuat::Identity,
|
||||
ECC_Visibility,
|
||||
Sphere,
|
||||
QueryParams
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Character Movement
|
||||
|
||||
### Character Movement Component
|
||||
|
||||
```cpp
|
||||
// Built into ACharacter class
|
||||
UCharacterMovementComponent* MoveComp = GetCharacterMovement();
|
||||
|
||||
// Configure movement
|
||||
MoveComp->MaxWalkSpeed = 600.0f;
|
||||
MoveComp->JumpZVelocity = 600.0f;
|
||||
MoveComp->AirControl = 0.2f;
|
||||
MoveComp->GravityScale = 1.0f;
|
||||
MoveComp->bOrientRotationToMovement = true;
|
||||
```
|
||||
|
||||
### Add Movement Input
|
||||
|
||||
```cpp
|
||||
// In Character class
|
||||
void AMyCharacter::MoveForward(float Value) {
|
||||
if (Value != 0.0f) {
|
||||
AddMovementInput(GetActorForwardVector(), Value);
|
||||
}
|
||||
}
|
||||
|
||||
void AMyCharacter::MoveRight(float Value) {
|
||||
if (Value != 0.0f) {
|
||||
AddMovementInput(GetActorRightVector(), Value);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Physical Materials
|
||||
|
||||
### Create Physical Material
|
||||
|
||||
1. Content Browser > Right Click > Physics > Physical Material
|
||||
2. Configure properties:
|
||||
- Friction: 0.0 - 1.0
|
||||
- Restitution (bounciness): 0.0 - 1.0
|
||||
|
||||
### Assign Physical Material
|
||||
|
||||
```cpp
|
||||
// In static mesh editor: Physics > Phys Material Override
|
||||
// Or in C++:
|
||||
MeshComp->SetPhysMaterialOverride(PhysicalMaterial);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Constraints (Physics Joints)
|
||||
|
||||
### Physics Constraint Component
|
||||
|
||||
```cpp
|
||||
UPhysicsConstraintComponent* Constraint = CreateDefaultSubobject<UPhysicsConstraintComponent>(TEXT("Constraint"));
|
||||
Constraint->SetConstrainedComponents(ComponentA, NAME_None, ComponentB, NAME_None);
|
||||
|
||||
// Configure constraint
|
||||
Constraint->SetLinearXLimit(ELinearConstraintMotion::LCM_Limited, 100.0f);
|
||||
Constraint->SetLinearYLimit(ELinearConstraintMotion::LCM_Locked, 0.0f);
|
||||
Constraint->SetLinearZLimit(ELinearConstraintMotion::LCM_Free, 0.0f);
|
||||
|
||||
Constraint->SetAngularSwing1Limit(EAngularConstraintMotion::ACM_Limited, 45.0f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Destruction (Chaos Destruction)
|
||||
|
||||
### Enable Chaos Destruction
|
||||
|
||||
```cpp
|
||||
// Plugin: Enable "Chaos" plugin
|
||||
// Create Geometry Collection asset for destructible objects
|
||||
```
|
||||
|
||||
### Destroy Geometry Collection
|
||||
|
||||
```cpp
|
||||
// Fracture mesh in Chaos editor
|
||||
// In game, apply damage:
|
||||
UGeometryCollectionComponent* GeoComp = /* Get component */;
|
||||
GeoComp->ApplyPhysicsField(/* Field parameters */);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Physics Optimization
|
||||
|
||||
```cpp
|
||||
// Simplify collision shapes (use simple primitives)
|
||||
MeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision); // Disable when not needed
|
||||
|
||||
// Use Physics Asset for skeletal meshes (simplified collision)
|
||||
// Don't simulate physics for distant objects
|
||||
|
||||
// Reduce physics substeps:
|
||||
// Project Settings > Engine > Physics > Max Substep Delta Time
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Physics Debug Visualization
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// show collision - Show collision shapes
|
||||
// p.Chaos.DebugDraw.Enabled 1 - Show Chaos debug
|
||||
// pxvis collision - Visualize collision
|
||||
|
||||
// Draw debug shapes:
|
||||
DrawDebugSphere(GetWorld(), Location, Radius, 12, FColor::Green, false, 2.0f);
|
||||
DrawDebugBox(GetWorld(), Location, Extent, FColor::Red, false, 2.0f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/physics-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/chaos-physics-overview-in-unreal-engine/
|
||||
297
docs/engine-reference/unreal/modules/rendering.md
Normal file
297
docs/engine-reference/unreal/modules/rendering.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Unreal Engine 5.7 — Rendering Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 has Megalights, production-ready Substrate, and Lumen improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 rendering stack:
|
||||
- **Lumen**: Real-time global illumination (default)
|
||||
- **Nanite**: Virtualized geometry for millions of triangles
|
||||
- **Megalights**: Support for millions of dynamic lights (NEW in 5.5+)
|
||||
- **Substrate**: Production-ready modular material system (NEW in 5.7)
|
||||
|
||||
---
|
||||
|
||||
## Lumen (Global Illumination)
|
||||
|
||||
### Enable Lumen
|
||||
|
||||
```cpp
|
||||
// Project Settings > Engine > Rendering > Dynamic Global Illumination Method = Lumen
|
||||
// Real-time GI, no lightmap baking needed
|
||||
```
|
||||
|
||||
### Lumen Quality Settings
|
||||
|
||||
```ini
|
||||
; DefaultEngine.ini
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.Lumen.DiffuseColorBoost=1.0
|
||||
r.Lumen.ScreenProbeGather.RadianceCache.NumFramesToKeepCached=2
|
||||
```
|
||||
|
||||
### Lumen in C++
|
||||
|
||||
```cpp
|
||||
// Check if Lumen is enabled
|
||||
bool bIsLumenEnabled = IConsoleManager::Get().FindConsoleVariable(TEXT("r.DynamicGlobalIlluminationMethod"))->GetInt() == 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nanite (Virtualized Geometry)
|
||||
|
||||
### Enable Nanite on Static Mesh
|
||||
|
||||
1. Static Mesh Editor
|
||||
2. Details > Nanite Settings > Enable Nanite Support
|
||||
3. Save mesh (auto-builds Nanite data)
|
||||
|
||||
### Nanite in C++
|
||||
|
||||
```cpp
|
||||
// Spawn Nanite mesh
|
||||
UStaticMeshComponent* MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
|
||||
MeshComp->SetStaticMesh(NaniteMesh); // Automatically uses Nanite if enabled
|
||||
```
|
||||
|
||||
### Nanite Limitations
|
||||
- No vertex animation (skeletal meshes)
|
||||
- No world position offset (WPO) in materials
|
||||
- Best for static, high-poly geometry
|
||||
|
||||
---
|
||||
|
||||
## Megalights (UE 5.5+)
|
||||
|
||||
### Enable Megalights
|
||||
|
||||
```cpp
|
||||
// Project Settings > Engine > Rendering > Megalights = Enabled
|
||||
// Supports millions of dynamic lights with minimal performance cost
|
||||
```
|
||||
|
||||
### Megalights Usage
|
||||
|
||||
```cpp
|
||||
// Add point lights as usual
|
||||
UPointLightComponent* Light = CreateDefaultSubobject<UPointLightComponent>(TEXT("Light"));
|
||||
Light->SetIntensity(5000.0f);
|
||||
Light->SetAttenuationRadius(500.0f);
|
||||
|
||||
// Megalights automatically handles thousands/millions of these
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Substrate Materials (Production-Ready in 5.7)
|
||||
|
||||
### Enable Substrate
|
||||
|
||||
```cpp
|
||||
// Project Settings > Engine > Substrate > Enable Substrate
|
||||
// Restart editor
|
||||
```
|
||||
|
||||
### Substrate Material Nodes
|
||||
- **Substrate Slab**: Physical material layer (diffuse, specular, etc.)
|
||||
- **Substrate Blend**: Blend multiple layers
|
||||
- **Substrate Thin Film**: Iridescence, soap bubbles
|
||||
- **Substrate Hair**: Hair-specific shading
|
||||
|
||||
### Example Substrate Material Graph
|
||||
|
||||
```
|
||||
Substrate Slab (Diffuse)
|
||||
└─ Base Color: Texture Sample
|
||||
└─ Roughness: Constant (0.5)
|
||||
└─ Metallic: Constant (0.0)
|
||||
└─ Connect to Material Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Materials (C++ API)
|
||||
|
||||
### Dynamic Material Instances
|
||||
|
||||
```cpp
|
||||
// Create dynamic material instance
|
||||
UMaterialInstanceDynamic* DynMat = UMaterialInstanceDynamic::Create(BaseMaterial, this);
|
||||
|
||||
// Set parameters
|
||||
DynMat->SetVectorParameterValue(TEXT("BaseColor"), FLinearColor::Red);
|
||||
DynMat->SetScalarParameterValue(TEXT("Metallic"), 0.8f);
|
||||
DynMat->SetTextureParameterValue(TEXT("DiffuseTexture"), MyTexture);
|
||||
|
||||
// Apply to mesh
|
||||
MeshComp->SetMaterial(0, DynMat);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Processing
|
||||
|
||||
### Post-Process Volume
|
||||
|
||||
```cpp
|
||||
// Add to level
|
||||
APostProcessVolume* PPV = GetWorld()->SpawnActor<APostProcessVolume>();
|
||||
PPV->bUnbound = true; // Affect entire world
|
||||
|
||||
// Configure settings
|
||||
PPV->Settings.bOverride_MotionBlurAmount = true;
|
||||
PPV->Settings.MotionBlurAmount = 0.5f;
|
||||
|
||||
PPV->Settings.bOverride_BloomIntensity = true;
|
||||
PPV->Settings.BloomIntensity = 1.0f;
|
||||
```
|
||||
|
||||
### Post-Process in C++
|
||||
|
||||
```cpp
|
||||
// Access camera post-process settings
|
||||
APlayerController* PC = GetWorld()->GetFirstPlayerController();
|
||||
if (APlayerCameraManager* CamManager = PC->PlayerCameraManager) {
|
||||
CamManager->PostProcessBlendWeight = 1.0f;
|
||||
CamManager->PostProcessSettings.BloomIntensity = 2.0f;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Lighting
|
||||
|
||||
### Directional Light (Sun)
|
||||
|
||||
```cpp
|
||||
ADirectionalLight* Sun = GetWorld()->SpawnActor<ADirectionalLight>();
|
||||
Sun->SetActorRotation(FRotator(-45.f, 0.f, 0.f));
|
||||
Sun->GetLightComponent()->SetIntensity(10.0f);
|
||||
Sun->GetLightComponent()->bCastShadows = true;
|
||||
```
|
||||
|
||||
### Point Light
|
||||
|
||||
```cpp
|
||||
APointLight* Light = GetWorld()->SpawnActor<APointLight>();
|
||||
Light->SetActorLocation(FVector(0, 0, 200));
|
||||
Light->GetPointLightComponent()->SetIntensity(5000.0f);
|
||||
Light->GetPointLightComponent()->SetAttenuationRadius(1000.0f);
|
||||
Light->GetPointLightComponent()->SetLightColor(FLinearColor::Red);
|
||||
```
|
||||
|
||||
### Spot Light
|
||||
|
||||
```cpp
|
||||
ASpotLight* Spotlight = GetWorld()->SpawnActor<ASpotLight>();
|
||||
Spotlight->GetSpotLightComponent()->SetInnerConeAngle(20.0f);
|
||||
Spotlight->GetSpotLightComponent()->SetOuterConeAngle(40.0f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Render Targets (Render to Texture)
|
||||
|
||||
### Create Render Target
|
||||
|
||||
```cpp
|
||||
// Create render target asset (2D texture)
|
||||
UTextureRenderTarget2D* RenderTarget = NewObject<UTextureRenderTarget2D>();
|
||||
RenderTarget->InitAutoFormat(512, 512); // 512x512 resolution
|
||||
RenderTarget->UpdateResourceImmediate();
|
||||
|
||||
// Render scene to texture
|
||||
UKismetRenderingLibrary::DrawMaterialToRenderTarget(
|
||||
GetWorld(),
|
||||
RenderTarget,
|
||||
MaterialToDraw
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Custom Render Passes (Advanced)
|
||||
|
||||
### Render Dependency Graph (RDG)
|
||||
|
||||
```cpp
|
||||
// UE5 uses Render Dependency Graph for custom rendering
|
||||
// Example: Custom post-process pass
|
||||
|
||||
#include "RenderGraphBuilder.h"
|
||||
|
||||
void RenderCustomPass(FRDGBuilder& GraphBuilder, const FViewInfo& View) {
|
||||
FRDGTextureRef SceneColor = /* Get scene color texture */;
|
||||
|
||||
// Define pass parameters
|
||||
struct FPassParameters {
|
||||
FRDGTextureRef InputTexture;
|
||||
};
|
||||
|
||||
FPassParameters* PassParams = GraphBuilder.AllocParameters<FPassParameters>();
|
||||
PassParams->InputTexture = SceneColor;
|
||||
|
||||
// Add render pass
|
||||
GraphBuilder.AddPass(
|
||||
RDG_EVENT_NAME("CustomPass"),
|
||||
PassParams,
|
||||
ERDGPassFlags::Raster,
|
||||
[](FRHICommandList& RHICmdList, const FPassParameters* Params) {
|
||||
// Render commands
|
||||
}
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### Render Stats
|
||||
|
||||
```cpp
|
||||
// Console commands for profiling:
|
||||
// stat fps - Show FPS
|
||||
// stat unit - Show frame time breakdown
|
||||
// stat gpu - Show GPU timings
|
||||
// profilegpu - Detailed GPU profile
|
||||
```
|
||||
|
||||
### Scalability Settings
|
||||
|
||||
```cpp
|
||||
// Get current scalability settings
|
||||
UGameUserSettings* Settings = UGameUserSettings::GetGameUserSettings();
|
||||
int32 ViewDistanceQuality = Settings->GetViewDistanceQuality(); // 0-4
|
||||
|
||||
// Set scalability
|
||||
Settings->SetViewDistanceQuality(3); // High
|
||||
Settings->SetShadowQuality(2); // Medium
|
||||
Settings->ApplySettings(false);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Visualize Render Features
|
||||
|
||||
```
|
||||
Console commands:
|
||||
- r.Lumen.Visualize 1 - Show Lumen debug
|
||||
- r.Nanite.Visualize 1 - Show Nanite triangles
|
||||
- viewmode wireframe - Wireframe mode
|
||||
- viewmode unlit - Disable lighting
|
||||
- show collision - Show collision meshes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/lumen-global-illumination-and-reflections-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/nanite-virtualized-geometry-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/substrate-materials-in-unreal-engine/
|
||||
353
docs/engine-reference/unreal/modules/ui.md
Normal file
353
docs/engine-reference/unreal/modules/ui.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# Unreal Engine 5.7 — UI Module Reference
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Knowledge Gap:** UE 5.7 UMG and CommonUI improvements
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
UE 5.7 UI systems:
|
||||
- **UMG (Unreal Motion Graphics)**: Visual widget-based UI (RECOMMENDED)
|
||||
- **CommonUI**: Cross-platform input-aware UI framework (console/PC)
|
||||
- **Slate**: Low-level C++ UI (engine/editor UI)
|
||||
|
||||
---
|
||||
|
||||
## UMG (Unreal Motion Graphics)
|
||||
|
||||
### Create Widget Blueprint
|
||||
|
||||
1. Content Browser > User Interface > Widget Blueprint
|
||||
2. Open Widget Designer
|
||||
3. Drag widgets from Palette: Button, Text, Image, ProgressBar, etc.
|
||||
|
||||
---
|
||||
|
||||
## Basic UMG Setup in C++
|
||||
|
||||
### Create and Display Widget
|
||||
|
||||
```cpp
|
||||
#include "Blueprint/UserWidget.h"
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "UI")
|
||||
TSubclassOf<UUserWidget> HealthBarWidgetClass;
|
||||
|
||||
void AMyCharacter::BeginPlay() {
|
||||
Super::BeginPlay();
|
||||
|
||||
// Create widget
|
||||
UUserWidget* HealthBarWidget = CreateWidget<UUserWidget>(GetWorld(), HealthBarWidgetClass);
|
||||
|
||||
// Add to viewport
|
||||
HealthBarWidget->AddToViewport();
|
||||
}
|
||||
```
|
||||
|
||||
### Remove Widget
|
||||
|
||||
```cpp
|
||||
HealthBarWidget->RemoveFromParent();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Access Widget Elements from C++
|
||||
|
||||
### Bind to Widget Elements
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UMyHealthWidget : public UUserWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// ✅ Bind to widget elements (must match names in Widget Blueprint)
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> HealthText;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UProgressBar> HealthBar;
|
||||
|
||||
void UpdateHealth(int32 CurrentHealth, int32 MaxHealth) {
|
||||
HealthText->SetText(FText::FromString(FString::Printf(TEXT("%d / %d"), CurrentHealth, MaxHealth)));
|
||||
HealthBar->SetPercent((float)CurrentHealth / MaxHealth);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common UMG Widgets
|
||||
|
||||
### Text Block
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UTextBlock> ScoreText;
|
||||
|
||||
ScoreText->SetText(FText::FromString(TEXT("Score: 100")));
|
||||
ScoreText->SetColorAndOpacity(FLinearColor::Green);
|
||||
```
|
||||
|
||||
### Button
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UButton> PlayButton;
|
||||
|
||||
void NativeConstruct() override {
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Bind button click
|
||||
PlayButton->OnClicked.AddDynamic(this, &UMyMenuWidget::OnPlayClicked);
|
||||
}
|
||||
|
||||
UFUNCTION()
|
||||
void OnPlayClicked() {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Play clicked"));
|
||||
}
|
||||
```
|
||||
|
||||
### Image
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UImage> PlayerAvatar;
|
||||
|
||||
PlayerAvatar->SetBrushFromTexture(AvatarTexture);
|
||||
PlayerAvatar->SetColorAndOpacity(FLinearColor::White);
|
||||
```
|
||||
|
||||
### Progress Bar
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UProgressBar> HealthBar;
|
||||
|
||||
HealthBar->SetPercent(0.75f); // 75%
|
||||
HealthBar->SetFillColorAndOpacity(FLinearColor::Red);
|
||||
```
|
||||
|
||||
### Slider
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<USlider> VolumeSlider;
|
||||
|
||||
void NativeConstruct() override {
|
||||
Super::NativeConstruct();
|
||||
VolumeSlider->OnValueChanged.AddDynamic(this, &UMyWidget::OnVolumeChanged);
|
||||
}
|
||||
|
||||
UFUNCTION()
|
||||
void OnVolumeChanged(float Value) {
|
||||
// Value is 0.0 - 1.0
|
||||
UE_LOG(LogTemp, Warning, TEXT("Volume: %f"), Value);
|
||||
}
|
||||
```
|
||||
|
||||
### EditableTextBox (Input Field)
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UEditableTextBox> PlayerNameInput;
|
||||
|
||||
void NativeConstruct() override {
|
||||
Super::NativeConstruct();
|
||||
PlayerNameInput->OnTextChanged.AddDynamic(this, &UMyWidget::OnNameChanged);
|
||||
}
|
||||
|
||||
UFUNCTION()
|
||||
void OnNameChanged(const FText& Text) {
|
||||
FString PlayerName = Text.ToString();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## UMG Animations
|
||||
|
||||
### Play Animation
|
||||
|
||||
```cpp
|
||||
UPROPERTY(Transient, meta = (BindWidgetAnim))
|
||||
TObjectPtr<UWidgetAnimation> FadeInAnimation;
|
||||
|
||||
void ShowUI() {
|
||||
PlayAnimation(FadeInAnimation);
|
||||
}
|
||||
```
|
||||
|
||||
### Stop Animation
|
||||
|
||||
```cpp
|
||||
StopAnimation(FadeInAnimation);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Canvas Panel (Layout)
|
||||
|
||||
### Canvas Panel (Absolute Positioning)
|
||||
|
||||
```cpp
|
||||
// Use in Widget Blueprint for absolute positioning
|
||||
// Anchor widgets to corners/edges for responsive UI
|
||||
```
|
||||
|
||||
### Vertical Box (Stack Vertically)
|
||||
|
||||
```cpp
|
||||
// Auto-stacks children vertically
|
||||
```
|
||||
|
||||
### Horizontal Box (Stack Horizontally)
|
||||
|
||||
```cpp
|
||||
// Auto-stacks children horizontally
|
||||
```
|
||||
|
||||
### Grid Panel (Grid Layout)
|
||||
|
||||
```cpp
|
||||
// Arranges children in a grid
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## World Space UI (3D UI)
|
||||
|
||||
### Widget Component (3D UI in World)
|
||||
|
||||
```cpp
|
||||
#include "Components/WidgetComponent.h"
|
||||
|
||||
UWidgetComponent* HealthBarWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("HealthBar"));
|
||||
HealthBarWidget->SetupAttachment(RootComponent);
|
||||
HealthBarWidget->SetWidgetClass(HealthBarWidgetClass);
|
||||
HealthBarWidget->SetWidgetSpace(EWidgetSpace::World); // 3D world space
|
||||
HealthBarWidget->SetDrawSize(FVector2D(200, 50));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Handling in UMG
|
||||
|
||||
### Override Keyboard Input
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UMyWidget : public UUserWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual FReply NativeOnKeyDown(const FGeometry& InGeometry, const FKeyEvent& InKeyEvent) override {
|
||||
if (InKeyEvent.GetKey() == EKeys::Escape) {
|
||||
// Handle Escape key
|
||||
CloseMenu();
|
||||
return FReply::Handled();
|
||||
}
|
||||
return Super::NativeOnKeyDown(InGeometry, InKeyEvent);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CommonUI (Cross-Platform Input)
|
||||
|
||||
### Enable CommonUI Plugin
|
||||
|
||||
```cpp
|
||||
// Enable: Edit > Plugins > CommonUI
|
||||
// Restart editor
|
||||
```
|
||||
|
||||
### Use CommonUI Widgets
|
||||
|
||||
```cpp
|
||||
// CommonUI widgets:
|
||||
// - CommonActivatableWidget: Base for screens/menus
|
||||
// - CommonButtonBase: Input-aware button (gamepad + mouse)
|
||||
// - CommonTextBlock: Text with styling
|
||||
```
|
||||
|
||||
### CommonActivatableWidget Example
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UMyMenuWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void NativeOnActivated() override {
|
||||
Super::NativeOnActivated();
|
||||
// Menu activated (shown)
|
||||
}
|
||||
|
||||
virtual void NativeOnDeactivated() override {
|
||||
Super::NativeOnDeactivated();
|
||||
// Menu deactivated (hidden)
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HUD Class (Alternative to UMG)
|
||||
|
||||
### Create HUD
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class AMyHUD : public AHUD {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual void DrawHUD() override {
|
||||
Super::DrawHUD();
|
||||
|
||||
// Draw text
|
||||
DrawText(TEXT("Score: 100"), FLinearColor::White, 50, 50);
|
||||
|
||||
// Draw texture
|
||||
DrawTexture(CrosshairTexture, Canvas->SizeX / 2, Canvas->SizeY / 2, 32, 32);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
### Optimize UMG
|
||||
|
||||
```cpp
|
||||
// Invalidation boxes: Only redraw when content changes
|
||||
// Add "Invalidation Box" widget to Widget Blueprint
|
||||
|
||||
// Disable tick if not needed
|
||||
bIsFocusable = false;
|
||||
SetVisibility(ESlateVisibility::Collapsed); // Collapsed = not rendered
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### UI Debug Commands
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// widget.debug - Show widget hierarchy
|
||||
// Slate.ShowDebugOutlines 1 - Show widget bounds
|
||||
// stat slate - Show Slate performance
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/umg-ui-designer-for-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/commonui-plugin-for-advanced-user-interfaces-in-unreal-engine/
|
||||
389
docs/engine-reference/unreal/plugins/common-ui.md
Normal file
389
docs/engine-reference/unreal/plugins/common-ui.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# Unreal Engine 5.7 — CommonUI Plugin
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready
|
||||
**Plugin:** `CommonUI` (built-in, enable in Plugins)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**CommonUI** is a cross-platform UI framework that automatically handles input routing
|
||||
for gamepad, mouse, and touch. It's designed for games that need to work seamlessly
|
||||
across PC, console, and mobile platforms with minimal platform-specific code.
|
||||
|
||||
**Use CommonUI for:**
|
||||
- Multi-platform games (console + PC)
|
||||
- Automatic gamepad/mouse/touch input routing
|
||||
- Input-agnostic UI (same UI works with any input method)
|
||||
- Widget focus and navigation
|
||||
- Action bars and input hints
|
||||
|
||||
**DON'T use CommonUI for:**
|
||||
- PC-only games with mouse-only UI (standard UMG is simpler)
|
||||
- Simple UI with no navigation requirements
|
||||
|
||||
---
|
||||
|
||||
## Key Differences from Standard UMG
|
||||
|
||||
| Feature | Standard UMG | CommonUI |
|
||||
|---------|--------------|----------|
|
||||
| **Input Handling** | Manual per widget | Automatic routing |
|
||||
| **Focus Management** | Basic | Advanced navigation |
|
||||
| **Platform Switching** | Manual detection | Automatic |
|
||||
| **Input Prompts** | Hardcode icons | Dynamic per platform |
|
||||
| **Screen Stack** | Manual | Built-in activatable widgets |
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Enable Plugin
|
||||
|
||||
`Edit > Plugins > CommonUI > Enabled > Restart`
|
||||
|
||||
### 2. Configure Project Settings
|
||||
|
||||
`Project Settings > Plugins > CommonUI`:
|
||||
- **Default Input Type**: Gamepad (or auto-detect)
|
||||
- **Platform-Specific Settings**: Configure input icons per platform
|
||||
|
||||
### 3. Create Common Input Settings Asset
|
||||
|
||||
1. Content Browser > Input > Common Input Settings
|
||||
2. Configure input data per platform:
|
||||
- Default Gamepad Data
|
||||
- Default Mouse & Keyboard Data
|
||||
- Default Touch Data
|
||||
|
||||
---
|
||||
|
||||
## Core Widgets
|
||||
|
||||
### CommonActivatableWidget (Screen Management)
|
||||
|
||||
Base class for screens/menus that can be activated/deactivated.
|
||||
|
||||
```cpp
|
||||
#include "CommonActivatableWidget.h"
|
||||
|
||||
UCLASS()
|
||||
class UMyMenuWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NativeOnActivated() override {
|
||||
Super::NativeOnActivated();
|
||||
// Menu is now visible and focused
|
||||
UE_LOG(LogTemp, Warning, TEXT("Menu activated"));
|
||||
}
|
||||
|
||||
virtual void NativeOnDeactivated() override {
|
||||
Super::NativeOnDeactivated();
|
||||
// Menu is now hidden
|
||||
UE_LOG(LogTemp, Warning, TEXT("Menu deactivated"));
|
||||
}
|
||||
|
||||
virtual UWidget* NativeGetDesiredFocusTarget() const override {
|
||||
// Return widget that should receive focus (e.g., first button)
|
||||
return PlayButton;
|
||||
}
|
||||
|
||||
private:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonButtonBase> PlayButton;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CommonButtonBase (Input-Aware Button)
|
||||
|
||||
Replaces standard UMG Button. Automatically handles gamepad/mouse/keyboard input.
|
||||
|
||||
```cpp
|
||||
#include "CommonButtonBase.h"
|
||||
|
||||
UCLASS()
|
||||
class UMyMenuWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonButtonBase> PlayButton;
|
||||
|
||||
virtual void NativeConstruct() override {
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Bind button click (works with any input method)
|
||||
PlayButton->OnClicked().AddUObject(this, &UMyMenuWidget::OnPlayClicked);
|
||||
|
||||
// Set button text
|
||||
PlayButton->SetButtonText(FText::FromString(TEXT("Play")));
|
||||
}
|
||||
|
||||
void OnPlayClicked() {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Play clicked"));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CommonTextBlock (Styled Text)
|
||||
|
||||
Text widget with CommonUI styling support.
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonTextBlock> TitleText;
|
||||
|
||||
TitleText->SetText(FText::FromString(TEXT("Main Menu")));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### CommonActionWidget (Input Prompts)
|
||||
|
||||
Displays input prompts (e.g., "Press A to Continue", automatically shows correct button icon).
|
||||
|
||||
```cpp
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonActionWidget> ConfirmActionWidget;
|
||||
|
||||
// Bind to input action
|
||||
ConfirmActionWidget->SetInputAction(ConfirmInputActionData);
|
||||
// Automatically shows correct icon (A on Xbox, X on PlayStation, Enter on PC)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Widget Stack (Screen Management)
|
||||
|
||||
### CommonActivatableWidgetStack
|
||||
|
||||
Manages a stack of screens (e.g., Main Menu → Settings → Controls).
|
||||
|
||||
```cpp
|
||||
#include "Widgets/CommonActivatableWidgetContainer.h"
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonActivatableWidgetStack> WidgetStack;
|
||||
|
||||
// Push new screen onto stack
|
||||
void ShowSettingsMenu() {
|
||||
WidgetStack->AddWidget(USettingsMenuWidget::StaticClass());
|
||||
}
|
||||
|
||||
// Pop current screen (go back)
|
||||
void GoBack() {
|
||||
WidgetStack->DeactivateWidget();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Actions (CommonUI Style)
|
||||
|
||||
### Define Input Actions
|
||||
|
||||
Create **Common Input Action Data Table**:
|
||||
1. Content Browser > Miscellaneous > Data Table
|
||||
2. Row Structure: `CommonInputActionDataBase`
|
||||
3. Add rows for actions (Confirm, Cancel, Navigate, etc.)
|
||||
|
||||
Example row:
|
||||
- **Action Name**: Confirm
|
||||
- **Default Input**: Gamepad Face Button Bottom (A/Cross)
|
||||
- **Alternate Inputs**: Enter (keyboard), Left Mouse Button
|
||||
|
||||
---
|
||||
|
||||
### Bind Input Actions in Widget
|
||||
|
||||
```cpp
|
||||
#include "Input/CommonUIActionRouterBase.h"
|
||||
|
||||
UCLASS()
|
||||
class UMyWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
virtual void NativeOnActivated() override {
|
||||
Super::NativeOnActivated();
|
||||
|
||||
// Bind input action
|
||||
FBindUIActionArgs BindArgs(ConfirmInputAction, FSimpleDelegate::CreateUObject(this, &UMyWidget::OnConfirm));
|
||||
BindArgs.bDisplayInActionBar = true; // Show in action bar
|
||||
RegisterUIActionBinding(BindArgs);
|
||||
}
|
||||
|
||||
void OnConfirm() {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Confirmed"));
|
||||
}
|
||||
|
||||
private:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
FDataTableRowHandle ConfirmInputAction;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Focus & Navigation
|
||||
|
||||
### Automatic Gamepad Navigation
|
||||
|
||||
CommonUI automatically handles gamepad navigation (D-Pad/Stick to move between buttons).
|
||||
|
||||
```cpp
|
||||
// In Widget Blueprint:
|
||||
// - Widgets are automatically navigable if they inherit from CommonButton/CommonUserWidget
|
||||
// - Focus order is determined by widget hierarchy and layout
|
||||
```
|
||||
|
||||
### Custom Focus Navigation
|
||||
|
||||
```cpp
|
||||
// Override focus navigation
|
||||
virtual UWidget* NativeGetDesiredFocusTarget() const override {
|
||||
return FirstButton; // Return widget that should receive focus
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Input Mode (Game vs UI)
|
||||
|
||||
### Switch Input Mode
|
||||
|
||||
```cpp
|
||||
#include "CommonUIExtensions.h"
|
||||
|
||||
// Switch to UI-only mode (pause game, show cursor)
|
||||
UCommonUIExtensions::PushStreamedGameplayUIInputConfig(this, FrontendInputConfig);
|
||||
|
||||
// Return to game mode (hide cursor, resume gameplay)
|
||||
UCommonUIExtensions::PopInputConfig(this);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Input Icons
|
||||
|
||||
### Configure Input Icons
|
||||
|
||||
1. Create **Common Input Base Controller Data** asset for each platform:
|
||||
- Gamepad (Xbox, PlayStation, Switch)
|
||||
- Mouse & Keyboard
|
||||
- Touch
|
||||
|
||||
2. Assign platform-specific icons:
|
||||
- Gamepad Face Button Bottom: `A` (Xbox), `Cross` (PlayStation)
|
||||
- Confirm Key: `Enter` icon
|
||||
|
||||
3. Assign to **Common Input Settings** asset
|
||||
|
||||
### Automatically Display Correct Icons
|
||||
|
||||
```cpp
|
||||
// CommonActionWidget automatically shows correct icon for current platform
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonActionWidget> JumpActionWidget;
|
||||
|
||||
JumpActionWidget->SetInputAction(JumpInputActionData);
|
||||
// Shows "A" on Xbox, "Cross" on PlayStation, "Space" on PC
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Main Menu with Navigation
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UMainMenuWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonButtonBase> PlayButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonButtonBase> SettingsButton;
|
||||
|
||||
UPROPERTY(meta = (BindWidget))
|
||||
TObjectPtr<UCommonButtonBase> QuitButton;
|
||||
|
||||
virtual void NativeConstruct() override {
|
||||
Super::NativeConstruct();
|
||||
|
||||
PlayButton->OnClicked().AddUObject(this, &UMainMenuWidget::OnPlayClicked);
|
||||
SettingsButton->OnClicked().AddUObject(this, &UMainMenuWidget::OnSettingsClicked);
|
||||
QuitButton->OnClicked().AddUObject(this, &UMainMenuWidget::OnQuitClicked);
|
||||
}
|
||||
|
||||
virtual UWidget* NativeGetDesiredFocusTarget() const override {
|
||||
return PlayButton; // Focus "Play" button when menu opens
|
||||
}
|
||||
|
||||
void OnPlayClicked() { /* Start game */ }
|
||||
void OnSettingsClicked() { /* Open settings */ }
|
||||
void OnQuitClicked() { /* Quit game */ }
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Pause Menu with Back Action
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UPauseMenuWidget : public UCommonActivatableWidget {
|
||||
GENERATED_BODY()
|
||||
|
||||
protected:
|
||||
UPROPERTY(EditDefaultsOnly, Category = "Input")
|
||||
FDataTableRowHandle BackInputAction; // Assign "Cancel" action in Blueprint
|
||||
|
||||
virtual void NativeOnActivated() override {
|
||||
Super::NativeOnActivated();
|
||||
|
||||
// Bind "Back" input (B/Circle/Escape)
|
||||
FBindUIActionArgs BindArgs(BackInputAction, FSimpleDelegate::CreateUObject(this, &UPauseMenuWidget::OnBack));
|
||||
RegisterUIActionBinding(BindArgs);
|
||||
}
|
||||
|
||||
void OnBack() {
|
||||
DeactivateWidget(); // Close pause menu
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Use **CommonActivatableWidgetStack** for screen management (automatically handles activation/deactivation)
|
||||
- Avoid creating/destroying widgets every frame (reuse widgets)
|
||||
- Use **Lazy Widgets** for complex menus (only create when needed)
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### CommonUI Debug Commands
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// CommonUI.DumpActivatableTree - Show active widget hierarchy
|
||||
// CommonUI.DumpActionBindings - Show registered input actions
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/commonui-plugin-for-advanced-user-interfaces-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/commonui-quickstart-guide-for-unreal-engine/
|
||||
386
docs/engine-reference/unreal/plugins/gameplay-ability-system.md
Normal file
386
docs/engine-reference/unreal/plugins/gameplay-ability-system.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# Unreal Engine 5.7 — Gameplay Ability System (GAS)
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready
|
||||
**Plugin:** `GameplayAbilities` (built-in, enable in Plugins)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Gameplay Ability System (GAS)** is a modular framework for building abilities, attributes,
|
||||
effects, and gameplay mechanics. It's the standard for RPGs, MOBAs, shooters with abilities,
|
||||
and any game with complex ability systems.
|
||||
|
||||
**Use GAS for:**
|
||||
- Character abilities (spells, skills, attacks)
|
||||
- Attributes (health, mana, stamina, stats)
|
||||
- Buffs/debuffs (temporary effects)
|
||||
- Cooldowns and costs
|
||||
- Damage calculation
|
||||
- Multiplayer-ready ability replication
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **Ability System Component** (ASC)
|
||||
- The main component that owns abilities, attributes, and effects
|
||||
- Added to Characters or PlayerStates
|
||||
|
||||
### 2. **Gameplay Abilities**
|
||||
- Individual skills/actions (fireball, heal, dash, etc.)
|
||||
- Activated, committed (cost/cooldown), and can be cancelled
|
||||
|
||||
### 3. **Attributes & Attribute Sets**
|
||||
- Stats that can be modified (Health, Mana, Stamina, Strength, etc.)
|
||||
- Stored in Attribute Sets
|
||||
|
||||
### 4. **Gameplay Effects**
|
||||
- Modify attributes (damage, healing, buffs, debuffs)
|
||||
- Can be instant, duration-based, or infinite
|
||||
|
||||
### 5. **Gameplay Tags**
|
||||
- Hierarchical tags for ability logic (e.g., `Ability.Attack.Melee`, `Status.Stunned`)
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Enable Plugin
|
||||
|
||||
`Edit > Plugins > Gameplay Abilities > Enabled > Restart`
|
||||
|
||||
### 2. Add Ability System Component
|
||||
|
||||
```cpp
|
||||
#include "AbilitySystemComponent.h"
|
||||
#include "AttributeSet.h"
|
||||
|
||||
UCLASS()
|
||||
class AMyCharacter : public ACharacter {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMyCharacter() {
|
||||
// Create ASC
|
||||
AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystem"));
|
||||
AbilitySystemComponent->SetIsReplicated(true);
|
||||
AbilitySystemComponent->SetReplicationMode(EGameplayEffectReplicationMode::Mixed);
|
||||
|
||||
// Create Attribute Set
|
||||
AttributeSet = CreateDefaultSubobject<UMyAttributeSet>(TEXT("AttributeSet"));
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Abilities")
|
||||
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;
|
||||
|
||||
UPROPERTY()
|
||||
TObjectPtr<const UAttributeSet> AttributeSet;
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Initialize ASC (Important for Multiplayer)
|
||||
|
||||
```cpp
|
||||
void AMyCharacter::PossessedBy(AController* NewController) {
|
||||
Super::PossessedBy(NewController);
|
||||
|
||||
// Server: Initialize ASC
|
||||
if (AbilitySystemComponent) {
|
||||
AbilitySystemComponent->InitAbilityActorInfo(this, this);
|
||||
GiveDefaultAbilities();
|
||||
}
|
||||
}
|
||||
|
||||
void AMyCharacter::OnRep_PlayerState() {
|
||||
Super::OnRep_PlayerState();
|
||||
|
||||
// Client: Initialize ASC
|
||||
if (AbilitySystemComponent) {
|
||||
AbilitySystemComponent->InitAbilityActorInfo(this, this);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Attributes & Attribute Sets
|
||||
|
||||
### Create Attribute Set
|
||||
|
||||
```cpp
|
||||
#include "AttributeSet.h"
|
||||
#include "AbilitySystemComponent.h"
|
||||
|
||||
UCLASS()
|
||||
class UMyAttributeSet : public UAttributeSet {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMyAttributeSet();
|
||||
|
||||
// Health
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Health)
|
||||
FGameplayAttributeData Health;
|
||||
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Health)
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_MaxHealth)
|
||||
FGameplayAttributeData MaxHealth;
|
||||
ATTRIBUTE_ACCESSORS(UMyAttributeSet, MaxHealth)
|
||||
|
||||
// Mana
|
||||
UPROPERTY(BlueprintReadOnly, Category = "Attributes", ReplicatedUsing = OnRep_Mana)
|
||||
FGameplayAttributeData Mana;
|
||||
ATTRIBUTE_ACCESSORS(UMyAttributeSet, Mana)
|
||||
|
||||
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
|
||||
|
||||
protected:
|
||||
UFUNCTION()
|
||||
virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void OnRep_MaxHealth(const FGameplayAttributeData& OldMaxHealth);
|
||||
|
||||
UFUNCTION()
|
||||
virtual void OnRep_Mana(const FGameplayAttributeData& OldMana);
|
||||
};
|
||||
```
|
||||
|
||||
### Implement Attribute Set
|
||||
|
||||
```cpp
|
||||
#include "Net/UnrealNetwork.h"
|
||||
|
||||
UMyAttributeSet::UMyAttributeSet() {
|
||||
// Default values
|
||||
Health = 100.0f;
|
||||
MaxHealth = 100.0f;
|
||||
Mana = 50.0f;
|
||||
}
|
||||
|
||||
void UMyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const {
|
||||
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
|
||||
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, MaxHealth, COND_None, REPNOTIFY_Always);
|
||||
DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Mana, COND_None, REPNOTIFY_Always);
|
||||
}
|
||||
|
||||
void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) {
|
||||
GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
|
||||
}
|
||||
|
||||
// Implement other OnRep functions similarly...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Abilities
|
||||
|
||||
### Create Gameplay Ability
|
||||
|
||||
```cpp
|
||||
#include "Abilities/GameplayAbility.h"
|
||||
|
||||
UCLASS()
|
||||
class UGA_Fireball : public UGameplayAbility {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGA_Fireball() {
|
||||
// Ability config
|
||||
InstancingPolicy = EGameplayAbilityInstancingPolicy::InstancedPerActor;
|
||||
NetExecutionPolicy = EGameplayAbilityNetExecutionPolicy::ServerInitiated;
|
||||
|
||||
// Tags
|
||||
AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Fireball")));
|
||||
}
|
||||
|
||||
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo,
|
||||
const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData) override {
|
||||
|
||||
if (!CommitAbility(Handle, ActorInfo, ActivationInfo)) {
|
||||
// Failed to commit (not enough mana, on cooldown, etc.)
|
||||
EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn fireball projectile
|
||||
SpawnFireball();
|
||||
|
||||
// End ability
|
||||
EndAbility(Handle, ActorInfo, ActivationInfo, true, false);
|
||||
}
|
||||
|
||||
void SpawnFireball() {
|
||||
// Spawn fireball logic
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Grant Abilities to Character
|
||||
|
||||
```cpp
|
||||
void AMyCharacter::GiveDefaultAbilities() {
|
||||
if (!HasAuthority() || !AbilitySystemComponent) return;
|
||||
|
||||
// Grant abilities
|
||||
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGA_Fireball::StaticClass(), 1, INDEX_NONE, this));
|
||||
AbilitySystemComponent->GiveAbility(FGameplayAbilitySpec(UGA_Heal::StaticClass(), 1, INDEX_NONE, this));
|
||||
}
|
||||
```
|
||||
|
||||
### Activate Ability
|
||||
|
||||
```cpp
|
||||
// Activate by class
|
||||
AbilitySystemComponent->TryActivateAbilityByClass(UGA_Fireball::StaticClass());
|
||||
|
||||
// Activate by tag
|
||||
FGameplayTagContainer TagContainer;
|
||||
TagContainer.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Fireball")));
|
||||
AbilitySystemComponent->TryActivateAbilitiesByTag(TagContainer);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Effects
|
||||
|
||||
### Create Gameplay Effect (Damage)
|
||||
|
||||
```cpp
|
||||
// Create Blueprint: Content Browser > Gameplay > Gameplay Effect
|
||||
|
||||
// OR in C++:
|
||||
UCLASS()
|
||||
class UGE_Damage : public UGameplayEffect {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGE_Damage() {
|
||||
// Instant damage
|
||||
DurationPolicy = EGameplayEffectDurationType::Instant;
|
||||
|
||||
// Modifier: Reduce Health
|
||||
FGameplayModifierInfo ModifierInfo;
|
||||
ModifierInfo.Attribute = UMyAttributeSet::GetHealthAttribute();
|
||||
ModifierInfo.ModifierOp = EGameplayModOp::Additive;
|
||||
ModifierInfo.ModifierMagnitude = FScalableFloat(-25.0f); // -25 health
|
||||
|
||||
Modifiers.Add(ModifierInfo);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Apply Gameplay Effect
|
||||
|
||||
```cpp
|
||||
// Apply damage to target
|
||||
if (UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Target)) {
|
||||
FGameplayEffectContextHandle EffectContext = AbilitySystemComponent->MakeEffectContext();
|
||||
EffectContext.AddSourceObject(this);
|
||||
|
||||
FGameplayEffectSpecHandle SpecHandle = AbilitySystemComponent->MakeOutgoingSpec(
|
||||
UGE_Damage::StaticClass(), 1, EffectContext);
|
||||
|
||||
if (SpecHandle.IsValid()) {
|
||||
AbilitySystemComponent->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), TargetASC);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Tags
|
||||
|
||||
### Define Tags
|
||||
|
||||
`Project Settings > Project > Gameplay Tags > Gameplay Tag List`
|
||||
|
||||
Example hierarchy:
|
||||
```
|
||||
Ability
|
||||
├─ Ability.Attack
|
||||
│ ├─ Ability.Attack.Melee
|
||||
│ └─ Ability.Attack.Ranged
|
||||
├─ Ability.Defend
|
||||
└─ Ability.Utility
|
||||
|
||||
Status
|
||||
├─ Status.Stunned
|
||||
├─ Status.Invulnerable
|
||||
└─ Status.Silenced
|
||||
```
|
||||
|
||||
### Use Tags in Abilities
|
||||
|
||||
```cpp
|
||||
UCLASS()
|
||||
class UGA_MeleeAttack : public UGameplayAbility {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UGA_MeleeAttack() {
|
||||
// This ability has these tags
|
||||
AbilityTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack.Melee")));
|
||||
|
||||
// Block these tags while active
|
||||
BlockAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Attack")));
|
||||
|
||||
// Cancel these abilities when activated
|
||||
CancelAbilitiesWithTag.AddTag(FGameplayTag::RequestGameplayTag(FName("Ability.Defend")));
|
||||
|
||||
// Can't activate if target has these tags
|
||||
ActivationBlockedTags.AddTag(FGameplayTag::RequestGameplayTag(FName("Status.Stunned")));
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cooldowns & Costs
|
||||
|
||||
### Add Cooldown
|
||||
|
||||
```cpp
|
||||
// In Ability Blueprint or C++:
|
||||
// Create Gameplay Effect with Duration = Cooldown time
|
||||
// Assign to Ability > Cooldown Gameplay Effect Class
|
||||
```
|
||||
|
||||
### Add Cost (Mana)
|
||||
|
||||
```cpp
|
||||
// Create Gameplay Effect that reduces Mana
|
||||
// Assign to Ability > Cost Gameplay Effect Class
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Get Current Attribute Value
|
||||
|
||||
```cpp
|
||||
float CurrentHealth = AbilitySystemComponent->GetNumericAttribute(UMyAttributeSet::GetHealthAttribute());
|
||||
```
|
||||
|
||||
### Listen for Attribute Changes
|
||||
|
||||
```cpp
|
||||
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UMyAttributeSet::GetHealthAttribute())
|
||||
.AddUObject(this, &AMyCharacter::OnHealthChanged);
|
||||
|
||||
void AMyCharacter::OnHealthChanged(const FOnAttributeChangeData& Data) {
|
||||
UE_LOG(LogTemp, Warning, TEXT("Health: %f"), Data.NewValue);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/gameplay-ability-system-for-unreal-engine/
|
||||
- https://github.com/tranek/GASDocumentation (community guide)
|
||||
321
docs/engine-reference/unreal/plugins/gameplay-camera-system.md
Normal file
321
docs/engine-reference/unreal/plugins/gameplay-camera-system.md
Normal file
@@ -0,0 +1,321 @@
|
||||
# Unreal Engine 5.7 — Gameplay Camera System
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** ⚠️ Experimental (introduced in UE 5.5)
|
||||
**Plugin:** `GameplayCameras` (built-in, enable in Plugins)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Gameplay Camera System** is a modular camera management framework introduced in UE 5.5.
|
||||
It replaces traditional camera setups with a flexible, node-based system that handles
|
||||
camera modes, blending, and context-aware camera behavior.
|
||||
|
||||
**Use Gameplay Cameras for:**
|
||||
- Dynamic camera behavior (3rd person, aiming, vehicles, cinematic)
|
||||
- Context-aware camera switching (combat, exploration, dialogue)
|
||||
- Smooth camera blending between modes
|
||||
- Procedural camera motion (camera shake, lag, offset)
|
||||
|
||||
**⚠️ Warning:** This plugin is experimental in UE 5.5-5.7. Expect API changes in future versions.
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **Camera Rig**
|
||||
- Defines camera configuration (position, rotation, FOV, etc.)
|
||||
- Modular node graph (similar to Material Editor)
|
||||
|
||||
### 2. **Camera Director**
|
||||
- Manages which camera rig is active
|
||||
- Handles blending between camera rigs
|
||||
|
||||
### 3. **Camera Nodes**
|
||||
- Building blocks for camera behavior:
|
||||
- **Position Nodes**: Orbit, Follow, Fixed Position
|
||||
- **Rotation Nodes**: Look At, Match Actor Rotation
|
||||
- **Modifiers**: Camera Shake, Lag, Offset
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Enable Plugin
|
||||
|
||||
`Edit > Plugins > Gameplay Cameras > Enabled > Restart`
|
||||
|
||||
### 2. Add Camera Component
|
||||
|
||||
```cpp
|
||||
#include "GameplayCameras/Public/GameplayCameraComponent.h"
|
||||
|
||||
UCLASS()
|
||||
class AMyCharacter : public ACharacter {
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
AMyCharacter() {
|
||||
// Create camera component
|
||||
CameraComponent = CreateDefaultSubobject<UGameplayCameraComponent>(TEXT("GameplayCamera"));
|
||||
CameraComponent->SetupAttachment(RootComponent);
|
||||
}
|
||||
|
||||
protected:
|
||||
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Camera")
|
||||
TObjectPtr<UGameplayCameraComponent> CameraComponent;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Create Camera Rig
|
||||
|
||||
### 1. Create Camera Rig Asset
|
||||
|
||||
1. Content Browser > Gameplay > Gameplay Camera Rig
|
||||
2. Open Camera Rig Editor (node-based graph)
|
||||
|
||||
### 2. Build Camera Rig (Example: Third Person)
|
||||
|
||||
**Node Setup:**
|
||||
```
|
||||
Actor Position (Character)
|
||||
↓
|
||||
Orbit Node (Orbit around character)
|
||||
↓
|
||||
Offset Node (Shoulder offset)
|
||||
↓
|
||||
Look At Node (Look at character)
|
||||
↓
|
||||
Camera Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Camera Nodes
|
||||
|
||||
### Position Nodes
|
||||
|
||||
#### Orbit Node (Third Person)
|
||||
- Orbits around target actor
|
||||
- Configure:
|
||||
- **Orbit Distance**: Distance from target (e.g., 300 units)
|
||||
- **Pitch Range**: Min/Max pitch angles
|
||||
- **Yaw Range**: Min/Max yaw angles
|
||||
|
||||
#### Follow Node (Smooth Follow)
|
||||
- Follows target with lag
|
||||
- Configure:
|
||||
- **Lag Speed**: How quickly camera catches up
|
||||
- **Offset**: Fixed offset from target
|
||||
|
||||
#### Fixed Position Node
|
||||
- Static camera position in world space
|
||||
|
||||
---
|
||||
|
||||
### Rotation Nodes
|
||||
|
||||
#### Look At Node
|
||||
- Points camera at target
|
||||
- Configure:
|
||||
- **Target**: Actor or component to look at
|
||||
- **Offset**: Look-at offset (e.g., aim at head instead of feet)
|
||||
|
||||
#### Match Actor Rotation
|
||||
- Matches target actor's rotation
|
||||
- Useful for first-person or vehicle cameras
|
||||
|
||||
---
|
||||
|
||||
### Modifier Nodes
|
||||
|
||||
#### Camera Shake
|
||||
- Adds procedural shake (e.g., footsteps, explosions)
|
||||
- Configure:
|
||||
- **Shake Pattern**: Perlin noise, sine wave, custom
|
||||
- **Amplitude**: Shake strength
|
||||
|
||||
#### Camera Lag
|
||||
- Smooth dampening of camera movement
|
||||
- Configure:
|
||||
- **Lag Speed**: Damping factor (0 = instant, higher = more lag)
|
||||
|
||||
#### Offset Node
|
||||
- Static offset from calculated position
|
||||
- Useful for shoulder camera offset
|
||||
|
||||
---
|
||||
|
||||
## Camera Director (Switching Between Rigs)
|
||||
|
||||
### Assign Camera Rig
|
||||
|
||||
```cpp
|
||||
#include "GameplayCameras/Public/GameplayCameraComponent.h"
|
||||
|
||||
void AMyCharacter::SetCameraMode(UGameplayCameraRig* NewRig) {
|
||||
if (CameraComponent) {
|
||||
CameraComponent->SetCameraRig(NewRig);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Blend Between Camera Rigs
|
||||
|
||||
```cpp
|
||||
// Blend to aiming camera over 0.5 seconds
|
||||
CameraComponent->BlendToCameraRig(AimingCameraRig, 0.5f);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Example: Third Person + Aiming
|
||||
|
||||
### 1. Create Two Camera Rigs
|
||||
|
||||
**Third Person Rig:**
|
||||
```
|
||||
Actor Position → Orbit (distance: 300) → Look At → Output
|
||||
```
|
||||
|
||||
**Aiming Rig:**
|
||||
```
|
||||
Actor Position → Orbit (distance: 150) → Offset (shoulder) → Look At → Output
|
||||
```
|
||||
|
||||
### 2. Switch on Aim
|
||||
|
||||
```cpp
|
||||
UPROPERTY(EditAnywhere, Category = "Camera")
|
||||
TObjectPtr<UGameplayCameraRig> ThirdPersonRig;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Camera")
|
||||
TObjectPtr<UGameplayCameraRig> AimingRig;
|
||||
|
||||
void StartAiming() {
|
||||
CameraComponent->BlendToCameraRig(AimingRig, 0.3f); // Blend over 0.3s
|
||||
}
|
||||
|
||||
void StopAiming() {
|
||||
CameraComponent->BlendToCameraRig(ThirdPersonRig, 0.3f);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Over-the-Shoulder Camera
|
||||
|
||||
```
|
||||
Actor Position
|
||||
↓
|
||||
Orbit Node (distance: 250, yaw offset: 30°)
|
||||
↓
|
||||
Offset Node (X: 0, Y: 50, Z: 50) // Shoulder offset
|
||||
↓
|
||||
Look At Node (target: Character head)
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Vehicle Camera
|
||||
|
||||
```
|
||||
Vehicle Position
|
||||
↓
|
||||
Follow Node (lag: 0.2)
|
||||
↓
|
||||
Offset Node (behind vehicle: X: -400, Z: 150)
|
||||
↓
|
||||
Look At Node (target: Vehicle)
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### First Person Camera
|
||||
|
||||
```
|
||||
Character Head Socket
|
||||
↓
|
||||
Match Actor Rotation
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Camera Shake
|
||||
|
||||
### Trigger Camera Shake
|
||||
|
||||
```cpp
|
||||
#include "GameplayCameras/Public/GameplayCameraShake.h"
|
||||
|
||||
void TriggerExplosionShake() {
|
||||
if (APlayerController* PC = GetWorld()->GetFirstPlayerController()) {
|
||||
if (UGameplayCameraComponent* CameraComp = PC->FindComponentByClass<UGameplayCameraComponent>()) {
|
||||
CameraComp->PlayCameraShake(ExplosionShakeClass, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Tips
|
||||
|
||||
- Limit camera shake frequency (don't trigger every frame)
|
||||
- Use camera lag sparingly (expensive for high lag values)
|
||||
- Cache camera rig references (don't search every frame)
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Camera Debug Visualization
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// GameplayCameras.Debug 1 - Show active camera rig info
|
||||
// showdebug camera - Show camera debug info
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration from Legacy Cameras
|
||||
|
||||
### Old Spring Arm + Camera Component
|
||||
|
||||
```cpp
|
||||
// ❌ OLD: Spring Arm Component
|
||||
USpringArmComponent* SpringArm;
|
||||
UCameraComponent* Camera;
|
||||
|
||||
// ✅ NEW: Gameplay Camera Component
|
||||
UGameplayCameraComponent* CameraComponent;
|
||||
// Build orbit + look-at rig in Camera Rig asset
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Limitations (Experimental Status)
|
||||
|
||||
- **API Instability**: Expect breaking changes in UE 5.8+
|
||||
- **Limited Documentation**: Official docs still evolving
|
||||
- **Blueprint Support**: Primarily C++ focused (Blueprint support improving)
|
||||
- **Production Risk**: Test thoroughly before shipping
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/gameplay-cameras-in-unreal-engine/
|
||||
- UE 5.5+ Release Notes
|
||||
- **Note:** This system is experimental. Always check latest official docs for API changes.
|
||||
356
docs/engine-reference/unreal/plugins/pcg.md
Normal file
356
docs/engine-reference/unreal/plugins/pcg.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# Unreal Engine 5.7 — PCG (Procedural Content Generation)
|
||||
|
||||
**Last verified:** 2026-02-13
|
||||
**Status:** Production-Ready (as of UE 5.7)
|
||||
**Plugin:** `PCG` (built-in, enable in Plugins)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
**Procedural Content Generation (PCG)** is Unreal's node-based framework for generating
|
||||
procedural content at massive scale. It's designed for populating large open worlds with
|
||||
foliage, rocks, props, buildings, and other environmental detail.
|
||||
|
||||
**Use PCG for:**
|
||||
- Procedural foliage placement (trees, grass, rocks)
|
||||
- Biome-based environment generation
|
||||
- Road/path generation
|
||||
- Building/structure placement
|
||||
- World detail population (props, clutter)
|
||||
|
||||
**DON'T use PCG for:**
|
||||
- Gameplay logic (use Blueprints/C++)
|
||||
- One-off manual placement (use editor tools)
|
||||
|
||||
**⚠️ Note:** PCG was experimental in UE 5.0-5.6, became production-ready in UE 5.7.
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### 1. **PCG Graph**
|
||||
- Node-based graph (similar to Material Editor)
|
||||
- Defines generation rules
|
||||
|
||||
### 2. **PCG Component**
|
||||
- Placed in level, executes PCG Graph
|
||||
- Generates content in defined volume
|
||||
|
||||
### 3. **PCG Data**
|
||||
- Point data (positions, rotations, scales)
|
||||
- Spline data (paths, roads, rivers)
|
||||
- Volume data (density, biome masks)
|
||||
|
||||
### 4. **Nodes**
|
||||
- **Samplers**: Generate points (Grid, Poisson, Surface)
|
||||
- **Filters**: Remove points based on rules (Density, Tag, Bounds)
|
||||
- **Modifiers**: Transform points (Offset, Rotate, Scale)
|
||||
- **Spawners**: Instantiate meshes/actors at points
|
||||
|
||||
---
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Enable Plugin
|
||||
|
||||
`Edit > Plugins > PCG > Enabled > Restart`
|
||||
|
||||
### 2. Create PCG Volume
|
||||
|
||||
1. Place Actors > Volumes > PCG Volume
|
||||
2. Scale volume to desired generation area
|
||||
|
||||
### 3. Create PCG Graph
|
||||
|
||||
1. Content Browser > PCG > PCG Graph
|
||||
2. Open PCG Graph Editor
|
||||
|
||||
---
|
||||
|
||||
## Basic Workflow
|
||||
|
||||
### Example: Forest Generation
|
||||
|
||||
#### 1. Create PCG Graph
|
||||
|
||||
**Node Setup:**
|
||||
```
|
||||
Input (Volume)
|
||||
↓
|
||||
Surface Sampler (sample volume surface, points per m²: 0.5)
|
||||
↓
|
||||
Density Filter (use texture mask or noise)
|
||||
↓
|
||||
Static Mesh Spawner (tree meshes)
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
#### 2. Assign Graph to Volume
|
||||
|
||||
1. Select PCG Volume
|
||||
2. Details Panel > PCG Component > Graph = Your PCG Graph
|
||||
3. Click "Generate" button
|
||||
|
||||
---
|
||||
|
||||
## Key Node Types
|
||||
|
||||
### Samplers (Point Generation)
|
||||
|
||||
#### Grid Sampler
|
||||
- Regular grid of points
|
||||
- Configure:
|
||||
- **Grid Size**: Distance between points
|
||||
- **Offset**: Random offset per point
|
||||
|
||||
#### Poisson Disk Sampler
|
||||
- Random points with minimum distance
|
||||
- Configure:
|
||||
- **Points Per m²**: Density
|
||||
- **Min Distance**: Spacing between points
|
||||
|
||||
#### Surface Sampler
|
||||
- Points on mesh surfaces or landscape
|
||||
- Configure:
|
||||
- **Points Per m²**: Density
|
||||
- **Surface Only**: Only surface, not volume
|
||||
|
||||
---
|
||||
|
||||
### Filters (Point Removal)
|
||||
|
||||
#### Density Filter
|
||||
- Remove points based on density value
|
||||
- Input: Texture or noise
|
||||
- Use for: Biome masks, clearings, paths
|
||||
|
||||
#### Tag Filter
|
||||
- Filter points by tag
|
||||
- Use for: Conditional spawning
|
||||
|
||||
#### Bounds Filter
|
||||
- Keep only points within bounds
|
||||
- Use for: Limiting generation to specific areas
|
||||
|
||||
---
|
||||
|
||||
### Modifiers (Point Transformation)
|
||||
|
||||
#### Rotate
|
||||
- Randomize point rotation
|
||||
- Configure:
|
||||
- **Min/Max Rotation**: Rotation range per axis
|
||||
|
||||
#### Scale
|
||||
- Randomize point scale
|
||||
- Configure:
|
||||
- **Min/Max Scale**: Scale range
|
||||
|
||||
#### Project to Ground
|
||||
- Snap points to landscape surface
|
||||
|
||||
---
|
||||
|
||||
### Spawners (Mesh/Actor Instantiation)
|
||||
|
||||
#### Static Mesh Spawner
|
||||
- Spawn static meshes at points
|
||||
- Configure:
|
||||
- **Mesh List**: Array of meshes (random selection)
|
||||
- **Culling Distance**: LOD/culling settings
|
||||
|
||||
#### Actor Spawner
|
||||
- Spawn Blueprint actors at points
|
||||
- Use for: Gameplay actors, interactive objects
|
||||
|
||||
---
|
||||
|
||||
## Data Sources
|
||||
|
||||
### Landscape
|
||||
- Use landscape as input for sampling
|
||||
- Automatically projects to landscape height
|
||||
|
||||
### Splines
|
||||
- Generate content along splines (roads, rivers, paths)
|
||||
- Example: Trees along path
|
||||
|
||||
### Textures
|
||||
- Use textures as density masks
|
||||
- Paint biomes, clearings, areas
|
||||
|
||||
---
|
||||
|
||||
## Biome Example (Mixed Forest)
|
||||
|
||||
### Graph Setup
|
||||
|
||||
```
|
||||
Input (Landscape)
|
||||
↓
|
||||
Surface Sampler (density: 1.0)
|
||||
↓
|
||||
┌─────────────────┬─────────────────┐
|
||||
│ Tree Biome │ Rock Biome │
|
||||
│ (density > 0.5) │ (density < 0.5) │
|
||||
├─────────────────┼─────────────────┤
|
||||
│ Tree Spawner │ Rock Spawner │
|
||||
└─────────────────┴─────────────────┘
|
||||
↓
|
||||
Merge
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Spline-Based Generation (Road with Trees)
|
||||
|
||||
### 1. Create PCG Graph
|
||||
|
||||
```
|
||||
Spline Input
|
||||
↓
|
||||
Spline Sampler (sample along spline)
|
||||
↓
|
||||
Offset (offset from spline path)
|
||||
↓
|
||||
Tree Spawner
|
||||
↓
|
||||
Output
|
||||
```
|
||||
|
||||
### 2. Add Spline Component to PCG Volume
|
||||
|
||||
1. PCG Volume > Add Component > Spline
|
||||
2. Draw spline path
|
||||
3. PCG Graph reads spline data
|
||||
|
||||
---
|
||||
|
||||
## Runtime Generation
|
||||
|
||||
### Trigger Generation from C++
|
||||
|
||||
```cpp
|
||||
#include "PCGComponent.h"
|
||||
|
||||
UPCGComponent* PCGComp = /* Get PCG Component */;
|
||||
PCGComp->Generate(); // Execute PCG graph
|
||||
```
|
||||
|
||||
### Stream Generation (Large Worlds)
|
||||
|
||||
- PCG automatically streams with World Partition
|
||||
- Only generates content in loaded cells
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
|
||||
### Optimization Tips
|
||||
|
||||
- Use **culling distance** on spawned meshes (LOD)
|
||||
- Limit **density** (fewer points = better performance)
|
||||
- Use **Hierarchical Instanced Static Meshes (HISM)** for repeated meshes
|
||||
- Enable **streaming** for large worlds
|
||||
|
||||
### Debug Performance
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// pcg.graph.debug 1 - Show PCG debug info
|
||||
// stat pcg - Show PCG performance stats
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Forest with Clearings
|
||||
|
||||
```
|
||||
Surface Sampler
|
||||
↓
|
||||
Density Filter (noise texture with clearings)
|
||||
↓
|
||||
Tree Spawner (pine, oak, birch)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Rocks on Steep Slopes
|
||||
|
||||
```
|
||||
Landscape Input
|
||||
↓
|
||||
Surface Sampler
|
||||
↓
|
||||
Slope Filter (angle > 30°)
|
||||
↓
|
||||
Rock Spawner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Props Along Road
|
||||
|
||||
```
|
||||
Spline Input (road spline)
|
||||
↓
|
||||
Spline Sampler
|
||||
↓
|
||||
Offset (side of road)
|
||||
↓
|
||||
Street Light Spawner
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### PCG Debug Visualization
|
||||
|
||||
```cpp
|
||||
// Console commands:
|
||||
// pcg.debug.display 1 - Show points and generation bounds
|
||||
// pcg.debug.colormode points - Color-code points
|
||||
```
|
||||
|
||||
### Graph Debugging
|
||||
|
||||
- PCG Graph Editor > Debug > Show Debug Points
|
||||
- Visualize points at each node in the graph
|
||||
|
||||
---
|
||||
|
||||
## Migration from UE 5.6 (Experimental) to 5.7 (Production)
|
||||
|
||||
### API Changes
|
||||
|
||||
```cpp
|
||||
// ❌ OLD (5.6 experimental API):
|
||||
// Some nodes renamed, API unstable
|
||||
|
||||
// ✅ NEW (5.7 production API):
|
||||
// Stable node types, documented API
|
||||
```
|
||||
|
||||
**Migration:** Rebuild PCG graphs using stable 5.7 nodes. Test thoroughly.
|
||||
|
||||
---
|
||||
|
||||
## Limitations
|
||||
|
||||
- **Not for gameplay logic**: Use Blueprints/C++ for game rules
|
||||
- **Large graphs can be slow**: Optimize with filters and density reduction
|
||||
- **Runtime generation overhead**: Pre-generate when possible
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
- https://docs.unrealengine.com/5.7/en-US/procedural-content-generation-in-unreal-engine/
|
||||
- https://docs.unrealengine.com/5.7/en-US/pcg-quick-start-in-unreal-engine/
|
||||
- UE 5.7 Release Notes (PCG Production-Ready announcement)
|
||||
357
docs/examples/README.md
Normal file
357
docs/examples/README.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# Collaborative Session Examples
|
||||
|
||||
This directory contains realistic, end-to-end session transcripts showing how the Game Studio Agent Architecture works in practice. Each example demonstrates the **collaborative workflow** where agents ask questions, present options, and wait for user approval rather than autonomously generating content.
|
||||
|
||||
---
|
||||
|
||||
## Visual Reference
|
||||
|
||||
**New to the system? Start here:**
|
||||
[Skill Flow Diagrams](skill-flow-diagrams.md) — visual maps of all 7 phases and how skills chain together.
|
||||
|
||||
---
|
||||
|
||||
## 📚 **Available Examples**
|
||||
|
||||
### CORE WORKFLOW
|
||||
|
||||
### [Skill Flow Diagrams](skill-flow-diagrams.md)
|
||||
**Type:** Visual Reference
|
||||
**Complexity:** All levels
|
||||
|
||||
Full pipeline overview (zero to ship), plus detailed chain diagrams for:
|
||||
design-system, story lifecycle, UX pipeline, and brownfield onboarding.
|
||||
**Start here if you want to understand how the pieces fit together.**
|
||||
|
||||
---
|
||||
|
||||
### [Session: Authoring a GDD with /design-system](session-design-system-skill.md)
|
||||
**Type:** Design (skill-driven)
|
||||
**Skill:** `/design-system`
|
||||
**Duration:** ~60 minutes (14 turns)
|
||||
**Complexity:** Medium
|
||||
|
||||
**Scenario:**
|
||||
Dev runs `/design-system movement` after `/map-systems` produced the systems index. The skill loads context from the game concept and dependency GDDs, runs a technical feasibility pre-check, then guides through all 8 GDD sections one at a time — drafting, approving, and writing each section to disk before moving to the next.
|
||||
|
||||
**Key Moments:**
|
||||
- Technical feasibility pre-check flags Jolt physics default change (Godot 4.6)
|
||||
- Incremental writing: each section on disk immediately after approval
|
||||
- Session crash during section 5 → agent resumes from first empty section
|
||||
- Dependency signals (stamina, inventory) surfaced during the Dependencies section
|
||||
- Ends with explicit handoff: "run `/design-review` before the next system"
|
||||
|
||||
**Learn:**
|
||||
- How `/design-system` is different from asking an agent to "write a GDD"
|
||||
- How the section-by-section cycle prevents 30k-token context bloat
|
||||
- How incremental file writing survives session crashes
|
||||
- How the skill surfaces downstream dependency contracts
|
||||
|
||||
---
|
||||
|
||||
### [Session: Full Story Lifecycle](session-story-lifecycle.md)
|
||||
**Type:** Full Workflow
|
||||
**Skills:** `/story-readiness` → implementation → `/story-done`
|
||||
**Duration:** ~50 minutes (13 turns)
|
||||
**Complexity:** Medium
|
||||
|
||||
**Scenario:**
|
||||
Dev picks up a story from the sprint backlog. `/story-readiness` catches a roll-direction ambiguity before any code is written. After implementation, `/story-done` verifies 9 acceptance criteria, identifies 2 deferred criteria (inventory not integrated yet), and closes the story with notes.
|
||||
|
||||
**Key Moments:**
|
||||
- `/story-readiness` catches spec ambiguity in Turn 2 — resolved before implementation starts
|
||||
- ADR status check: story would be BLOCKED if ADR was still Proposed
|
||||
- Manifest version check: confirms story's guidance hasn't drifted from current architecture
|
||||
- Deferred criteria tracked (not lost) when integration not yet possible
|
||||
- `sprint-status.yaml` updated at story close, next ready story surfaced automatically
|
||||
|
||||
**Learn:**
|
||||
- Why `/story-readiness` prevents late-implementation ambiguity
|
||||
- How deferred criteria work (COMPLETE WITH NOTES vs. BLOCKED)
|
||||
- How TR-ID references prevent false deviation flags
|
||||
- The full loop from backlog → implemented → closed
|
||||
|
||||
---
|
||||
|
||||
### [Session: Gate Check and Phase Transition](session-gate-check-phase-transition.md)
|
||||
**Type:** Phase Gate
|
||||
**Skill:** `/gate-check`
|
||||
**Duration:** ~20 minutes (7 turns)
|
||||
**Complexity:** Low
|
||||
|
||||
**Scenario:**
|
||||
Dev completes the Systems Design phase and runs `/gate-check` to advance. The gate finds all 6 MVP GDDs complete, cross-review passed with one low-severity concern. Gate passes, `stage.txt` updated, and the agent provides a specific ordered checklist for Technical Setup.
|
||||
|
||||
**Key Moments:**
|
||||
- Gate validates artifact presence AND internal completeness (8 sections per GDD)
|
||||
- CONCERNS ≠ FAIL: low-severity cross-review note passes the gate
|
||||
- stage.txt update changes what `/help`, `/sprint-status`, and all skills see going forward
|
||||
- Agent surfaces the cross-review concern as a concrete ADR to write next
|
||||
- Next phase checklist is specific and ordered, not generic
|
||||
|
||||
**Learn:**
|
||||
- What a gate check actually validates (not just "do files exist?")
|
||||
- How PASS/CONCERNS/FAIL verdicts work
|
||||
- Why stage.txt is the authority for phase tracking
|
||||
- What changes after a phase transition
|
||||
|
||||
---
|
||||
|
||||
### [Session: UX Pipeline — /ux-design → /ux-review → /team-ui](session-ux-pipeline.md)
|
||||
**Type:** UX Design Pipeline
|
||||
**Skills:** `/ux-design`, `/ux-review`, `/team-ui`
|
||||
**Duration:** ~90 minutes (16 turns)
|
||||
**Complexity:** Medium-High
|
||||
|
||||
**Scenario:**
|
||||
Dev designs the HUD and inventory screen. `/ux-design` reads the player journey and GDDs to ground decisions in player emotional state. `/ux-review` catches a blocking accessibility gap (no keyboard alternative to drag-drop) and an advisory colorblind issue. After fixes, `/team-ui` accepts the handoff.
|
||||
|
||||
**Key Moments:**
|
||||
- HUD philosophy choice (diegetic vs. persistent vs. tactical) grounded in survival genre conventions
|
||||
- `/ux-review` distinguishes BLOCKING (stops handoff) vs. ADVISORY (can fix in visual pass)
|
||||
- Accessibility caught before implementation, not during QA
|
||||
- Keyboard alternative added in one turn; review re-runs and passes
|
||||
- `/team-ui` checks for a passing `/ux-review` before starting visual design
|
||||
|
||||
**Learn:**
|
||||
- How `/ux-design` uses player journey context to ground UI decisions
|
||||
- What `/ux-review` actually checks (not just "does a spec exist?")
|
||||
- The difference between HUD doc (`design/ux/hud.md`) and per-screen specs
|
||||
- How accessibility issues are handled at design time vs. implementation time
|
||||
|
||||
---
|
||||
|
||||
### [Session: Brownfield Onboarding with /adopt](session-adopt-brownfield.md)
|
||||
**Type:** Brownfield Adoption
|
||||
**Skill:** `/adopt`
|
||||
**Duration:** ~30 minutes (8 turns)
|
||||
**Complexity:** Low-Medium
|
||||
|
||||
**Scenario:**
|
||||
Dev has 3 months of existing code and rough design notes but nothing in the right format. `/adopt` audits format compliance (not just file existence), classifies 4 gaps by severity, builds an ordered 7-step migration plan, and immediately fixes the BLOCKING gap (missing systems index) by inferring it from the codebase.
|
||||
|
||||
**Key Moments:**
|
||||
- FORMAT audit distinguishes "file exists" from "file has required internal structure"
|
||||
- BLOCKING gap identified: missing systems index prevents 4+ skills from running
|
||||
- Migration plan is ordered: blocking gaps first, then high, then medium
|
||||
- Systems index bootstrapped from code structure — brownfield code contains the answer
|
||||
- Retrofit mode vs. new authoring: `/design-system retrofit` fills gaps without overwriting
|
||||
|
||||
**Learn:**
|
||||
- The difference between `/adopt` and `/project-stage-detect`
|
||||
- How format compliance is checked (section detection, not just file presence)
|
||||
- How brownfield projects can onboard without losing existing work
|
||||
- When to use retrofit mode vs. full authoring
|
||||
|
||||
---
|
||||
|
||||
### FOUNDATIONAL EXAMPLES
|
||||
|
||||
### [Session: Designing the Crafting System](session-design-crafting-system.md)
|
||||
**Type:** Design
|
||||
**Agent:** game-designer
|
||||
**Duration:** ~45 minutes (12 turns)
|
||||
**Complexity:** Medium
|
||||
|
||||
**Scenario:**
|
||||
Solo dev needs to design a crafting system that serves Pillar 2 ("Emergent Discovery Through Experimentation"). The agent guides them through question/answer, presents 3 design options with game theory analysis, incorporates user modifications, and iteratively drafts the GDD with approval at each step.
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Agent asks 5 clarifying questions upfront
|
||||
- Presents 3 distinct options with pros/cons + MDA alignment
|
||||
- User modifies recommended option, agent incorporates immediately
|
||||
- Edge case flagged proactively ("what if non-recipe combo?")
|
||||
- Each GDD section shown for approval before moving to next
|
||||
- Explicit "May I write to [file]?" before creating file
|
||||
|
||||
**Learn:**
|
||||
- How design agents ask about goals, constraints, references
|
||||
- How to present options using game design theory (MDA, SDT, Bartle)
|
||||
- How to iterate on drafts section-by-section
|
||||
- When to delegate to specialists (systems-designer, economy-designer)
|
||||
|
||||
---
|
||||
|
||||
### [Session: Implementing Combat Damage Calculation](session-implement-combat-damage.md)
|
||||
**Type:** Implementation
|
||||
**Agent:** gameplay-programmer
|
||||
**Duration:** ~30 minutes (10 turns)
|
||||
**Complexity:** Low-Medium
|
||||
|
||||
**Scenario:**
|
||||
User has a complete design doc and wants the damage calculation implemented. Agent reads the spec, identifies 7 ambiguities/gaps, asks clarifying questions, proposes architecture for approval, implements with rule enforcement, and proactively writes tests.
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Agent reads design doc first, identifies 7 spec ambiguities
|
||||
- Architecture proposed with code samples BEFORE implementation
|
||||
- User requests type safety, agent refines and re-proposes
|
||||
- Rules catch issues (hardcoded values), agent fixes transparently
|
||||
- Tests written proactively following verification-driven development
|
||||
- Agent offers options for next steps rather than assuming
|
||||
|
||||
**Learn:**
|
||||
- How implementation agents clarify specs before coding
|
||||
- How to propose architecture with code samples for approval
|
||||
- How rules enforce standards automatically
|
||||
- How to handle spec gaps (ask, don't assume)
|
||||
- Verification-driven development (tests prove it works)
|
||||
|
||||
---
|
||||
|
||||
### [Session: Scope Crisis - Strategic Decision Making](session-scope-crisis-decision.md)
|
||||
**Type:** Strategic Decision
|
||||
**Agent:** creative-director
|
||||
**Duration:** ~25 minutes (8 turns)
|
||||
**Complexity:** High
|
||||
|
||||
**Scenario:**
|
||||
Solo dev faces crisis: Alpha milestone in 2 weeks, crafting system needs 3 weeks, investor demo is make-or-break. Creative director gathers context, frames the decision, presents 3 strategic options with honest trade-off analysis, makes recommendation but defers to user, then documents decision with ADR and demo script.
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Agent reads context docs before proposing solutions
|
||||
- Asks 5 questions to understand decision constraints
|
||||
- Frames decision properly (what's at stake, evaluation criteria)
|
||||
- Presents 3 options with risk analysis and historical precedent
|
||||
- Makes strong recommendation but explicitly: "this is your call"
|
||||
- Documents decision + provides demo script to support user
|
||||
|
||||
**Learn:**
|
||||
- How leadership agents frame strategic decisions
|
||||
- How to present options with trade-off analysis
|
||||
- How to use game dev precedent and theory in recommendations
|
||||
- How to document decisions (ADRs)
|
||||
- How to cascade decisions to affected departments
|
||||
|
||||
---
|
||||
|
||||
### [Reverse Documentation Workflow](reverse-document-workflow-example.md)
|
||||
**Type:** Brownfield Documentation
|
||||
**Agent:** game-designer
|
||||
**Duration:** ~20 minutes
|
||||
**Complexity:** Low
|
||||
|
||||
**Scenario:**
|
||||
Developer built a skill tree system but never wrote a design doc. Agent reads the code, infers the design intent, asks clarifying questions about ambiguous decisions, and produces a retroactive GDD.
|
||||
|
||||
---
|
||||
|
||||
## 🎯 **What These Examples Demonstrate**
|
||||
|
||||
All examples follow the **collaborative workflow pattern:**
|
||||
|
||||
```
|
||||
Question → Options → Decision → Draft → Approval
|
||||
```
|
||||
|
||||
> **Note:** These examples show the collaborative pattern as conversational text.
|
||||
> In practice, agents now use the `AskUserQuestion` tool at decision points to
|
||||
> present structured option pickers (with labels, descriptions, and multi-select).
|
||||
> The pattern is **Explain → Capture**: agents explain their analysis in
|
||||
> conversation first, then present a structured UI picker for the user's decision.
|
||||
|
||||
### ✅ **Collaborative Behaviors Shown:**
|
||||
|
||||
1. **Agents Ask Before Assuming**
|
||||
- Design agents ask about goals, constraints, references
|
||||
- Implementation agents clarify spec ambiguities
|
||||
- Leadership agents gather full context before recommending
|
||||
|
||||
2. **Agents Present Options, Not Dictates**
|
||||
- 2-4 options with pros/cons
|
||||
- Reasoning based on theory, precedent, project pillars
|
||||
- Recommendation made, but user decides
|
||||
|
||||
3. **Agents Show Work Before Finalizing**
|
||||
- Design drafts shown section-by-section
|
||||
- Architecture proposals shown before implementation
|
||||
- Strategic analysis presented before decisions
|
||||
|
||||
4. **Agents Get Approval Before Writing Files**
|
||||
- Explicit "May I write to [file]?" before using Write/Edit tools
|
||||
- Multi-file changes list all affected files first
|
||||
- User says "Yes" before any file is created
|
||||
|
||||
5. **Agents Iterate on Feedback**
|
||||
- User modifications incorporated immediately
|
||||
- No defensiveness when user changes recommendations
|
||||
- Celebrate when user improves agent's suggestion
|
||||
|
||||
---
|
||||
|
||||
## 📖 **How to Use These Examples**
|
||||
|
||||
### For New Users:
|
||||
Read these examples BEFORE your first session. They show realistic expectations for how agents work:
|
||||
- Agents are consultants, not autonomous executors
|
||||
- You make all creative/strategic decisions
|
||||
- Agents provide expert guidance and options
|
||||
|
||||
### For Understanding Specific Workflows:
|
||||
- **New to the system?** → Read skill-flow-diagrams.md first
|
||||
- **Running /design-system for the first time?** → Read session-design-system-skill.md
|
||||
- **Picking up a story?** → Read session-story-lifecycle.md
|
||||
- **Finishing a phase?** → Read session-gate-check-phase-transition.md
|
||||
- **Starting UI work?** → Read session-ux-pipeline.md
|
||||
- **Have an existing project?** → Read session-adopt-brownfield.md
|
||||
- **Designing a system (agent-driven)?** → Read session-design-crafting-system.md
|
||||
- **Implementing code?** → Read session-implement-combat-damage.md
|
||||
- **Making strategic decisions?** → Read session-scope-crisis-decision.md
|
||||
|
||||
### For Training:
|
||||
If you're teaching someone to use this system, walk through one example turn-by-turn to show:
|
||||
- What good questions look like
|
||||
- How to evaluate presented options
|
||||
- When to approve vs. request changes
|
||||
- How to maintain creative control while leveraging AI expertise
|
||||
|
||||
---
|
||||
|
||||
## 🔍 **Common Patterns Across All Examples**
|
||||
|
||||
### Turn 1-2: **Understand Before Acting**
|
||||
- Agent reads context (design docs, specs, constraints)
|
||||
- Agent asks clarifying questions
|
||||
- No assumptions or guesses
|
||||
|
||||
### Turn 3-5: **Present Options with Reasoning**
|
||||
- 2-4 distinct approaches
|
||||
- Pros/cons for each
|
||||
- Theory/precedent supporting the analysis
|
||||
- Recommendation made, decision deferred to user
|
||||
|
||||
### Turn 6-8: **Iterate on Drafts**
|
||||
- Show work incrementally
|
||||
- Incorporate feedback immediately
|
||||
- Flag edge cases or ambiguities proactively
|
||||
|
||||
### Turn 9-10: **Approval and Completion**
|
||||
- "May I write to [file]?"
|
||||
- User: "Yes"
|
||||
- Agent writes files
|
||||
- Agent offers next steps (tests, review, integration)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 **Try It Yourself**
|
||||
|
||||
After reading these examples, try this exercise:
|
||||
|
||||
1. Pick one of your game systems (combat, inventory, progression, etc.)
|
||||
2. Ask the relevant agent to design or implement it
|
||||
3. Notice if the agent:
|
||||
- ✅ Asks clarifying questions upfront
|
||||
- ✅ Presents options with reasoning
|
||||
- ✅ Shows drafts before finalizing
|
||||
- ✅ Requests approval before writing files
|
||||
|
||||
If the agent skips any of these, remind it:
|
||||
> "Please follow the collaborative protocol from docs/COLLABORATIVE-DESIGN-PRINCIPLE.md"
|
||||
|
||||
---
|
||||
|
||||
## 📝 **Additional Resources**
|
||||
|
||||
- **Full Principle Documentation:** [docs/COLLABORATIVE-DESIGN-PRINCIPLE.md](../COLLABORATIVE-DESIGN-PRINCIPLE.md)
|
||||
- **Workflow Guide:** [docs/WORKFLOW-GUIDE.md](../WORKFLOW-GUIDE.md)
|
||||
- **Agent Roster:** [.claude/docs/agent-roster.md](../../.claude/docs/agent-roster.md)
|
||||
- **CLAUDE.md (Collaboration Protocol):** [CLAUDE.md](../../CLAUDE.md#collaboration-protocol)
|
||||
121
docs/examples/reverse-document-workflow-example.md
Normal file
121
docs/examples/reverse-document-workflow-example.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# Reverse Documentation Workflow Example
|
||||
|
||||
## Scenario
|
||||
Developer built a working skill tree system but never wrote a design doc.
|
||||
Code exists in `src/gameplay/skills/`, but `design/gdd/` has no skill-system.md.
|
||||
|
||||
---
|
||||
|
||||
## Session Transcript
|
||||
|
||||
**User:**
|
||||
```
|
||||
I have a working skill tree system in src/gameplay/skills/ with about 1200 lines
|
||||
of code. I never wrote a design doc. Can we create one from the implementation?
|
||||
```
|
||||
|
||||
**Game-Designer (reads code, analyzes):**
|
||||
```
|
||||
I've analyzed your skill tree implementation. Here's what I found:
|
||||
|
||||
STRUCTURE:
|
||||
- 3 skill trees: Combat, Survival, Tech
|
||||
- 5 tiers per tree (unlock via level + prerequisites)
|
||||
- Total 45 skills (15 per tree)
|
||||
- Cost: skill points (1 per level, can respec for gold)
|
||||
|
||||
MECHANICS:
|
||||
- Passive skills (stat bonuses)
|
||||
- Active skills (abilities with cooldowns)
|
||||
- Synergy bonuses (cross-tree unlocks at tier 4+)
|
||||
|
||||
IMPLEMENTATION DETAILS:
|
||||
- Data-driven (JSON files in assets/data/skills/)
|
||||
- Save-compatible (skill state serialized)
|
||||
- UI shows locked/unlocked/available states
|
||||
|
||||
QUESTIONS before I draft the design doc:
|
||||
|
||||
1. **Design Intent:** Was the 3-tree structure meant to represent player archetypes
|
||||
(fighter/survivor/engineer) or playstyle flexibility?
|
||||
|
||||
2. **Respec Cost:** Respec costs gold - is this meant to:
|
||||
- Discourage frequent respecs (commitment to builds)?
|
||||
- Create a gold sink (economy balance)?
|
||||
- Or should it be free (experimentation encouraged)?
|
||||
|
||||
3. **Synergy System:** Cross-tree synergies appear at tier 4. Was this:
|
||||
- Intentional (reward hybrid builds late-game)?
|
||||
- Or emergent (accidentally creates hybrid meta)?
|
||||
|
||||
4. **Balance Philosophy:** I see some skills are clearly better (tier-5 passive
|
||||
is +50% damage). Is this:
|
||||
- Power fantasy (late-game should feel OP)?
|
||||
- Or unbalanced (needs tuning)?
|
||||
```
|
||||
|
||||
**User:**
|
||||
```
|
||||
Good questions! Here's the intent:
|
||||
|
||||
1. Yes, the 3 trees are archetypes but I want flexibility
|
||||
2. Respec should be cheap - I want experimentation
|
||||
3. Synergies were intentional - hybrid builds are a feature
|
||||
4. Yeah, tier 5 is meant to be powerful, but +50% might be too much
|
||||
```
|
||||
|
||||
**Game-Designer:**
|
||||
```
|
||||
Perfect, that clarifies the vision. Let me draft the design doc:
|
||||
|
||||
[Shows draft of design/gdd/skill-system.md]
|
||||
|
||||
SECTIONS:
|
||||
1. Overview (3-tree system, hybrid-friendly, respec-encouraged)
|
||||
2. Design Pillars:
|
||||
- Archetype clarity (each tree has identity)
|
||||
- Build flexibility (no lock-in, cheap respec)
|
||||
- Synergy rewards (cross-tree combinations viable)
|
||||
3. Detailed Design (tree structure, unlock rules, respec)
|
||||
4. Balance Framework (tier power curve, synergy limits)
|
||||
5. Edge Cases (respec during combat? max skills? cap?)
|
||||
6. Dependencies (UI, save system, tutorial)
|
||||
7. Acceptance Criteria (all trees viable, 10+ hybrid builds, clear tooltips)
|
||||
8. Open Questions:
|
||||
- Should respec cost scale with level or stay flat?
|
||||
- Tier 5 damage bonus: tune to +30% or keep +50%?
|
||||
|
||||
**May I write this to design/gdd/skill-system.md?**
|
||||
```
|
||||
|
||||
**User:**
|
||||
```
|
||||
Yes, write it. But let's tune tier 5 to +30% and make respec cost scale.
|
||||
```
|
||||
|
||||
**Game-Designer:**
|
||||
```
|
||||
✅ Written to design/gdd/skill-system.md
|
||||
✅ Marked as [REVERSE-DOCUMENTED FROM IMPLEMENTATION]
|
||||
✅ Added TODO: Update tier-5 passive to +30% in assets/data/skills/combat-tree.json
|
||||
✅ Added TODO: Implement scaling respec cost formula
|
||||
|
||||
Next steps:
|
||||
- Run /balance-check to validate skill tree progression curve?
|
||||
- Create ADR documenting why 3 trees instead of 5?
|
||||
- Update tutorial to explain hybrid synergies?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Observations
|
||||
|
||||
1. **Agent asked clarifying questions** - didn't just describe code
|
||||
2. **Separated intent from implementation** - user corrected "accidental" imbalance
|
||||
3. **Design doc captures "why"** - not just "what the code does"
|
||||
4. **Identified missing pieces** - edge cases not in code
|
||||
5. **Flagged follow-up work** - balance tuning, ADRs, tutorial
|
||||
|
||||
## Outcome
|
||||
|
||||
The design doc now exists and **matches reality** while also capturing the **intended vision**. Future changes will reference this doc, and new team members understand the *why* behind decisions.
|
||||
197
docs/examples/session-adopt-brownfield.md
Normal file
197
docs/examples/session-adopt-brownfield.md
Normal file
@@ -0,0 +1,197 @@
|
||||
# Example Session: /adopt — Brownfield Project Onboarding
|
||||
|
||||
**Context:** Developer has been building a game for 3 months before discovering this agent system. They have code, some design docs, and rough notes — but nothing in the right format for the skills to use.
|
||||
|
||||
**Starting State:**
|
||||
- `src/gameplay/` — ~4000 lines of Godot 4.6 GDScript
|
||||
- `design/` — 3 markdown files with rough design notes (not in GDD template format)
|
||||
- No `design/gdd/` directory
|
||||
- No `docs/architecture/` directory
|
||||
- No `production/` directory
|
||||
- No `design/gdd/systems-index.md`
|
||||
- CLAUDE.md exists (engine configured: Godot 4.6)
|
||||
|
||||
**Session Duration:** ~30 minutes
|
||||
**Turns:** 8
|
||||
**Outcome:** Adoption plan written, most urgent gap (missing system index) fixed immediately
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Invokes /adopt
|
||||
|
||||
**User:**
|
||||
> /adopt
|
||||
|
||||
## Turn 2: Agent Detects Project State
|
||||
|
||||
**Agent (technical-director, via /adopt, context: fork):**
|
||||
> Running brownfield audit...
|
||||
>
|
||||
> **Phase 1: Project State Detection**
|
||||
>
|
||||
> | Artifact Category | Found | Notes |
|
||||
> |------------------|-------|-------|
|
||||
> | Engine configuration | ✅ | Godot 4.6 in CLAUDE.md |
|
||||
> | Source code | ✅ | ~4000 lines in `src/gameplay/` |
|
||||
> | Design docs | ⚠️ | 3 files in `design/`, not in GDD format |
|
||||
> | Architecture docs | ❌ | `docs/architecture/` missing |
|
||||
> | Systems index | ❌ | `design/gdd/systems-index.md` missing |
|
||||
> | Production tracking | ❌ | `production/` directory missing |
|
||||
> | Sprint/story files | ❌ | None |
|
||||
>
|
||||
> **Phase 2: Format Audit (existing files)**
|
||||
>
|
||||
> Checking internal structure of the 3 design files...
|
||||
>
|
||||
> *`design/combat-notes.md`:*
|
||||
> - Has informal notes, no GDD template sections
|
||||
> - Contains: some rule descriptions, a damage formula, rough balance numbers
|
||||
> - Missing from GDD template: Overview, Player Fantasy, Edge Cases, Dependencies, Tuning Knobs, Acceptance Criteria (6 of 8 sections)
|
||||
>
|
||||
> *`design/crafting-ideas.md`:*
|
||||
> - Brainstorm-style notes, no structure
|
||||
> - No formulas, no acceptance criteria
|
||||
> - Represents early concept thinking — useful as input for a GDD, not yet a GDD
|
||||
>
|
||||
> *`design/inventory.md`:*
|
||||
> - Closest to a GDD — has Overview and Rules sections
|
||||
> - Missing: Player Fantasy, Formulas, Edge Cases, Tuning Knobs, Acceptance Criteria (5 of 8 sections)
|
||||
>
|
||||
> **Phase 3: Gap Classification**
|
||||
>
|
||||
> | Gap | Severity | Reason |
|
||||
> |-----|----------|--------|
|
||||
> | No systems index | BLOCKING | `/design-system`, `/create-stories`, `/gate-check` all require it |
|
||||
> | GDD files not in template format | HIGH | Skills cannot reference TR-IDs from malformed GDDs |
|
||||
> | No architecture docs | HIGH | Cannot run `/architecture-review` or `/create-control-manifest` |
|
||||
> | No production tracking | MEDIUM | Sprint and story skills won't work, but not needed until production phase |
|
||||
> | `design/crafting-ideas.md` — pre-GDD content | LOW | Useful as input; needs full GDD authoring before it blocks anything |
|
||||
|
||||
## Turn 3: Agent Presents the Migration Plan
|
||||
|
||||
**Agent:**
|
||||
> **Phase 4: Migration Plan**
|
||||
>
|
||||
> 7 ordered steps to bring your project into full alignment. NEVER overwrites your existing content — each step fills gaps only.
|
||||
>
|
||||
> | # | Step | Skill | Priority | Notes |
|
||||
> |---|------|-------|----------|-------|
|
||||
> | 1 | Create `design/gdd/systems-index.md` | `/map-systems` or `/adopt` inline | BLOCKING | Unblocks all downstream skills |
|
||||
> | 2 | Retrofit `design/inventory.md` → GDD format | `/design-system retrofit design/inventory.md` | HIGH | Adds 5 missing sections, keeps your existing content |
|
||||
> | 3 | Retrofit `design/combat-notes.md` → GDD format | `/design-system retrofit design/combat-notes.md` | HIGH | Adds 6 missing sections |
|
||||
> | 4 | Convert `design/crafting-ideas.md` → full GDD | `/design-system crafting` (new authoring, uses ideas as input) | MEDIUM | Not a retrofit — too informal to retrofit |
|
||||
> | 5 | Create architecture ADRs from existing code | `/reverse-document` + `/architecture-decision` | HIGH | Captures decisions already made in code |
|
||||
> | 6 | Create master architecture document | `/create-architecture` | HIGH | After ADRs |
|
||||
> | 7 | Set up production tracking | `/sprint-plan new` | MEDIUM | After architecture phase |
|
||||
>
|
||||
> **Important:** Step 1 must happen before any other step. Everything else can be done in parallel or reordered based on your priorities.
|
||||
>
|
||||
> May I write this plan to `docs/adoption-plan-2026-03-12.md`?
|
||||
|
||||
**User:**
|
||||
> Yes. Write it.
|
||||
|
||||
**Agent writes adoption plan.**
|
||||
|
||||
## Turn 4: Agent Offers to Fix the Most Urgent Gap
|
||||
|
||||
**Agent:**
|
||||
> The BLOCKING gap is the missing `design/gdd/systems-index.md`.
|
||||
>
|
||||
> I can create one now by reading your existing code and design docs to infer what systems you've built. This is faster than running `/map-systems` from scratch — I'll populate it based on what already exists, then you confirm or adjust.
|
||||
>
|
||||
> Would you like me to do that now?
|
||||
|
||||
**User:**
|
||||
> Yes, do it.
|
||||
|
||||
## Turn 5: Agent Reads Codebase and Drafts Systems Index
|
||||
|
||||
**Agent:**
|
||||
> Reading `src/gameplay/` to infer systems...
|
||||
>
|
||||
> Found these system boundaries in the code:
|
||||
>
|
||||
> - `src/gameplay/combat/` — damage calculation, hit detection, status effects
|
||||
> - `src/gameplay/inventory/` — item management, weight, stacks
|
||||
> - `src/gameplay/crafting/` — recipe lookup, result generation (references inventory)
|
||||
> - `src/gameplay/movement/` — CharacterBody2D, terrain modifiers (referenced by combat for positioning)
|
||||
> - `src/gameplay/ui/` — HUD, inventory screen, dialogue box
|
||||
> - `src/gameplay/world/` — tile map, terrain types, chunk loading
|
||||
>
|
||||
> **Draft systems index:**
|
||||
>
|
||||
> | System | Category | Priority | Dependencies | GDD Status |
|
||||
> |--------|----------|----------|--------------|------------|
|
||||
> | Movement | Foundation | MVP | None | ❌ Missing |
|
||||
> | World/Terrain | Foundation | MVP | Movement | ❌ Missing |
|
||||
> | Combat | Core | MVP | Movement | ⚠️ Needs retrofit |
|
||||
> | Inventory | Core | MVP | None | ⚠️ Needs retrofit |
|
||||
> | Crafting | Feature | MVP | Inventory | ❌ Pre-GDD only |
|
||||
> | UI / HUD | Presentation | MVP | Combat, Inventory, Movement | ❌ Missing |
|
||||
>
|
||||
> Does this look right? Anything I missed or miscategorized?
|
||||
|
||||
**User:**
|
||||
> Looks right. Add a Stamina system — it's in combat/ but should be its own system.
|
||||
|
||||
**Agent:**
|
||||
> Added Stamina as a Core / MVP system:
|
||||
>
|
||||
> | Stamina | Core | MVP | Movement, Combat | ❌ Missing |
|
||||
>
|
||||
> Updated dependency: Movement depends on Stamina (movement drains it).
|
||||
>
|
||||
> May I write this to `design/gdd/systems-index.md`?
|
||||
|
||||
## Turn 6: User Approves
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes `design/gdd/systems-index.md`.**
|
||||
|
||||
## Turn 7: Agent Shows What's Now Unlocked
|
||||
|
||||
**Agent:**
|
||||
> `design/gdd/systems-index.md` created.
|
||||
>
|
||||
> **BLOCKING gap resolved.** The following skills can now run:
|
||||
>
|
||||
> - ✅ `/design-system [name]` — guided GDD authoring for any system
|
||||
> - ✅ `/design-system retrofit [path]` — fill missing sections in existing docs
|
||||
> - ✅ `/gate-check` — now has a systems index to validate
|
||||
> - ✅ `/help` — can now show correct phase workflow
|
||||
>
|
||||
> **Recommended next steps (from adoption plan):**
|
||||
>
|
||||
> 1. Run `/design-system retrofit design/inventory.md` — your most complete existing doc, easiest win
|
||||
> 2. Run `/design-system retrofit design/combat-notes.md`
|
||||
> 3. Run `/design-system crafting` — author crafting GDD using your ideas file as input
|
||||
>
|
||||
> After those 3, you'll have 5 of 7 systems with complete GDDs. Then run `/review-all-gdds` to catch cross-system consistency issues before architecture work.
|
||||
|
||||
## Turn 8: User Confirms Plan
|
||||
|
||||
**User:**
|
||||
> Perfect. I'll start with the inventory retrofit.
|
||||
|
||||
**Agent:**
|
||||
> Run:
|
||||
> ```
|
||||
> /design-system retrofit design/inventory.md
|
||||
> ```
|
||||
>
|
||||
> The skill will detect your existing Overview and Rules sections, show you which of the 8 sections are missing, and run the section cycle only for the gaps. Your existing content won't be touched.
|
||||
|
||||
---
|
||||
|
||||
## What This Example Demonstrates
|
||||
|
||||
- **FORMAT audit, not existence audit**: `/adopt` doesn't just check whether files exist — it checks whether their internal structure matches what skills expect. A file named `gdd.md` with no template sections is flagged as a gap.
|
||||
- **Migration, not replacement**: existing content is never overwritten. The plan fills gaps only.
|
||||
- **BLOCKING gaps surface first**: the missing systems index is identified as BLOCKING because it prevents the most skills from functioning.
|
||||
- **Inline creation of the urgent gap**: rather than just reporting the gap, the agent offers to fix it immediately in the same session.
|
||||
- **Inferred from code**: the systems index is bootstrapped from the codebase structure, not written from scratch — brownfield code already contains the answer.
|
||||
- **Retrofit vs. new authoring**: `/design-system retrofit` handles existing-but-incomplete docs; full `/design-system` handles net-new docs. The adoption plan distinguishes which applies to each file.
|
||||
- **Context: fork**: `/adopt` runs in a forked context to avoid polluting the main session with large file reads across the whole codebase.
|
||||
262
docs/examples/session-design-crafting-system.md
Normal file
262
docs/examples/session-design-crafting-system.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Example Session: Designing the Crafting System
|
||||
|
||||
**Context:** Solo indie dev, week 2 of project, needs to design the crafting system mentioned in the game pillars.
|
||||
|
||||
**Starting State:**
|
||||
- Game concept and pillars defined
|
||||
- Engine set up (Godot 4.6)
|
||||
- Core combat system designed but not implemented yet
|
||||
- Pillar 2: "Emergent Discovery Through Experimentation"
|
||||
|
||||
**Session Duration:** ~45 minutes
|
||||
**Turns:** 12
|
||||
**Outcome:** Complete crafting system GDD ready for implementation
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Initiates Design
|
||||
|
||||
**User:**
|
||||
> I need to design the crafting system. Pillar 2 is "Emergent Discovery Through Experimentation", so I want players to discover recipes by trying combinations.
|
||||
|
||||
## Turn 2: Agent Asks Clarifying Questions
|
||||
|
||||
**Agent (game-designer):**
|
||||
> I'll help design the crafting system with discovery at its core. Before proposing mechanics, I need to understand your vision:
|
||||
>
|
||||
> 1. **Discovery Method**: Should recipes be pure trial-and-error, or should there be hints/clues to guide players?
|
||||
> 2. **Failure Punishment**: When players try an invalid combination, what happens?
|
||||
> - Materials completely lost (harsh)
|
||||
> - Materials partially refunded (moderate)
|
||||
> - No cost to experiment (forgiving)
|
||||
> 3. **Progression**: Does crafting skill improve over time? If so, what does higher skill unlock?
|
||||
> 4. **Scope**: How central is crafting to the game? Core pillar feature or supporting system?
|
||||
> 5. **Reference Games**: Any crafting systems you love or hate? (Minecraft, Terraria, Stardew Valley, Potion Craft?)
|
||||
|
||||
## Turn 3: User Provides Context
|
||||
|
||||
**User:**
|
||||
> 1. I want hints that unlock gradually - start with pure discovery, earn hints through skill
|
||||
> 2. Moderate punishment - waste materials but gain partial XP
|
||||
> 3. Yes, skill unlocks hint system and improves success rates
|
||||
> 4. Core feature - one of the three main loops (combat, exploration, crafting)
|
||||
> 5. Love Potion Craft's deduction mechanic. Hate Minecraft wikis being mandatory.
|
||||
|
||||
## Turn 4: Agent Presents Options
|
||||
|
||||
**Agent:**
|
||||
> Perfect context! Based on "emergent discovery" + "deduction over wikis" + "Potion Craft inspiration", here are three mechanical approaches:
|
||||
>
|
||||
> **Option A: Pure Randomized Discovery**
|
||||
> - **Mechanics**: Try any 2-4 ingredient combo, roll for success based on compatibility
|
||||
> - **Hint System**: None - players document discoveries manually
|
||||
> - **Pros**:
|
||||
> - Maximum emergent discovery
|
||||
> - Community can share findings without breaking core loop
|
||||
> - Aligns strongly with "experimentation" pillar
|
||||
> - **Cons**:
|
||||
> - High frustration for completionists
|
||||
> - Low skill expression (mostly RNG)
|
||||
> - Players may feel "wiki is required" despite your intent
|
||||
> - **Aligns with Pillars**: ✅✅ Emergent Discovery, ⚠️ Meaningful Choices (randomness reduces agency)
|
||||
>
|
||||
> **Option B: Tag-Based Deduction System (Potion Craft-style)**
|
||||
> - **Mechanics**: Each ingredient has hidden "tags" (Fire, Water, Healing, Damage, etc.)
|
||||
> - Recipes require specific tag combinations
|
||||
> - At Skill 0: No tags visible, pure experimentation
|
||||
> - At Skill 5: Can inspect items to see 1 tag
|
||||
> - At Skill 10: See 2 tags, etc.
|
||||
> - **Hint System**: Tags ARE the hint system, unlocked by skill
|
||||
> - **Pros**:
|
||||
> - Rewards observation and pattern recognition (skill expression!)
|
||||
> - Progressive scaffolding - accessible to all player types
|
||||
> - Creates "aha!" moments when patterns click
|
||||
> - No wiki needed - everything discoverable in-game
|
||||
> - **Cons**:
|
||||
> - More complex to implement (tag system + UI)
|
||||
> - Requires careful tag design to avoid degeneracy
|
||||
> - **Aligns with Pillars**: ✅✅ Emergent Discovery, ✅✅ Meaningful Choices
|
||||
>
|
||||
> **Option C: Ingredient Compatibility Matrix**
|
||||
> - **Mechanics**: Each ingredient pairs well/poorly with others
|
||||
> - Success rate = compatibility score
|
||||
> - Skill 0: No compatibility shown, ~30% base success
|
||||
> - Higher skill: See color-coded compatibility (red/yellow/green)
|
||||
> - **Hint System**: Visual compatibility indicators unlock with skill
|
||||
> - **Pros**:
|
||||
> - Simple to implement and understand
|
||||
> - Clear progression arc (skill = more information)
|
||||
> - Feels fair (players understand why they failed)
|
||||
> - **Cons**:
|
||||
> - Less emergent than tag system (more prescriptive)
|
||||
> - Optimal recipes more obvious once you have hints
|
||||
> - **Aligns with Pillars**: ✅ Emergent Discovery, ✅ Meaningful Choices
|
||||
>
|
||||
> **My Recommendation: Option B (Tag-Based Deduction)**
|
||||
>
|
||||
> **Reasoning:**
|
||||
> - It's the only option that creates TRUE emergent discovery through systems interaction
|
||||
> - Players develop mastery by learning tag patterns, not memorizing recipes
|
||||
> - The progressive reveal (tags unlock with skill) serves both Explorers (discovery) and Achievers (progression)
|
||||
> - Potion Craft proved this works and is satisfying
|
||||
> - Avoids wiki-dependency because the deduction is solvable in-game
|
||||
>
|
||||
> Which option resonates with your vision? Or should I explore a hybrid?
|
||||
|
||||
## Turn 5: User Decides with Modification
|
||||
|
||||
**User:**
|
||||
> Option B feels perfect, but I want to add: failed attempts should give hints about which tags were WRONG, not just which were right. Like "You added Fire, but this recipe needs Water instead."
|
||||
|
||||
## Turn 6: Agent Incorporates Feedback and Drafts
|
||||
|
||||
**Agent:**
|
||||
> Excellent modification! That adds a deduction game on TOP of the tag discovery. Now failed attempts aren't just "waste" - they're valuable information.
|
||||
>
|
||||
> Let me draft the **Overview** and **Player Fantasy** sections:
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> ## Overview
|
||||
>
|
||||
> The crafting system uses a tag-based deduction mechanic where players experiment with ingredient combinations to discover recipes. Each ingredient has 1-3 hidden tags (Fire, Water, Earth, Air, Healing, Damage, Poison, etc.). Recipes require specific tag combinations. Failed attempts waste materials but provide feedback ("This needs Water, not Fire") that teaches players tag patterns. As crafting skill increases, players unlock the ability to inspect ingredients to see their tags directly, transforming pure experimentation into informed deduction.
|
||||
>
|
||||
> ## Player Fantasy
|
||||
>
|
||||
> "I'm an alchemist-scientist discovering nature's hidden patterns. Each failed potion teaches me something new about how elements interact. Over time, I develop intuition - I can look at a mushroom and KNOW it contains Earth and Poison tags before even testing it. I don't need recipes from a wiki; I understand the RULES that generate recipes, and I can create my own solutions."
|
||||
>
|
||||
> **Target MDA Aesthetics**:
|
||||
> - Discovery (primary): Uncovering hidden tag patterns
|
||||
> - Challenge (secondary): Deduction puzzle
|
||||
> - Expression (tertiary): Creating custom recipes once rules are mastered
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> Does this capture your intent? Should I continue to **Detailed Rules**?
|
||||
|
||||
## Turn 7: User Approves and Requests Next Section
|
||||
|
||||
**User:**
|
||||
> Yes, that's perfect! Continue to Detailed Rules.
|
||||
|
||||
## Turn 8: Agent Drafts Rules, Asks About Edge Case
|
||||
|
||||
**Agent:**
|
||||
> Drafting Detailed Rules...
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> ## Detailed Rules
|
||||
>
|
||||
> ### Core Loop
|
||||
> 1. Player selects 2-4 ingredients from inventory
|
||||
> 2. Player activates crafting station
|
||||
> 3. System checks ingredient tags against recipe database
|
||||
> 4. **If exact tag match**: Success! Recipe learned, item created, full XP awarded
|
||||
> 5. **If partial match** (some tags correct): Failure. Feedback: "Close! This needs [missing tag]." Materials consumed, 40% XP awarded, recipe marked as "Discovered (Incomplete)"
|
||||
> 6. **If no match**: Failure. Feedback: "Incompatible. Try replacing [wrong tag] with [needed tag]." Materials consumed, 15% XP awarded
|
||||
>
|
||||
> ### Tag System
|
||||
> - 12 core tags: Fire, Water, Earth, Air, Healing, Damage, Poison, Light, Dark, Metal, Plant, Magic
|
||||
> - Each ingredient has 1-3 tags (simple ingredients have 1, rare ingredients have 3)
|
||||
> - Each recipe requires 2-6 total tags (basic potions need 2, advanced need 6)
|
||||
> - Tag combinations must be unique (no two recipes with identical tag sets)
|
||||
>
|
||||
> ### Skill Progression
|
||||
> - Skill 0-4: No inspection ability. Pure experimentation.
|
||||
> - Skill 5-9: Can inspect ingredients to see 1 tag
|
||||
> - Skill 10-14: Can see 2 tags per ingredient
|
||||
> - Skill 15+: Can see all tags per ingredient
|
||||
>
|
||||
> ### XP Gain
|
||||
> ```
|
||||
> success_xp = base_xp * recipe_complexity^1.3
|
||||
> failure_xp_partial = success_xp * 0.40
|
||||
> failure_xp_none = success_xp * 0.15
|
||||
> ```
|
||||
>
|
||||
> Where `recipe_complexity` = number of unique tags required (2-6)
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> **Question before I continue:** What happens if a player discovers a tag combination that DOESN'T match any recipe in the database? Should we:
|
||||
> A) Allow it and create "junk items" (player wasted materials)
|
||||
> B) Always provide feedback steering toward valid recipes
|
||||
> C) Procedurally generate a minor potion for any valid tag combo not in the DB
|
||||
>
|
||||
> This affects the "experimentation" feel - option A is harsh but realistic, option C rewards all experimentation.
|
||||
|
||||
## Turn 9: User Resolves Edge Case
|
||||
|
||||
**User:**
|
||||
> Option C - let's procedurally generate minor potions. If someone finds Fire+Water (not a designed recipe), give them "Warm Water" that restores 5 HP. Rewards experimentation even when "off the beaten path."
|
||||
|
||||
## Turn 10: Agent Completes Remaining Sections
|
||||
|
||||
**Agent:**
|
||||
> Perfect! That keeps experimentation always rewarding. Updating rules with that edge case...
|
||||
>
|
||||
> [Agent continues drafting Formulas, Edge Cases, Dependencies, Tuning Knobs, and Acceptance Criteria sections, showing each and incorporating minor feedback]
|
||||
>
|
||||
> [After all sections complete]
|
||||
>
|
||||
> Complete crafting system GDD ready. Here's the summary:
|
||||
>
|
||||
> **8 Required Sections:**
|
||||
> ✅ Overview - Tag-based deduction system
|
||||
> ✅ Player Fantasy - Alchemist-scientist discovering patterns
|
||||
> ✅ Detailed Rules - Core loop with tag matching and feedback system
|
||||
> ✅ Formulas - XP curves, success rates, skill thresholds
|
||||
> ✅ Edge Cases - Procedural minor potions for non-recipe combos, inventory full, skill at max
|
||||
> ✅ Dependencies - Requires inventory system, integrates with progression system
|
||||
> ✅ Tuning Knobs - XP rates, skill thresholds, tag count per ingredient, feedback verbosity
|
||||
> ✅ Acceptance Criteria - Players discover 10 recipes in 30 minutes without wiki, skill progression feels meaningful
|
||||
>
|
||||
> **Delegations Made:**
|
||||
> - Consulted `systems-designer` for XP curve formula validation
|
||||
> - Consulted `economy-designer` for material cost balancing
|
||||
>
|
||||
> May I write this to `design/gdd/crafting-system.md`?
|
||||
|
||||
## Turn 11: User Approves Writing
|
||||
|
||||
**User:**
|
||||
> Yes, write it!
|
||||
|
||||
## Turn 12: Agent Writes File and Offers Next Steps
|
||||
|
||||
**Agent:**
|
||||
> ✅ Created `design/gdd/crafting-system.md`
|
||||
>
|
||||
> **Next Steps:**
|
||||
> 1. Run `/design-review design/gdd/crafting-system.md` to validate against 8-section standard
|
||||
> 2. Create data file structure in `assets/data/` for ingredients and recipes
|
||||
> 3. When ready to implement, use `/team-combat` pattern but for crafting (game-designer → gameplay-programmer → ui-programmer → qa-tester)
|
||||
>
|
||||
> Would you like me to run the design review now, or would you prefer to read it first?
|
||||
|
||||
---
|
||||
|
||||
## Session Summary
|
||||
|
||||
**What Worked Well:**
|
||||
- Agent asked clarifying questions upfront instead of guessing
|
||||
- Presented 3 distinct options with clear pros/cons and pillar alignment
|
||||
- User's modification ("wrong tag feedback") was incorporated immediately
|
||||
- Edge case (non-recipe combos) was flagged proactively, not assumed
|
||||
- Each section was shown for approval before moving to next
|
||||
- Explicit permission requested before writing file
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Turn 2: Agent asked 5 questions to understand vision
|
||||
- Turn 4: Agent presented 3 options, made recommendation but deferred to user
|
||||
- Turn 5: User modified recommended option, agent incorporated it
|
||||
- Turn 8: Agent caught edge case and asked rather than assuming
|
||||
- Turn 9: User's choice (procedural potions) changed design direction
|
||||
- Turn 10: Agent requested approval before writing file
|
||||
|
||||
**Files Created:**
|
||||
- `design/gdd/crafting-system.md` (complete 8-section GDD)
|
||||
|
||||
**Context Usage:** ~35% (estimated)
|
||||
**User Satisfaction:** High - user made all creative decisions, agent provided expert guidance
|
||||
269
docs/examples/session-design-system-skill.md
Normal file
269
docs/examples/session-design-system-skill.md
Normal file
@@ -0,0 +1,269 @@
|
||||
# Example Session: Using /design-system to Author a GDD
|
||||
|
||||
**Context:** Solo indie dev, week 3 of project. `/map-systems` already ran and produced `design/gdd/systems-index.md`. The movement system is listed as MVP tier, Foundation priority.
|
||||
|
||||
**Starting State:**
|
||||
- `design/gdd/game-concept.md` exists (survival-crafting game, 2D, Godot 4.6)
|
||||
- `design/gdd/systems-index.md` exists (15 systems identified, movement is first in design order)
|
||||
- No per-system GDDs written yet
|
||||
- No upstream dependencies for movement (it is the foundation)
|
||||
|
||||
**Session Duration:** ~60 minutes
|
||||
**Turns:** 14
|
||||
**Outcome:** Complete `design/gdd/movement-system.md` with all 8 required sections written to file, ready for `/design-review`
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Invokes the Skill
|
||||
|
||||
**User:**
|
||||
> /design-system movement
|
||||
|
||||
## Turn 2: Agent Loads Context and Presents the Plan
|
||||
|
||||
**Agent (game-designer, via /design-system):**
|
||||
> I'm loading context before we start:
|
||||
>
|
||||
> - Read `design/gdd/game-concept.md` — survival-crafting, exploration-heavy, lone survivor stranded on alien planet
|
||||
> - Read `design/gdd/systems-index.md` — movement is **Foundation / MVP**, no upstream deps, downstream: combat, exploration, stamina
|
||||
> - No prior GDD found for movement — starting fresh
|
||||
>
|
||||
> **Technical Feasibility Pre-Check:**
|
||||
>
|
||||
> | Domain | Signal |
|
||||
> |--------|--------|
|
||||
> | Engine | Godot 4.6 — CharacterBody2D with move_and_slide() is well-supported |
|
||||
> | Physics | Jolt is now the default in 4.6 — 2D movement unaffected, but note for future 3D work |
|
||||
> | Downstream risk | Stamina system listed as downstream dep — movement must expose a stamina callback hook |
|
||||
>
|
||||
> The GDD template has **8 required sections**. We'll go one at a time:
|
||||
> `Overview → Player Fantasy → Detailed Rules → Formulas → Edge Cases → Dependencies → Tuning Knobs → Acceptance Criteria`
|
||||
>
|
||||
> I'll show you a draft of each section, you approve or revise, then I write it to the file before we move on.
|
||||
>
|
||||
> Creating the file skeleton now with empty section headers — may I write `design/gdd/movement-system.md` with placeholders?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes** `design/gdd/movement-system.md` with 8 empty section headers.
|
||||
|
||||
---
|
||||
|
||||
## Turn 3: Section 1 — Overview
|
||||
|
||||
**Agent:**
|
||||
> **Section 1 draft — Overview:**
|
||||
>
|
||||
> > The movement system is the player's primary interface with the world. It governs how the lone survivor walks, runs, rolls, and traverses alien terrain. Movement is physically grounded — the planet has heavier-than-Earth gravity, and the survivor is physically exhausted — but remains responsive enough to stay out of the player's way. The system feeds directly into exploration pacing, combat positioning, and the stamina system.
|
||||
>
|
||||
> Does this capture what you want?
|
||||
|
||||
**User:**
|
||||
> Good but add something about the terrain — surfaces slow you down or have friction modifiers.
|
||||
|
||||
**Agent:**
|
||||
> Revised:
|
||||
>
|
||||
> > The movement system is the player's primary interface with the world. It governs how the lone survivor walks, runs, rolls, and traverses alien terrain. Movement is physically grounded — the planet has heavier-than-Earth gravity, and the survivor is physically exhausted — but remains responsive enough to stay out of the player's way. Terrain surfaces apply friction and speed modifiers (slick rock, sand, shallow water) that make navigation a meaningful skill. The system feeds directly into exploration pacing, combat positioning, and the stamina system.
|
||||
>
|
||||
> May I write this to Section 1 in the file?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes Section 1 to file.**
|
||||
|
||||
---
|
||||
|
||||
## Turn 4: Section 2 — Player Fantasy
|
||||
|
||||
**Agent:**
|
||||
> **Section 2 draft — Player Fantasy:**
|
||||
>
|
||||
> > The player should feel like a tired but capable survivor — not a superhero, not helpless. Early game movement is deliberate and taxing; the player earns fluency over time. When traversing alien terrain, the goal is careful navigation with moments of urgency (running from threats, leaping a gap). The fantasy is "I know this terrain" — every surface type becomes readable and manageable as the player internalizes the friction system.
|
||||
>
|
||||
> Approve, revise, or change direction?
|
||||
|
||||
**User:**
|
||||
> Love it. Write it.
|
||||
|
||||
**Agent writes Section 2 to file.**
|
||||
|
||||
---
|
||||
|
||||
## Turn 5: Section 3 — Detailed Rules
|
||||
|
||||
**Agent:**
|
||||
> **Section 3 draft — Detailed Rules:**
|
||||
>
|
||||
> > **Base States:**
|
||||
> > - **Walk** (default): Full directional control, moderate speed, low stamina drain
|
||||
> > - **Run** (hold Shift): Faster, medium stamina drain per second — cancels automatically when stamina hits 0
|
||||
> > - **Roll/Dodge** (tap Space): Quick burst in movement direction, brief invincibility frames, high stamina cost (flat)
|
||||
> > - **Idle**: Zero movement input, stamina regenerates at full rate
|
||||
> >
|
||||
> > **Terrain Modifiers:**
|
||||
> > - Normal ground: 1.0x speed multiplier, standard friction
|
||||
> > - Sand/loose soil: 0.8x speed, 1.1x stamina drain (harder to move)
|
||||
> > - Slick rock (wet): 1.1x speed, reduced stop friction (player overshoots)
|
||||
> > - Shallow water (ankle-deep): 0.7x speed, no friction modifier
|
||||
> > - Deep water: movement disabled, replaced by swimming (separate system, out of scope)
|
||||
> >
|
||||
> > **Collision:**
|
||||
> > - CharacterBody2D + move_and_slide(); wall slide enabled
|
||||
> > - Slopes up to 45° walked normally; >45° blocked
|
||||
> > - One-way platforms supported (drop-through with Down + Jump)
|
||||
>
|
||||
> A lot here — want me to trim or expand anything?
|
||||
|
||||
**User:**
|
||||
> Add that running is disabled when carrying a heavy object (like a large resource chunk).
|
||||
|
||||
**Agent:**
|
||||
> Added to Run rule:
|
||||
> > - **Run** (hold Shift): Faster, medium stamina drain per second — cancels automatically when stamina hits 0. **Disabled when carrying heavy objects** (signals broadcast by inventory system).
|
||||
>
|
||||
> Write to file?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes Section 3 to file.**
|
||||
|
||||
---
|
||||
|
||||
## Turn 6: Section 4 — Formulas
|
||||
|
||||
**Agent:**
|
||||
> **Section 4 draft — Formulas:**
|
||||
>
|
||||
> Let me define variables first, then the formulas:
|
||||
>
|
||||
> | Variable | Type | Description |
|
||||
> |----------|------|-------------|
|
||||
> | `base_walk_speed` | float | Base walk speed in px/s (tunable) |
|
||||
> | `run_multiplier` | float | Run speed = walk × this |
|
||||
> | `terrain_speed_mod` | float | Per-terrain multiplier (0.7–1.1) |
|
||||
> | `stamina_drain_walk` | float | Stamina units/sec while walking |
|
||||
> | `stamina_drain_run` | float | Stamina units/sec while running |
|
||||
> | `stamina_cost_roll` | float | Flat stamina cost per roll |
|
||||
> | `terrain_drain_mod` | float | Per-terrain stamina drain multiplier |
|
||||
>
|
||||
> **Effective Speed:**
|
||||
> ```
|
||||
> effective_speed = base_walk_speed × run_multiplier (if running, else 1.0) × terrain_speed_mod
|
||||
> ```
|
||||
>
|
||||
> **Stamina Drain (per second):**
|
||||
> ```
|
||||
> drain_per_sec = stamina_drain_walk (or stamina_drain_run if running) × terrain_drain_mod
|
||||
> ```
|
||||
>
|
||||
> **Roll Stamina Cost (flat):**
|
||||
> ```
|
||||
> stamina_after_roll = current_stamina - stamina_cost_roll
|
||||
> (clamped to 0 — roll still executes, but player lands with 0 stamina)
|
||||
> ```
|
||||
>
|
||||
> Approve or adjust variable names/formulas?
|
||||
|
||||
**User:**
|
||||
> Looks right. Write it.
|
||||
|
||||
**Agent writes Section 4 to file.**
|
||||
|
||||
---
|
||||
|
||||
## Turn 7–9: Sections 5, 6, 7
|
||||
|
||||
*(These follow the same pattern — agent drafts, user approves or requests minor tweaks, agent writes each section to file before moving on. Abbreviated here.)*
|
||||
|
||||
**Section 5 — Edge Cases** covered:
|
||||
- Player rolls into a wall (animation completes, no movement bonus)
|
||||
- Stamina hits 0 mid-run (transitions to walk, no lurch)
|
||||
- Two terrain types overlapping (lower multiplier wins)
|
||||
- Roll on slick rock (still applies slick friction to stopping, not to the roll itself)
|
||||
|
||||
**Section 6 — Dependencies** covered:
|
||||
- **Stamina system** (downstream): movement exposes `on_stamina_event(type: String, amount: float)` signal
|
||||
- **Inventory system** (downstream): listens to `carrying_heavy_object_changed(is_heavy: bool)` signal
|
||||
- **Input system**: movement direction from `InputMap` — standard Godot action names (`ui_left`, `ui_right`, etc.)
|
||||
- **Terrain layer**: tile metadata drives terrain modifier lookup
|
||||
|
||||
**Section 7 — Tuning Knobs** covered:
|
||||
- `base_walk_speed` (default: 120 px/s)
|
||||
- `run_multiplier` (default: 1.7)
|
||||
- Per-terrain speed and drain modifiers (table of 4 terrain types)
|
||||
- `stamina_drain_walk`, `stamina_drain_run`, `stamina_cost_roll`
|
||||
|
||||
---
|
||||
|
||||
## Turn 10: Section 8 — Acceptance Criteria
|
||||
|
||||
**Agent:**
|
||||
> **Section 8 draft — Acceptance Criteria:**
|
||||
>
|
||||
> These are the testable conditions that prove this system is complete:
|
||||
>
|
||||
> - [ ] Player walks at `base_walk_speed` on normal ground (measured with debug overlay)
|
||||
> - [ ] Player runs at `base_walk_speed × run_multiplier` on normal ground
|
||||
> - [ ] Run automatically cancels when stamina reaches 0, no input required
|
||||
> - [ ] Roll executes with correct invincibility frames (verify via hitbox debug mode)
|
||||
> - [ ] Each terrain type applies the correct speed modifier (QA table in sprint plan)
|
||||
> - [ ] Carrying heavy object disables run (test with inventory integration)
|
||||
> - [ ] Stamina system receives `on_stamina_event` signals during walk, run, and roll
|
||||
> - [ ] All movement values come from exported variables — no hardcoded numbers in code
|
||||
>
|
||||
> Does this cover what "done" means to you?
|
||||
|
||||
**User:**
|
||||
> Add one for slopes — make sure >45 degree slopes block movement.
|
||||
|
||||
**Agent:**
|
||||
> Added:
|
||||
> - [ ] Slopes ≤45° traversed normally; slopes >45° block movement (player slides back)
|
||||
>
|
||||
> Write to file?
|
||||
|
||||
**User:**
|
||||
> Yes. Write it.
|
||||
|
||||
**Agent writes Section 8 to file.**
|
||||
|
||||
---
|
||||
|
||||
## Turn 11: Completion Summary
|
||||
|
||||
**Agent:**
|
||||
> All 8 sections written to `design/gdd/movement-system.md`.
|
||||
>
|
||||
> **GDD Status:**
|
||||
> - ✅ Overview
|
||||
> - ✅ Player Fantasy
|
||||
> - ✅ Detailed Rules
|
||||
> - ✅ Formulas
|
||||
> - ✅ Edge Cases
|
||||
> - ✅ Dependencies (exposes 2 signals: stamina_event, carrying_heavy)
|
||||
> - ✅ Tuning Knobs
|
||||
> - ✅ Acceptance Criteria
|
||||
>
|
||||
> **Key decisions made:**
|
||||
> - Terrain modifiers as tile metadata (not code constants)
|
||||
> - Rolling has a flat stamina cost (not per-second)
|
||||
> - Heavy carry disables run (signal-driven, not hardcoded check)
|
||||
>
|
||||
> **Recommended next step:** Run `/design-review design/gdd/movement-system.md` before moving to the next system in your queue (stamina is next in dependency order, per systems-index).
|
||||
>
|
||||
> The review will validate completeness, internal consistency, and catch anything that needs revision before you build on this GDD.
|
||||
|
||||
---
|
||||
|
||||
## What This Example Demonstrates
|
||||
|
||||
- **Incremental file writing**: each section written to disk immediately after approval — a session crash loses at most one in-progress section
|
||||
- **Template population**: the GDD template's 8 sections become concrete content through dialogue, not bulk generation
|
||||
- **Dependency signaling**: agent proactively surfaces downstream signal contracts (stamina, inventory) during the Dependencies section
|
||||
- **Technical feasibility pre-check**: agent notes Jolt physics change before design starts, not after implementation breaks
|
||||
- **Section-by-section cycle**: no full draft dumped at once — each section is a small, reviewable unit
|
||||
- **Recovery**: if the session crashes, the agent re-reads the file on restart and resumes from the first empty section
|
||||
152
docs/examples/session-gate-check-phase-transition.md
Normal file
152
docs/examples/session-gate-check-phase-transition.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Example Session: /gate-check Phase Transition — Systems Design → Technical Setup
|
||||
|
||||
**Context:** Solo indie dev, end of week 5. The Systems Design phase feels complete — 6 MVP GDDs written, cross-review done, engine set up. User wants to formally advance to Technical Setup.
|
||||
|
||||
**Starting State:**
|
||||
- `production/stage.txt` — currently `systems-design`
|
||||
- `design/gdd/systems-index.md` — 6 MVP systems listed, all marked `Status: GDD Complete`
|
||||
- `design/gdd/` — 6 GDD files present
|
||||
- `design/gdd/gdd-cross-review-2026-03-10.md` — cross-review report, verdict: CONCERNS (1 low-priority note)
|
||||
- Engine configured (Godot 4.6, `/setup-engine` complete)
|
||||
|
||||
**Session Duration:** ~20 minutes
|
||||
**Turns:** 7
|
||||
**Outcome:** Gate passed, `stage.txt` updated to `technical-setup`, clear list of what comes next
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Initiates Gate Check
|
||||
|
||||
**User:**
|
||||
> /gate-check
|
||||
|
||||
## Turn 2: Agent Detects Current Phase and Runs the Check
|
||||
|
||||
**Agent (via /gate-check):**
|
||||
> Reading `production/stage.txt` — current phase: **systems-design**
|
||||
>
|
||||
> Running gate: **Systems Design → Technical Setup**
|
||||
>
|
||||
> ---
|
||||
> **Required Artifacts Checklist:**
|
||||
>
|
||||
> | Artifact | Requirement | Status |
|
||||
> |----------|-------------|--------|
|
||||
> | `design/gdd/systems-index.md` | Exists, all MVP systems listed | ✅ |
|
||||
> | Per-system GDDs | All MVP systems have a GDD | ✅ (6/6) |
|
||||
> | `/design-review` per GDD | Each GDD reviewed | ✅ (6/6 review comments found) |
|
||||
> | Cross-GDD review report | `design/gdd/gdd-cross-review-*.md` exists | ✅ |
|
||||
> | Cross-review verdict | PASS or CONCERNS (not FAIL) | ✅ CONCERNS |
|
||||
> | Engine configured | `production/stage.txt` or CLAUDE.md engine entry | ✅ Godot 4.6 |
|
||||
>
|
||||
> ---
|
||||
> **Content Scan:**
|
||||
>
|
||||
> Checking GDD completeness (8 required sections each)...
|
||||
>
|
||||
> | GDD | Missing Sections |
|
||||
> |-----|-----------------|
|
||||
> | movement-system.md | None ✅ |
|
||||
> | stamina-system.md | None ✅ |
|
||||
> | combat-system.md | None ✅ |
|
||||
> | inventory-system.md | None ✅ |
|
||||
> | crafting-system.md | None ✅ |
|
||||
> | exploration-system.md | None ✅ |
|
||||
>
|
||||
> ---
|
||||
> **Cross-Review Concerns (from `gdd-cross-review-2026-03-10.md`):**
|
||||
>
|
||||
> 1. ⚠️ LOW — Crafting and inventory both define "item stack size" independently with different defaults (crafting: 99, inventory: 64). One system should be the authority; the other should reference it. Not blocking — can resolve in ADR.
|
||||
>
|
||||
> ---
|
||||
> **Verdict: PASS**
|
||||
>
|
||||
> All required artifacts present. Cross-review concern is LOW severity and can be resolved during Technical Setup via an ADR. This is a normal finding at this stage.
|
||||
>
|
||||
> **May I advance `production/stage.txt` to `technical-setup`?**
|
||||
|
||||
## Turn 3: User Confirms
|
||||
|
||||
**User:**
|
||||
> Yes, advance it.
|
||||
|
||||
## Turn 4: Agent Updates Stage and Presents Next Steps
|
||||
|
||||
**Agent:**
|
||||
> Updated `production/stage.txt` → `technical-setup`
|
||||
>
|
||||
> ---
|
||||
> **You are now in: Technical Setup**
|
||||
>
|
||||
> This phase establishes the technical foundation before production coding starts. Your goal is a complete, approved architecture so that every story written in Pre-Production has clear, unambiguous technical guidance.
|
||||
>
|
||||
> **Required steps this phase (in order):**
|
||||
>
|
||||
> 1. **`/create-architecture`** — Author the master architecture document. Covers system decomposition, data flow, layer boundaries, and module responsibilities.
|
||||
>
|
||||
> 2. **`/architecture-decision` (one per significant technical choice)** — At minimum:
|
||||
> - Rendering approach (2D camera, viewport, etc.)
|
||||
> - Save/load strategy
|
||||
> - Audio bus architecture
|
||||
> - The "item stack size authority" issue from cross-review (crafting vs inventory)
|
||||
>
|
||||
> 3. **`/architecture-review`** — Validates all ADRs for completeness, dependency ordering, and GDD traceability. Produces the TR-registry.
|
||||
>
|
||||
> 4. **`/create-control-manifest`** — Converts Accepted ADRs into a flat programmer rules sheet. This is what implementation agents will consult during coding.
|
||||
>
|
||||
> 5. **`/gate-check`** — Technical Setup → Pre-Production gate. Requires: architecture doc, all ADRs Accepted, control manifest, and dependency ordering resolved.
|
||||
>
|
||||
> **Optional but recommended:**
|
||||
> - `/tech-debt` scan if you have any prototype code you plan to carry forward
|
||||
> - `/setup-engine` re-run if you haven't yet fetched Godot 4.6 API snapshots
|
||||
>
|
||||
> **Tip:** Start with `/architecture-decision` for the item stack size conflict. It's a quick win that closes an open question before it creates ambiguity in two ADRs later.
|
||||
|
||||
## Turn 5: User Asks About the Cross-Review Concern
|
||||
|
||||
**User:**
|
||||
> What do I actually write in the ADR for the stack size conflict?
|
||||
|
||||
## Turn 6: Agent Advises
|
||||
|
||||
**Agent:**
|
||||
> The ADR decision is: **which system owns the canonical `max_stack_size` value?**
|
||||
>
|
||||
> Two options to consider:
|
||||
>
|
||||
> **Option A: Inventory owns it**
|
||||
> - `ItemData` resource (owned by inventory) gets `max_stack_size: int = 64`
|
||||
> - Crafting system reads from `ItemData`, never sets its own value
|
||||
> - Pros: inventory is the source of truth for all item properties
|
||||
> - Cons: crafting team needs to know to look at inventory data
|
||||
>
|
||||
> **Option B: Item data resource owns it (shared resource)**
|
||||
> - `ItemData.max_stack_size` is the authority for both systems
|
||||
> - Both crafting and inventory import from `ItemData`
|
||||
> - Pros: single source, no coupling between crafting and inventory modules
|
||||
> - Cons: one more thing in the shared data layer
|
||||
>
|
||||
> Most studios go with B (shared resource) once they have >2 systems touching item data. But either is valid for a solo project.
|
||||
>
|
||||
> Run `/architecture-decision` and it will guide you through the ADR format, including the Dependencies section that links this ADR back to the two GDDs.
|
||||
|
||||
## Turn 7: User Wraps Up
|
||||
|
||||
**User:**
|
||||
> Got it. I'll start with /architecture-decision for this.
|
||||
|
||||
**Agent:**
|
||||
> Good starting point. The ADR will document the decision, the reasoning, and the GDD requirements it addresses — so when you write code months from now, the rationale is preserved.
|
||||
>
|
||||
> Your phase gate is logged. Run `/help` at any time during Technical Setup to see your current progress against the required checklist.
|
||||
|
||||
---
|
||||
|
||||
## What This Example Demonstrates
|
||||
|
||||
- **Gate check is automated, not manual**: agent reads artifacts and checks them — user doesn't fill out a form
|
||||
- **CONCERNS ≠ FAIL**: the cross-review concern is LOW severity and passes the gate. A FAIL would require resolution before advancing.
|
||||
- **Stage.txt is the authority**: the status line, `/help`, and all skills read from `production/stage.txt` — updating it here changes what every subsequent skill sees
|
||||
- **Next steps are phase-specific**: the agent doesn't give a generic "good luck" — it gives the ordered checklist for Technical Setup specifically
|
||||
- **Gate check surfaces carry-forward work**: the stack size conflict was a cross-review note; the gate check ensures it becomes a concrete ADR rather than getting lost
|
||||
- **One advance per gate**: the user confirmed advancement explicitly. The gate doesn't auto-advance; human confirmation is required.
|
||||
296
docs/examples/session-implement-combat-damage.md
Normal file
296
docs/examples/session-implement-combat-damage.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# Example Session: Implementing Combat Damage Calculation
|
||||
|
||||
**Context:** Solo indie dev, week 4 of project, implementing gameplay systems.
|
||||
|
||||
**Starting State:**
|
||||
- `design/gdd/combat-system.md` exists and reviewed
|
||||
- Godot 4.6 project set up with basic scene structure
|
||||
- No gameplay code written yet
|
||||
- Sprint goal: "Implement core combat damage calculation"
|
||||
|
||||
**Session Duration:** ~30 minutes
|
||||
**Turns:** 10
|
||||
**Outcome:** Working damage calculator with tests, ready for integration
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Requests Implementation
|
||||
|
||||
**User:**
|
||||
> Implement the damage calculation system from design/gdd/combat-system.md. The formula is in section 4.
|
||||
|
||||
## Turn 2: Agent Reads Design and Asks Clarifying Questions
|
||||
|
||||
**Agent (gameplay-programmer):**
|
||||
> I've read `design/gdd/combat-system.md`. The damage formula is:
|
||||
>
|
||||
> ```
|
||||
> final_damage = base_damage * (1 + attack_stat * 0.1) * crit_multiplier * type_effectiveness * (1 - target_defense)
|
||||
> ```
|
||||
>
|
||||
> Before implementing, I need to clarify some details that aren't specified in the doc:
|
||||
>
|
||||
> **Architecture Questions:**
|
||||
> 1. Should `DamageCalculator` be a static utility class (no state, just calculations) or an autoload singleton?
|
||||
> 2. Where should `base_damage` come from? (Weapon data? Character stats? Ability data?)
|
||||
> 3. Where should `type_effectiveness` values live? (Per weapon? Per damage type? Separate data file?)
|
||||
>
|
||||
> **Spec Ambiguities:**
|
||||
> 4. Should `attack_stat` be clamped to a range? (Prevents exponential scaling bugs)
|
||||
> 5. The doc says "critical hits do 2x damage" but doesn't specify rounding. Should I `floor()`, `ceil()`, or `round()` the final damage?
|
||||
> 6. What happens if `target_defense` >= 1.0? (100% mitigation or higher) Should damage go negative or clamp to 0?
|
||||
>
|
||||
> **Integration:**
|
||||
> 7. I don't see a design doc for the Health/HP system. Should I create a minimal one, or is that coming separately?
|
||||
>
|
||||
> Please clarify so I can implement this correctly the first time.
|
||||
|
||||
## Turn 3: User Resolves Ambiguities
|
||||
|
||||
**User:**
|
||||
> 1. Static utility - no state needed
|
||||
> 2. Base damage from Weapon resource. I'll create a simple Weapon class.
|
||||
> 3. Type effectiveness in a separate data file `assets/data/combat_damage.json`
|
||||
> 4. Yes, clamp attack_stat to 0-100
|
||||
> 5. Use `floor()` to round down to integers
|
||||
> 6. Clamp damage to minimum of 1 (always do at least 1 damage, even with 100% defense)
|
||||
> 7. Create a minimal health component - just current_hp, max_hp, and take_damage() method
|
||||
|
||||
## Turn 4: Agent Proposes Architecture
|
||||
|
||||
**Agent:**
|
||||
> Got it! Here's my proposed architecture:
|
||||
>
|
||||
> **Files I'll create:**
|
||||
> ```
|
||||
> src/gameplay/combat/damage_calculator.gd # Static damage formula
|
||||
> src/gameplay/combat/health_component.gd # HP tracking node component
|
||||
> src/gameplay/combat/weapon.gd # Weapon resource
|
||||
> assets/data/combat_damage.json # Type effectiveness values
|
||||
> design/gdd/health-system.md # Minimal health doc
|
||||
> tests/combat/test_damage_calculator.gd # Unit tests
|
||||
> ```
|
||||
>
|
||||
> **Architecture Details:**
|
||||
>
|
||||
> **DamageCalculator** (static class):
|
||||
> ```gdscript
|
||||
> class_name DamageCalculator
|
||||
> extends RefCounted
|
||||
>
|
||||
> static func calculate_damage(
|
||||
> attacker_stats: Dictionary,
|
||||
> weapon: Weapon,
|
||||
> defender_stats: Dictionary,
|
||||
> is_crit: bool = false
|
||||
> ) -> int:
|
||||
> # Implementation here
|
||||
> return floori(final_damage)
|
||||
> ```
|
||||
>
|
||||
> **HealthComponent** (node component):
|
||||
> ```gdscript
|
||||
> extends Node
|
||||
> class_name HealthComponent
|
||||
>
|
||||
> signal health_changed(old_hp: int, new_hp: int)
|
||||
> signal died()
|
||||
>
|
||||
> @export var max_hp: int = 100
|
||||
> var current_hp: int:
|
||||
> set(value):
|
||||
> # Clamp and emit signals
|
||||
>
|
||||
> func take_damage(amount: int) -> void:
|
||||
> current_hp -= amount
|
||||
> if current_hp <= 0:
|
||||
> died.emit()
|
||||
> ```
|
||||
>
|
||||
> **Weapon** (resource):
|
||||
> ```gdscript
|
||||
> extends Resource
|
||||
> class_name Weapon
|
||||
>
|
||||
> @export var base_damage: float
|
||||
> @export var damage_type: String # "physical", "fire", "ice", etc.
|
||||
> ```
|
||||
>
|
||||
> **combat_damage.json**:
|
||||
> ```json
|
||||
> {
|
||||
> "crit_multiplier": 2.0,
|
||||
> "attack_stat_min": 0,
|
||||
> "attack_stat_max": 100,
|
||||
> "type_effectiveness": {
|
||||
> "physical": { "armored": 0.5, "flesh": 1.0, "crystal": 1.5 },
|
||||
> "fire": { "armored": 1.0, "flesh": 1.2, "crystal": 0.3 }
|
||||
> }
|
||||
> }
|
||||
> ```
|
||||
>
|
||||
> **Question:** This architecture makes `DamageCalculator` easily testable (pure function, no dependencies). The `HealthComponent` uses signals for VFX/SFX hookup. Does this match your expectations, or should I change anything before implementing?
|
||||
|
||||
## Turn 5: User Approves with Minor Change
|
||||
|
||||
**User:**
|
||||
> Looks good, but change `attacker_stats` and `defender_stats` to proper typed dictionaries or classes. I don't want untyped Dictionary bags floating around.
|
||||
|
||||
## Turn 6: Agent Refines Architecture
|
||||
|
||||
**Agent:**
|
||||
> Absolutely - type safety is important. I'll create a `CharacterStats` resource:
|
||||
>
|
||||
> ```gdscript
|
||||
> extends Resource
|
||||
> class_name CharacterStats
|
||||
>
|
||||
> @export var attack_stat: int = 0 # 0-100
|
||||
> @export var defense: float = 0.0 # 0.0-1.0 (0-100% mitigation)
|
||||
> @export var armor_type: String = "flesh" # for type effectiveness lookup
|
||||
> ```
|
||||
>
|
||||
> Then `DamageCalculator.calculate_damage()` signature becomes:
|
||||
> ```gdscript
|
||||
> static func calculate_damage(
|
||||
> attacker: CharacterStats,
|
||||
> weapon: Weapon,
|
||||
> defender: CharacterStats,
|
||||
> is_crit: bool = false
|
||||
> ) -> int
|
||||
> ```
|
||||
>
|
||||
> Much cleaner. May I proceed with implementation?
|
||||
|
||||
## Turn 7: User Approves Implementation
|
||||
|
||||
**User:**
|
||||
> Yes, implement it.
|
||||
|
||||
## Turn 8: Agent Implements and Reports
|
||||
|
||||
**Agent:**
|
||||
> Implementation complete! Here's what I created:
|
||||
>
|
||||
> **Files Created:**
|
||||
> - ✅ `src/gameplay/combat/damage_calculator.gd` (82 lines)
|
||||
> - ✅ `src/gameplay/combat/health_component.gd` (45 lines)
|
||||
> - ✅ `src/gameplay/combat/weapon.gd` (12 lines)
|
||||
> - ✅ `src/gameplay/combat/character_stats.gd` (16 lines)
|
||||
> - ✅ `assets/data/combat_damage.json` (validated JSON)
|
||||
> - ✅ `design/gdd/health-system.md` (minimal 8-section doc)
|
||||
>
|
||||
> **Rules Enforcement Results:**
|
||||
>
|
||||
> The `gameplay-code` rule automatically flagged 2 issues during implementation:
|
||||
> 1. ❌ `crit_multiplier` was hardcoded as `2.0` in damage_calculator.gd
|
||||
> - **Fixed:** Moved to `combat_damage.json` and loaded from config
|
||||
> 2. ❌ `HealthComponent` had direct `print()` debug output
|
||||
> - **Fixed:** Removed hardcoded debug, added proper signals instead
|
||||
>
|
||||
> The `data-files` rule validated:
|
||||
> - ✅ `combat_damage.json` is valid JSON
|
||||
> - ✅ Naming convention matches `[system]_[name].json`
|
||||
> - ✅ All values have comment documentation
|
||||
>
|
||||
> **Implementation Notes:**
|
||||
> - Attack stat is clamped in `calculate_damage()` before use
|
||||
> - Damage always returns minimum of 1 (per your requirement)
|
||||
> - Type effectiveness defaults to 1.0 if combo not found in config
|
||||
> - All numeric values loaded from `combat_damage.json` for designer tuning
|
||||
>
|
||||
> **Manual Testing:**
|
||||
> I created a quick test scene and validated:
|
||||
> - Base damage: 10, attack_stat: 50 → Damage: 15 ✓
|
||||
> - Same but with crit → Damage: 30 ✓
|
||||
> - With 50% defense → Damage: 15 (before defense) → 7 (after) ✓
|
||||
> - With 100% defense → Damage: 1 (clamped) ✓
|
||||
>
|
||||
> **Next Steps - Your Choice:**
|
||||
> A) I can write formal unit tests now (recommended - verification-driven development)
|
||||
> B) You can review the code first via `/code-review src/gameplay/combat/damage_calculator.gd`
|
||||
> C) Skip tests for now and integrate into actual combat
|
||||
>
|
||||
> What would you like?
|
||||
|
||||
## Turn 9: User Requests Tests
|
||||
|
||||
**User:**
|
||||
> A - write unit tests. Cover all the edge cases.
|
||||
|
||||
## Turn 10: Agent Writes Tests and Completes
|
||||
|
||||
**Agent:**
|
||||
> Unit tests created: `tests/combat/test_damage_calculator.gd`
|
||||
>
|
||||
> **Test Coverage (8 tests):**
|
||||
> ```
|
||||
> ✓ test_damage_basic_calculation() - Verify formula with known values
|
||||
> ✓ test_damage_with_crit() - Critical hits double damage
|
||||
> ✓ test_damage_with_defense() - Defense reduces damage correctly
|
||||
> ✓ test_damage_type_effectiveness() - Type modifiers apply
|
||||
> ✓ test_damage_min_clamp() - Never goes below 1
|
||||
> ✓ test_damage_attack_stat_clamp() - Attack stat clamped to 0-100
|
||||
> ✓ test_damage_missing_type_combo() - Defaults to 1.0 if not in config
|
||||
> ✓ test_damage_rounding() - Verifies floor() behavior
|
||||
> ```
|
||||
>
|
||||
> **Test Results:**
|
||||
> ```
|
||||
> Running tests/combat/test_damage_calculator.gd...
|
||||
> ✓ All 8 tests PASSED
|
||||
> Execution time: 12ms
|
||||
> ```
|
||||
>
|
||||
> **Code Review Self-Check:**
|
||||
> - ✅ All tests follow `test_[system]_[scenario]_[expected]` naming
|
||||
> - ✅ Arrange/Act/Assert structure used
|
||||
> - ✅ No external state dependencies
|
||||
> - ✅ Each test is independent and can run in any order
|
||||
>
|
||||
> **System is ready for integration!**
|
||||
>
|
||||
> Files you can now commit:
|
||||
> ```bash
|
||||
> git add src/gameplay/combat/*.gd
|
||||
> git add assets/data/combat_damage.json
|
||||
> git add design/gdd/health-system.md
|
||||
> git add tests/combat/test_damage_calculator.gd
|
||||
> git commit -m "Implement damage calculation system per design/gdd/combat-system.md"
|
||||
> ```
|
||||
>
|
||||
> The `validate-commit` hook will check:
|
||||
> - ✅ Design doc referenced in message
|
||||
> - ✅ JSON validity
|
||||
> - ✅ No hardcoded values (already fixed)
|
||||
>
|
||||
> Ready to commit?
|
||||
|
||||
---
|
||||
|
||||
## Session Summary
|
||||
|
||||
**What Worked Well:**
|
||||
- Agent read design doc first, identified 7 ambiguities upfront
|
||||
- Didn't assume or guess - asked for clarification on every unclear point
|
||||
- Proposed full architecture for user approval before writing code
|
||||
- Incorporated user's type safety feedback immediately
|
||||
- Rules caught issues automatically and agent fixed them transparently
|
||||
- Tests written proactively following verification-driven development
|
||||
- Agent offered next-step options rather than assuming what user wants
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Turn 2: Agent identified 7 spec gaps and asked rather than guessing
|
||||
- Turn 4: Full architecture proposal shown before implementation
|
||||
- Turn 5: User requested type safety, agent adapted
|
||||
- Turn 8: Agent transparently reported rule violations and fixes
|
||||
- Turn 10: Agent verified all acceptance criteria with tests
|
||||
|
||||
**Files Created:**
|
||||
- 4 source files (damage_calculator, health_component, weapon, character_stats)
|
||||
- 1 config file (combat_damage.json)
|
||||
- 1 design doc (health-system.md)
|
||||
- 1 test file (test_damage_calculator.gd)
|
||||
|
||||
**Context Usage:** ~25% (focused implementation task)
|
||||
**User Satisfaction:** High - spec ambiguities resolved early, no rework needed
|
||||
360
docs/examples/session-scope-crisis-decision.md
Normal file
360
docs/examples/session-scope-crisis-decision.md
Normal file
@@ -0,0 +1,360 @@
|
||||
# Example Session: Scope Crisis - Strategic Decision Making
|
||||
|
||||
**Context:** Solo indie dev, week 8 of project (month 2), approaching Alpha milestone.
|
||||
|
||||
**Crisis:**
|
||||
- Alpha milestone is in 2 weeks
|
||||
- Crafting system (Pillar 2) not yet implemented
|
||||
- Lead programmer says full crafting will take 3 weeks
|
||||
- Investor demo at Alpha - must show all pillars
|
||||
|
||||
**Session Duration:** ~25 minutes
|
||||
**Turns:** 8
|
||||
**Outcome:** Strategic scope decision with documented ADR
|
||||
|
||||
---
|
||||
|
||||
## Turn 1: User Escalates Problem to Creative Director
|
||||
|
||||
**User:**
|
||||
> We have a problem. Alpha is in 2 weeks. The crafting system (Pillar 2 "Emergent Discovery") isn't implemented yet. Lead programmer says it'll take 3 weeks to implement fully. But the investor demo is at Alpha and they need to see both pillars (Combat + Crafting). What should I do?
|
||||
|
||||
## Turn 2: Creative Director Gathers Full Context
|
||||
|
||||
**Agent (creative-director):**
|
||||
> This is a classic pillar vs. schedule conflict - exactly what I'm here to help resolve. Let me gather the full context before recommending options.
|
||||
>
|
||||
> First, let me review the relevant documents:
|
||||
> - `production/milestones/alpha.md` (milestone definition)
|
||||
> - `design/pillars.md` (what's non-negotiable)
|
||||
> - `design/gdd/crafting-system.md` (what was designed)
|
||||
> - `production/sprints/sprint-current.md` (current work)
|
||||
>
|
||||
> [Reads documents]
|
||||
>
|
||||
> **Context Understood:**
|
||||
> - Pillar 2: "Emergent Discovery Through Experimentation" (crafting is primary expression)
|
||||
> - Alpha success criteria: "All pillars demonstrated in playable form"
|
||||
> - Investor demo purpose: Validate vision and secure next funding round
|
||||
> - Current crafting design: Tag-based deduction system with 12 tags, ~50 recipes, progressive hint unlocking
|
||||
>
|
||||
> **Clarifying Questions:**
|
||||
> 1. Is the Alpha date **hard** (investor won't reschedule) or **soft** (we could ask for 1 more week)?
|
||||
> 2. What's the **minimum crafting** that still demonstrates Pillar 2? (One recipe? Ten recipes? Full tag system?)
|
||||
> 3. What **breaks** if crafting is cut entirely from Alpha? (Does the game still make sense?)
|
||||
> 4. How **critical** is this investor relationship? (Nice-to-have funding vs. make-or-break for project survival?)
|
||||
> 5. What's the **team status**? (Burned out from crunch? Fresh and could push if needed?)
|
||||
|
||||
## Turn 3: User Provides Decision Context
|
||||
|
||||
**User:**
|
||||
> 1. Hard date - investor flying in specifically for demo, can't reschedule
|
||||
> 2. I think minimum is: discovery mechanic working (tag system) + 10 recipes. Doesn't need polish.
|
||||
> 3. Without crafting, Alpha only shows combat. That's 50% of the vision - investor might think it's just another combat game
|
||||
> 4. Make-or-break. This investor is our only lead. No funding = project dies.
|
||||
> 5. Just me solo, I'm not burned out yet.
|
||||
|
||||
## Turn 4: Creative Director Frames the Decision
|
||||
|
||||
**Agent:**
|
||||
> Thank you. Now I understand what's truly at stake. Let me frame this decision properly:
|
||||
>
|
||||
> **The Core Question:**
|
||||
> Do we deliver full crafting and slip Alpha, deliver simplified crafting and make Alpha, or cut crafting and risk the investor not seeing our full vision?
|
||||
>
|
||||
> **What's Really at Stake:**
|
||||
> - **Vision Integrity**: Can investor see Pillar 2 at all?
|
||||
> - **Schedule Trust**: Do we keep our commitment to Alpha date?
|
||||
> - **Project Survival**: No funding = no game
|
||||
> - **Quality Standards**: Is "rough but working" acceptable for this demo?
|
||||
>
|
||||
> **Decision Criteria (in priority order for this scenario):**
|
||||
> 1. **Investor Confidence** (highest - project survival depends on this)
|
||||
> 2. **Pillar Representation** (high - they need to see the full vision)
|
||||
> 3. **Schedule Integrity** (medium - matters, but less than funding)
|
||||
> 4. **Polish Quality** (lowest - vertical slice can be rough)
|
||||
>
|
||||
> Now let me present three strategic options...
|
||||
|
||||
## Turn 5: Creative Director Presents Options with Analysis
|
||||
|
||||
**Agent:**
|
||||
> **Option A: Implement Full Crafting System (3 weeks, slip Alpha by 1 week)**
|
||||
>
|
||||
> **Execution:**
|
||||
> - Implement complete tag-based system with all 50 recipes
|
||||
> - Full UI polish, all features from GDD
|
||||
> - Miss Alpha date, reschedule investor demo (+1 week)
|
||||
>
|
||||
> **Pros:**
|
||||
> - ✅ Pillar 2 fully represented with production quality
|
||||
> - ✅ No design compromises
|
||||
> - ✅ First impression is polished
|
||||
>
|
||||
> **Cons:**
|
||||
> - ❌ Breaks investor commitment (they're flying in for the original date)
|
||||
> - ❌ Signals schedule management issues ("if they slipped Alpha, will they slip release?")
|
||||
> - ❌ May damage investor trust more than rough demo would
|
||||
>
|
||||
> **Risk: CRITICAL**
|
||||
> - Investor may not reschedule (busy schedule, other opportunities)
|
||||
> - Even if they do, the slip damages credibility
|
||||
> - Unlikely to succeed given hard date constraint
|
||||
>
|
||||
> **Verdict: NOT RECOMMENDED** - The date is hard and non-negotiable
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> **Option B: Simplify Crafting to Core Pillar (1.5 weeks, make Alpha)**
|
||||
>
|
||||
> **Execution:**
|
||||
> - Implement core tag discovery mechanic ONLY
|
||||
> - 10 recipes instead of 50 (enough to demonstrate the system)
|
||||
> - Basic UI (functional, not polished)
|
||||
> - No progressive hint unlock (all tags visible at Skill 0 for demo clarity)
|
||||
> - No failed-attempt feedback system (just success/fail)
|
||||
>
|
||||
> **Scope: "Vertical Slice of Pillar 2"**
|
||||
> - Players CAN discover recipes by combining tagged ingredients
|
||||
> - Players CAN learn tag patterns and deduce new recipes
|
||||
> - The SOUL of Pillar 2 is present, just unpolished
|
||||
>
|
||||
> **Pros:**
|
||||
> - ✅ Makes Alpha deadline (schedule integrity maintained)
|
||||
> - ✅ Pillar 2 demonstrated (investor sees full vision)
|
||||
> - ✅ Core mechanic is playable and provable
|
||||
> - ✅ Can use "Alpha → Beta improvement" as narrative ("here's rough, here's polished")
|
||||
>
|
||||
> **Cons:**
|
||||
> - ⚠️ First impression is "rough" (but vertical slices are expected to be rough)
|
||||
> - ⚠️ Investor might think "this feels small" (mitigation: present roadmap showing post-Alpha expansion)
|
||||
>
|
||||
> **Risk: MEDIUM**
|
||||
> - Investor might not "get" the pillar from 10 recipes (mitigation: good demo script explaining the vision)
|
||||
> - Might feel incomplete (mitigation: frame as "vertical slice proving the concept")
|
||||
>
|
||||
> **Verdict: RECOMMENDED** - Balances all constraints
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> **Option C: Cut Crafting from Alpha, Focus on Combat Polish (0 weeks, make Alpha)**
|
||||
>
|
||||
> **Execution:**
|
||||
> - Deliver Alpha with only Pillar 1 (Combat) fully polished
|
||||
> - Use extra 2 weeks to super-polish combat, add content, make it shine
|
||||
> - Present crafting as "design doc + concept art" (not playable)
|
||||
>
|
||||
> **Pros:**
|
||||
> - ✅ Makes deadline with zero stress
|
||||
> - ✅ What's there is AAA-polished
|
||||
> - ✅ No risk of "half-baked" perception
|
||||
>
|
||||
> **Cons:**
|
||||
> - ❌ Pillar 2 not demonstrated (50% of vision missing)
|
||||
> - ❌ Investor sees "another combat game" - not differentiated
|
||||
> - ❌ Harder to secure funding for "potential" vs. "proven concept"
|
||||
> - ❌ If combat alone doesn't wow them, we have no fallback
|
||||
>
|
||||
> **Risk: HIGH**
|
||||
> - Investor won't see what makes this game special
|
||||
> - They might think "Combat is fine, but where's the hook?"
|
||||
> - Competing games have combat - we need to show our differentiator
|
||||
>
|
||||
> **Verdict: NOT RECOMMENDED** - Too risky for make-or-break demo
|
||||
>
|
||||
> ---
|
||||
>
|
||||
> ## My Recommendation: Option B (Simplified Crafting Core)
|
||||
>
|
||||
> **Reasoning:**
|
||||
> 1. **Investor Confidence**: Making the date we committed to shows reliability
|
||||
> 2. **Pillar Visibility**: Rough but working > polished but missing. Investors back VISION, not polish.
|
||||
> 3. **Demo Narrative**: "Here's Alpha vs. Beta progression" is compelling - shows trajectory
|
||||
> 4. **Risk Mitigation**: If crafting doesn't land, combat is still solid. If we cut crafting (Option C) and combat doesn't wow them alone, we have nothing.
|
||||
> 5. **Historical Precedent**: Hades, Dead Cells, Slay the Spire all showed rough Early Access verticals and investors loved the SYSTEMS over polish
|
||||
>
|
||||
> **What Option B Sacrifices:**
|
||||
> - Polish (acceptable for vertical slice)
|
||||
> - Feature completeness (50 recipes → 10)
|
||||
> - Progressive unlocks (deferred to post-Alpha)
|
||||
>
|
||||
> **What Option B Protects:**
|
||||
> - Schedule trust
|
||||
> - Pillar representation
|
||||
> - Vision clarity
|
||||
> - Project survival (funding secured)
|
||||
>
|
||||
> **Implementation Plan if you choose Option B:**
|
||||
> 1. I'll create an ADR documenting this scope decision
|
||||
> 2. I'll update `design/gdd/crafting-system.md` with "Alpha Scope" markers
|
||||
> 3. I'll brief gameplay-programmer on simplified implementation
|
||||
> 4. I'll create a demo script for you: how to PRESENT the 10-recipe system to show the 50-recipe vision
|
||||
>
|
||||
> **But this is your call.** You know this investor better than I do. If you believe they'll value polish over pillar representation, Option C might be right. If you think they'll forgive a 1-week slip, Option A is safest creatively.
|
||||
>
|
||||
> What's your decision?
|
||||
|
||||
## Turn 6: User Makes Decision
|
||||
|
||||
**User:**
|
||||
> Option B. The investor is technical and will appreciate the systems thinking even if it's rough. Let's simplify the crafting scope to make Alpha. Can you create the ADR and update the GDD?
|
||||
|
||||
## Turn 7: Creative Director Documents Decision
|
||||
|
||||
**Agent:**
|
||||
> Excellent decision. I'll document this thoroughly so future-you understands why we made this choice.
|
||||
>
|
||||
> **Creating:**
|
||||
> 1. ADR-007: Alpha Crafting Scope Reduction
|
||||
> 2. Updated design/gdd/crafting-system.md with Alpha/Beta scope markers
|
||||
> 3. Updated production/milestones/alpha.md success criteria
|
||||
> 4. Demo script guidance for investor presentation
|
||||
>
|
||||
> [Creates documents]
|
||||
>
|
||||
> **ADR Summary:**
|
||||
> ```markdown
|
||||
> ## Decision
|
||||
> Simplify crafting system to core tag discovery mechanic (10 recipes)
|
||||
> for Alpha milestone. Defer full implementation (50 recipes + progressive
|
||||
> unlocks) to post-Alpha.
|
||||
>
|
||||
> ## Context
|
||||
> - Alpha in 2 weeks (hard date, investor demo)
|
||||
> - Full crafting requires 3 weeks
|
||||
> - Investor demo is make-or-break for funding
|
||||
>
|
||||
> ## Consequences
|
||||
> Positive:
|
||||
> - Makes Alpha deadline, preserves investor trust
|
||||
> - Both pillars demonstrated (rough but playable)
|
||||
> - Can show Alpha→Beta improvement narrative
|
||||
>
|
||||
> Negative:
|
||||
> - First impression is "rough"
|
||||
> - Limited recipe variety
|
||||
> - Missing progressive unlock system
|
||||
>
|
||||
> ## Validation Criteria
|
||||
> We'll know this was the right choice if:
|
||||
> - Investor demo successfully shows both pillars
|
||||
> - Funding secured for next development phase
|
||||
> - Post-Alpha sprint can implement full system without rework
|
||||
> ```
|
||||
>
|
||||
> **Updated GDD Markers:**
|
||||
> ```markdown
|
||||
> ## Detailed Rules
|
||||
>
|
||||
> ### Core Loop (**ALPHA SCOPE**)
|
||||
> [Core mechanic description]
|
||||
>
|
||||
> ### Progressive Unlocks (**POST-ALPHA**)
|
||||
> [Skill-based hint system - deferred]
|
||||
>
|
||||
> ### Recipe Catalog
|
||||
> - Alpha: 10 recipes (demonstrates tag variety)
|
||||
> - Beta: 50 recipes (full content)
|
||||
> ```
|
||||
>
|
||||
> **Demo Script Guidance:**
|
||||
> ```markdown
|
||||
> ## Presenting Crafting to Investor (10-recipe Alpha)
|
||||
>
|
||||
> **Frame it as proof-of-concept:**
|
||||
> "This is a vertical slice of the crafting pillar. You'll see 10 recipes
|
||||
> here, but the SYSTEM supports unlimited recipes through tag combinations.
|
||||
> Watch how players deduce patterns..."
|
||||
>
|
||||
> **Show, don't tell:**
|
||||
> - Live demo: Discover 2 recipes on the fly
|
||||
> - Explain: "Notice Fire+Water made Steam Potion. Now the player knows
|
||||
> Fire+Water is valid. They can deduce that Fire+Earth might work..."
|
||||
> - Connect to pillar: "This is Emergent Discovery - the game teaches rules,
|
||||
> players become scientists."
|
||||
>
|
||||
> **Roadmap slide:**
|
||||
> "Alpha: 10 recipes (proof of concept)
|
||||
> Beta: 50 recipes (content expansion)
|
||||
> Release: 100+ recipes (includes community-designed recipes)"
|
||||
>
|
||||
> **Emphasize systems:**
|
||||
> "We're not making a content game, we're making a systems game. The tag
|
||||
> system can generate endless combinations. We're building a platform for
|
||||
> discovery, not a recipe database."
|
||||
> ```
|
||||
>
|
||||
> May I proceed to write these documents?
|
||||
|
||||
## Turn 8: User Approves and Agent Completes
|
||||
|
||||
**User:**
|
||||
> Yes, create all those documents and also notify the gameplay-programmer about the scope change.
|
||||
|
||||
**Agent:**
|
||||
> ✅ Complete! Created/Updated:
|
||||
> - `docs/architecture/adr-007-alpha-crafting-scope.md`
|
||||
> - `design/gdd/crafting-system.md` (added Alpha/Beta scope markers)
|
||||
> - `production/milestones/alpha.md` (updated success criteria)
|
||||
> - `production/demo-script-investor-alpha.md` (new file with presentation guide)
|
||||
>
|
||||
> **Notifications Sent:**
|
||||
>
|
||||
> To `gameplay-programmer`:
|
||||
> > "Crafting scope simplified for Alpha: Core tag mechanic + 10 recipes only.
|
||||
> > Progressive unlocks and full recipe set deferred to post-Alpha. Updated
|
||||
> > GDD has implementation guidance. Estimated 1.5 weeks to implement Alpha scope."
|
||||
>
|
||||
> To `producer`:
|
||||
> > "Strategic decision made: Simplified crafting to make Alpha deadline.
|
||||
> > ADR-007 documents rationale. Sprint plan needs updating to reflect new scope."
|
||||
>
|
||||
> **Next Steps:**
|
||||
> 1. Gameplay-programmer can start implementing simplified crafting
|
||||
> 2. You can practice demo script before investor meeting
|
||||
> 3. Producer will update sprint plan with new task breakdown
|
||||
>
|
||||
> **Validation:**
|
||||
> We'll know this decision was correct when:
|
||||
> - Alpha ships on time with both pillars playable
|
||||
> - Investor demo successfully communicates the vision
|
||||
> - Funding secured for continued development
|
||||
>
|
||||
> Good luck with the demo! This was the right call - vision over polish for this audience.
|
||||
|
||||
---
|
||||
|
||||
## Session Summary
|
||||
|
||||
**What Worked Well:**
|
||||
- Creative director read context docs before proposing solutions
|
||||
- Asked 5 clarifying questions to understand constraints
|
||||
- Framed decision properly (what's at stake, evaluation criteria)
|
||||
- Presented 3 distinct options with honest pros/cons
|
||||
- Made clear recommendation with reasoning, but deferred to user
|
||||
- Documented decision thoroughly with ADR and updated all affected docs
|
||||
- Provided demo script to help user present the decision positively
|
||||
|
||||
**Key Collaborative Moments:**
|
||||
- Turn 2: Agent gathered full context before jumping to solutions
|
||||
- Turn 3: User provided decision criteria priority (funding > schedule > polish)
|
||||
- Turn 5: Agent presented 3 options, made recommendation, but explicitly "this is your call"
|
||||
- Turn 6: User made strategic decision
|
||||
- Turn 7: Agent provided demo script to support user's presentation
|
||||
- Turn 8: Agent cascaded decision to affected parties
|
||||
|
||||
**Strategic Decision Quality:**
|
||||
- User made the final call
|
||||
- Agent provided expert analysis using game dev theory and precedent
|
||||
- Trade-offs were explicit and honest
|
||||
- Decision was documented for future reference
|
||||
- Success criteria defined for validation
|
||||
|
||||
**Files Created/Updated:**
|
||||
- ADR-007 (decision record)
|
||||
- Updated crafting GDD with scope markers
|
||||
- Updated Alpha milestone definition
|
||||
- Demo script for investor presentation
|
||||
|
||||
**Context Usage:** ~30%
|
||||
**User Satisfaction:** High - confident decision made with full information
|
||||
245
docs/examples/session-story-lifecycle.md
Normal file
245
docs/examples/session-story-lifecycle.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# Example Session: Full Story Lifecycle — /story-readiness → Implement → /story-done
|
||||
|
||||
**Context:** Solo indie dev, mid-production sprint. The movement system GDD and architecture are complete. A story for movement's core implementation has been generated by `/create-stories`.
|
||||
|
||||
**Starting State:**
|
||||
- `design/gdd/movement-system.md` — complete, all 8 sections
|
||||
- `docs/architecture/movement-adr-001.md` — Status: Accepted
|
||||
- `production/sprints/sprint-03.md` — lists STORY-MOV-001 in backlog
|
||||
- `production/stories/STORY-MOV-001.md` — exists but not yet validated
|
||||
- No implementation code yet
|
||||
|
||||
**Session Duration:** ~50 minutes
|
||||
**Turns:** 13
|
||||
**Outcome:** Story validated ready, implemented, verified complete with `/story-done`
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Story Readiness Check
|
||||
|
||||
### Turn 1: User Runs Readiness Check
|
||||
|
||||
**User:**
|
||||
> /story-readiness production/stories/STORY-MOV-001.md
|
||||
|
||||
### Turn 2: Agent Validates the Story
|
||||
|
||||
**Agent (via /story-readiness):**
|
||||
> Reading `production/stories/STORY-MOV-001.md`...
|
||||
>
|
||||
> **STORY-MOV-001: Implement CharacterBody2D movement with terrain modifiers**
|
||||
>
|
||||
> Running 4 validation checks:
|
||||
>
|
||||
> **1. Design Completeness**
|
||||
> - GDD referenced: `design/gdd/movement-system.md` ✅
|
||||
> - GDD section coverage: Detailed Rules ✅, Formulas ✅, Edge Cases ✅
|
||||
> - TR-IDs embedded: TR-MOV-001 through TR-MOV-008 ✅
|
||||
>
|
||||
> **2. Architecture Completeness**
|
||||
> - ADR referenced: `docs/architecture/movement-adr-001.md` ✅
|
||||
> - ADR status: **Accepted** ✅ *(Would be BLOCKED if Proposed)*
|
||||
> - Control manifest version in story: `2026-03-10` — current manifest is `2026-03-10` ✅
|
||||
>
|
||||
> **3. Scope Clarity**
|
||||
> - Acceptance criteria: 9 items, all measurable ✅
|
||||
> - Out-of-scope items listed: swimming, climbing ✅
|
||||
> - Ambiguities found: **1**
|
||||
> - ⚠️ Story says "roll direction follows last input direction" but GDD says "roll in movement direction" — these conflict when player stops and immediately rolls. Clarify before pickup.
|
||||
>
|
||||
> **4. Definition of Done**
|
||||
> - Tests required: unit tests for formula functions ✅
|
||||
> - Integration signal: `on_stamina_event` must fire correctly ✅
|
||||
>
|
||||
> ---
|
||||
> **Verdict: NEEDS WORK**
|
||||
>
|
||||
> One ambiguity must be resolved before implementation starts. Would you like to resolve it now so I can mark the story as ready?
|
||||
|
||||
### Turn 3: User Resolves the Ambiguity
|
||||
|
||||
**User:**
|
||||
> Roll uses last movement direction. If player is standing still, roll uses facing direction instead.
|
||||
|
||||
### Turn 4: Agent Updates the Story
|
||||
|
||||
**Agent:**
|
||||
> Updating the story's Detailed Rules section:
|
||||
>
|
||||
> > Roll direction: last movement direction. If velocity is zero at roll input, roll uses current facing direction.
|
||||
>
|
||||
> May I write this to `production/stories/STORY-MOV-001.md` and set Status to `ready-for-dev`?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes update. Story status: `ready-for-dev`.**
|
||||
|
||||
> **Verdict: READY**
|
||||
> Story is implementation-ready. Pick it up when you're ready to build.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Implementation
|
||||
|
||||
### Turn 5: User Starts Implementation
|
||||
|
||||
**User:**
|
||||
> Let's implement STORY-MOV-001. I'm ready to start coding.
|
||||
|
||||
### Turn 6: Agent Reads Story and ADR
|
||||
|
||||
**Agent (gameplay-programmer):**
|
||||
> Reading `production/stories/STORY-MOV-001.md`, `design/gdd/movement-system.md`, and `docs/architecture/movement-adr-001.md`...
|
||||
>
|
||||
> **Architecture (from ADR-001):**
|
||||
> - `MovementComponent.gd` — attached to player scene, not a singleton
|
||||
> - Terrain modifier lookup via `TerrainLayer.get_modifier(tile_position)` helper
|
||||
> - Stamina events via signal (not direct call to StaminaSystem)
|
||||
>
|
||||
> **My proposed file structure:**
|
||||
> ```
|
||||
> src/gameplay/movement/
|
||||
> movement_component.gd # CharacterBody2D logic, state machine
|
||||
> terrain_modifier_table.gd # Const table: terrain_type -> {speed_mod, drain_mod}
|
||||
> tests/unit/
|
||||
> test_movement_formulas.gd # Unit tests for speed/drain calculations
|
||||
> ```
|
||||
>
|
||||
> May I proceed with this structure?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
### Turn 7: Agent Implements and Shows Key Code
|
||||
|
||||
**Agent:**
|
||||
> Implementing `movement_component.gd`. Key excerpt for your review before I write the file:
|
||||
>
|
||||
> ```gdscript
|
||||
> class_name MovementComponent
|
||||
> extends Node
|
||||
>
|
||||
> signal stamina_event(type: String, amount: float)
|
||||
>
|
||||
> @export var base_walk_speed: float = 120.0
|
||||
> @export var run_multiplier: float = 1.7
|
||||
> @export var stamina_drain_walk: float = 2.0
|
||||
> @export var stamina_drain_run: float = 6.0
|
||||
> @export var stamina_cost_roll: float = 15.0
|
||||
>
|
||||
> var _is_carrying_heavy: bool = false
|
||||
>
|
||||
> func _physics_process(delta: float) -> void:
|
||||
> var terrain_mod := _get_terrain_modifier()
|
||||
> var effective_speed := _calculate_speed(terrain_mod)
|
||||
> # ...
|
||||
> ```
|
||||
>
|
||||
> All values are `@export` — none hardcoded. Is this the direction you want?
|
||||
|
||||
**User:**
|
||||
> Looks good. Write it.
|
||||
|
||||
### Turn 8–9: Agent Writes Files and Tests
|
||||
|
||||
**Agent writes:**
|
||||
- `src/gameplay/movement/movement_component.gd`
|
||||
- `src/gameplay/movement/terrain_modifier_table.gd`
|
||||
- `tests/unit/test_movement_formulas.gd`
|
||||
|
||||
**Agent:**
|
||||
> Files written. Running available linting checks...
|
||||
>
|
||||
> No errors. Tests file created with 6 unit tests covering:
|
||||
> - Walk speed formula (3 terrain types)
|
||||
> - Run speed formula
|
||||
> - Stamina drain calculation
|
||||
> - Roll stamina cost (with and without zero-velocity edge case)
|
||||
>
|
||||
> Manual testing needed for: invincibility frames, slope blocking, terrain integration (requires running scene).
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Story Completion
|
||||
|
||||
### Turn 10: User Tests Manually and Runs /story-done
|
||||
|
||||
**User:**
|
||||
> I've tested it in the scene — everything works. /story-done production/stories/STORY-MOV-001.md
|
||||
|
||||
### Turn 11: Agent Runs Completion Review
|
||||
|
||||
**Agent (via /story-done):**
|
||||
> Reading story, GDD, ADRs...
|
||||
>
|
||||
> **Phase 1: Story Located** ✅ `production/stories/STORY-MOV-001.md`
|
||||
>
|
||||
> **Phase 2: Acceptance Criteria Verification**
|
||||
>
|
||||
> | # | Criterion | Verification | Status |
|
||||
> |---|-----------|--------------|--------|
|
||||
> | 1 | Walk speed correct on normal ground | Auto: formula unit tests pass | ✅ AUTO |
|
||||
> | 2 | Run speed correct | Auto: formula unit tests pass | ✅ AUTO |
|
||||
> | 3 | Run cancels at stamina 0 | Manual: user confirmed | ✅ MANUAL |
|
||||
> | 4 | Roll invincibility frames work | Manual: user confirmed | ✅ MANUAL |
|
||||
> | 5 | Terrain modifiers apply correctly | Manual: user confirmed | ✅ MANUAL |
|
||||
> | 6 | Heavy carry disables run | Auto: not yet testable (inventory not integrated) | ⏳ DEFERRED |
|
||||
> | 7 | Stamina signals fire correctly | Auto: integration test pending | ⏳ DEFERRED |
|
||||
> | 8 | No hardcoded values | Auto: grep for magic numbers → 0 found | ✅ AUTO |
|
||||
> | 9 | Slopes >45° block movement | Manual: user confirmed | ✅ MANUAL |
|
||||
>
|
||||
> **Phase 3: GDD/ADR Deviations**
|
||||
>
|
||||
> Scanning implementation against TR-IDs...
|
||||
>
|
||||
> - TR-MOV-001 through TR-MOV-007: ✅ Implemented as specified
|
||||
> - TR-MOV-008 (heavy carry signal): ⚠️ ADVISORY — deferred to inventory integration story. Not a deviation, just unverifiable now.
|
||||
>
|
||||
> **Phase 4: Manifest Staleness Check**
|
||||
>
|
||||
> Story control manifest version `2026-03-10` matches current manifest `2026-03-10`. ✅
|
||||
>
|
||||
> **Phase 5: Code Review Prompt**
|
||||
>
|
||||
> Suggest running `/code-review src/gameplay/movement/` before marking complete. Not blocking.
|
||||
>
|
||||
> ---
|
||||
> **Completion Report: COMPLETE WITH NOTES**
|
||||
>
|
||||
> Story is complete. Two criteria deferred to inventory integration — log as deferred, not blocked.
|
||||
>
|
||||
> May I update the story status to `Status: Complete` and add Completion Notes, and update `production/sprint-status.yaml`?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
### Turn 12: Agent Closes the Story
|
||||
|
||||
**Agent writes:**
|
||||
- Story `Status: Complete` + Completion Notes (deferred criteria noted)
|
||||
- `production/sprint-status.yaml` — STORY-MOV-001 set to `done`
|
||||
|
||||
**Agent:**
|
||||
> STORY-MOV-001 closed.
|
||||
>
|
||||
> **Deferred criteria logged** (will auto-surface in inventory integration story):
|
||||
> - Heavy carry disables run (requires inventory signal)
|
||||
> - Stamina signal integration test (requires stamina system)
|
||||
>
|
||||
> **Next ready story from sprint:**
|
||||
> - STORY-MOV-002: Stamina system — this is the logical next story (downstream dep now unblocked)
|
||||
> - STORY-HUD-001: Movement HUD indicators — also unblocked
|
||||
>
|
||||
> Run `/story-readiness` on whichever you pick up next.
|
||||
|
||||
---
|
||||
|
||||
## What This Example Demonstrates
|
||||
|
||||
- **Readiness gate catches real issues**: the roll direction ambiguity would have caused a late implementation decision without `/story-readiness`
|
||||
- **ADR status is a hard gate**: if `movement-adr-001.md` had been `Proposed` instead of `Accepted`, story would be `BLOCKED` and implementation wouldn't start
|
||||
- **Deferred criteria**: not every acceptance criterion can be verified at story close — `/story-done` tracks deferred items rather than blocking completion
|
||||
- **TR-ID references**: story stores `TR-MOV-001` IDs, not quoted GDD text — avoids false deviation flags when GDD wording is cleaned up later
|
||||
- **sprint-status.yaml**: single source of truth for sprint state, updated by `/story-done` and read by `/sprint-status`
|
||||
- **Manifest version check**: ensures story's implementation guidance hasn't drifted from current architecture decisions
|
||||
210
docs/examples/session-ux-pipeline.md
Normal file
210
docs/examples/session-ux-pipeline.md
Normal file
@@ -0,0 +1,210 @@
|
||||
# Example Session: UX Pipeline — /ux-design → /ux-review → /team-ui
|
||||
|
||||
**Context:** Solo indie dev, Pre-Production phase. The HUD and inventory screen are the first UI to design. The game has a player journey doc and GDDs for inventory and combat.
|
||||
|
||||
**Starting State:**
|
||||
- `design/gdd/inventory-system.md` — complete
|
||||
- `design/gdd/combat-system.md` — complete
|
||||
- `design/player-journey.md` — complete (6-phase arc defined)
|
||||
- No UX specs yet
|
||||
- `design/ux/` directory does not exist
|
||||
|
||||
**Session Duration:** ~90 minutes across two sessions (HUD design, then inventory screen)
|
||||
**Turns:** 16
|
||||
**Outcome:** `design/ux/hud.md` and `design/ux/screens/inventory.md` created and reviewed; team-ui handed off for visual implementation
|
||||
|
||||
---
|
||||
|
||||
## Part 1: HUD Design with /ux-design
|
||||
|
||||
### Turn 1: User Starts HUD Design
|
||||
|
||||
**User:**
|
||||
> /ux-design hud
|
||||
|
||||
### Turn 2: Agent Loads Context and Presents the Approach
|
||||
|
||||
**Agent (ux-designer, via /ux-design):**
|
||||
> Starting HUD design. Loading context...
|
||||
>
|
||||
> - `design/player-journey.md` read — key states: Calm Exploration, Tension (threat detected), Crisis (low HP/stamina), Combat
|
||||
> - `design/gdd/combat-system.md` — HUD needs: HP bar, stamina bar, active ability cooldowns (up to 3), damage numbers
|
||||
> - `design/gdd/inventory-system.md` — HUD needs: quick slots (4 slots), active item indicator
|
||||
>
|
||||
> **HUD Philosophy question:** What is the player's primary attention focus during play?
|
||||
>
|
||||
> This determines how aggressive the HUD should be:
|
||||
> - **Option A: Diegetic (low presence)** — HP shown only when damaged, stamina shown only when draining. World-first, HUD recedes unless urgent.
|
||||
> - **Option B: Persistent minimal** — HP and stamina always visible, small and unobtrusive. Quick reference without mode-switching.
|
||||
> - **Option C: Full tactical** — All info always visible (HP, stamina, cooldowns, map, inventory slots). Player always has full awareness.
|
||||
>
|
||||
> Given your survival game and "lone survivor" fantasy, A or B is most consistent with the genre.
|
||||
|
||||
**User:**
|
||||
> B — I want it always there but minimal. The player is always managing resources.
|
||||
|
||||
### Turn 3: Agent Drafts HUD Philosophy Section
|
||||
|
||||
**Agent:**
|
||||
> **HUD Philosophy draft:**
|
||||
>
|
||||
> > **Approach: Persistent Minimal**
|
||||
> > The HUD is always visible but occupies minimal screen real estate. The player is a resource-managing survivor — denying HP/stamina visibility creates frustration, not tension. All critical stats are readable at a glance without interrupting exploration focus. Elements scale in visual weight when they become urgent (HP below 30% pulses, stamina at 0 flashes), then return to minimal state.
|
||||
>
|
||||
> May I write this to `design/ux/hud.md` as the Philosophy section?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent creates `design/ux/hud.md` and writes Philosophy section.**
|
||||
|
||||
*(Turns 4–7 continue the section-by-section HUD authoring: Info Architecture, Zones, Element Specs, State Machine, Visual Budget, Platform Adaptation — each drafted, approved, and written to file.)*
|
||||
|
||||
---
|
||||
|
||||
**Final HUD structure after all sections:**
|
||||
|
||||
```
|
||||
design/ux/hud.md
|
||||
- Philosophy: Persistent Minimal
|
||||
- Info Architecture: HP (top-left), Stamina (top-left below HP), Quick Slots (bottom-center),
|
||||
Ability Cooldowns (bottom-right), Damage Numbers (world-space)
|
||||
- Zones: 4 defined (top-left vitals, bottom-center interaction, bottom-right abilities, world-space)
|
||||
- Element Specs: 6 elements with dimensions, font sizes, opacity rules
|
||||
- State Machine: Calm / Tension / Crisis / Combat (4 states, transition triggers)
|
||||
- Visual Budget: Max 8% screen coverage, 3 simultaneous animations
|
||||
- Platform Adaptation: Controller (aim reticle added), Mobile (out of scope v1)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Part 2: Inventory Screen with /ux-design
|
||||
|
||||
### Turn 8: User Starts Inventory Screen
|
||||
|
||||
**User:**
|
||||
> /ux-design screen inventory
|
||||
|
||||
### Turn 9: Agent Scans GDD and Player Journey
|
||||
|
||||
**Agent:**
|
||||
> Loading context for inventory screen...
|
||||
>
|
||||
> From `design/gdd/inventory-system.md`:
|
||||
> - Grid inventory, 6×8 slots
|
||||
> - Items have weight (affects movement) and stack size
|
||||
> - Player can equip, drop, inspect, split stacks
|
||||
>
|
||||
> From `design/player-journey.md`:
|
||||
> - Inventory opens frequently during Calm Exploration
|
||||
> - Players should feel "organized and in control" when opening inventory
|
||||
> - Anxiety moment: "I need to decide what to drop" — should be clear, not panicked
|
||||
>
|
||||
> **Player Need (from journey):** *I need to quickly assess what I'm carrying, decide what to keep or drop, and get back into the world.*
|
||||
>
|
||||
> **Key design question:** How do players navigate to inventory actions?
|
||||
> - **Option A: Right-click context menu** — hover item → right-click → menu appears (Escape/common)
|
||||
> - **Option B: Drag-based** — drag to drop zone to drop; drag to equipment slot to equip (Stardew Valley)
|
||||
> - **Option C: Inspect panel** — click item → details panel appears on right with action buttons
|
||||
|
||||
**User:**
|
||||
> B — drag-based. I want it tactile.
|
||||
|
||||
*(Turns 10–12 continue section-by-section for the inventory screen spec: Player Need, Layout Zones, States, Interaction Map, Data Requirements, Events Fired, Accessibility, Localization.)*
|
||||
|
||||
---
|
||||
|
||||
## Part 3: /ux-review
|
||||
|
||||
### Turn 13: User Runs Review
|
||||
|
||||
**User:**
|
||||
> /ux-review design/ux/
|
||||
|
||||
### Turn 14: Agent Reviews Both Documents
|
||||
|
||||
**Agent (via /ux-review):**
|
||||
> Reviewing all UX specs in `design/ux/`...
|
||||
>
|
||||
> ---
|
||||
> **HUD Review (`hud.md`):**
|
||||
>
|
||||
> | Check | Status | Notes |
|
||||
> |-------|--------|-------|
|
||||
> | GDD alignment — all required elements present | ✅ | HP, stamina, quick slots, cooldowns all specced |
|
||||
> | State machine coverage | ✅ | All 4 gameplay states covered |
|
||||
> | Accessibility tier | ⚠️ | No colorblind mode specified for HP/stamina bars (red/yellow convention) |
|
||||
> | Visual budget | ✅ | 8% screen coverage, within budget |
|
||||
> | Platform notes | ✅ | Controller variant documented |
|
||||
>
|
||||
> **Inventory Screen Review (`screens/inventory.md`):**
|
||||
>
|
||||
> | Check | Status | Notes |
|
||||
> |-------|--------|-------|
|
||||
> | Player Need stated | ✅ | |
|
||||
> | All GDD actions covered | ✅ | Equip, drop, inspect, split all in interaction map |
|
||||
> | Drag-drop interaction fully specified | ⚠️ | Drop target behavior when inventory is full not defined — what happens? |
|
||||
> | Accessibility: keyboard alternative for drag | ❌ | MISSING — drag-only with no keyboard path fails motor accessibility |
|
||||
> | Localization: all strings tagged | ✅ | |
|
||||
> | Data events fired | ✅ | `item_equipped`, `item_dropped`, `stack_split` all mapped |
|
||||
>
|
||||
> ---
|
||||
> **Verdict: NEEDS REVISION**
|
||||
>
|
||||
> Two issues must be resolved before handoff to /team-ui:
|
||||
>
|
||||
> 1. **BLOCKING (Accessibility):** Inventory screen has no keyboard alternative to drag-drop. Motor accessibility requires a non-drag interaction path. Add a "press X to pick up / press X again to place" toggle, or a details panel with keyboard-accessible action buttons.
|
||||
>
|
||||
> 2. **ADVISORY (HUD):** HP/stamina bars use color alone to convey state. Add a secondary indicator (shape, icon, or pattern) for colorblind users. Can be resolved in visual pass.
|
||||
> 3. **ADVISORY (Inventory):** Full-inventory drop behavior is unspecified. Resolve in GDD before implementation, or document as out-of-scope.
|
||||
|
||||
### Turn 15: User Resolves Blocking Issue
|
||||
|
||||
**User:**
|
||||
> For keyboard: press F to pick up item at cursor, press F again on target slot to place it. Add this to the inventory spec.
|
||||
|
||||
**Agent:**
|
||||
> Adding to Interaction Map under "Keyboard / Controller Path":
|
||||
>
|
||||
> > Press F (or South button) to pick up item at cursor. Item enters "held" state with visual indicator. Press F again on target slot to place. Press Escape to cancel and return item.
|
||||
>
|
||||
> May I write this to `design/ux/screens/inventory.md` and re-run the accessibility check?
|
||||
|
||||
**User:**
|
||||
> Yes
|
||||
|
||||
**Agent writes update. Re-checks accessibility — now passes.**
|
||||
|
||||
---
|
||||
|
||||
## Part 4: Handoff to /team-ui
|
||||
|
||||
### Turn 16: User Kicks Off Team UI
|
||||
|
||||
**User:**
|
||||
> /team-ui
|
||||
|
||||
**Agent (via /team-ui):**
|
||||
> Running UX pipeline context check...
|
||||
>
|
||||
> - `design/ux/hud.md` — reviewed ✅
|
||||
> - `design/ux/screens/inventory.md` — reviewed ✅
|
||||
> - `/ux-review` verdict: NEEDS REVISION was resolved — current state: APPROVED ✅
|
||||
>
|
||||
> Proceeding to **Phase 2: Visual Design**
|
||||
>
|
||||
> Handing off to `art-director` for visual spec (color palette, typography, iconography aligned to art bible)...
|
||||
>
|
||||
> *(team-ui continues through visual design → layout implementation → accessibility audit → final review)*
|
||||
|
||||
---
|
||||
|
||||
## What This Example Demonstrates
|
||||
|
||||
- **Context-driven design**: agent reads player-journey.md to ground HUD decisions in player emotional state, not just feature lists
|
||||
- **UX review is a hard gate**: `/team-ui` checks for a passing `/ux-review` before proceeding to visual design
|
||||
- **Accessibility caught early**: the missing keyboard alternative was flagged by review, not discovered by a QA tester in the final week
|
||||
- **Blocking vs. advisory**: the missing keyboard path was BLOCKING (stops handoff); the colorblind colors were ADVISORY (can be fixed in visual pass)
|
||||
- **Section-by-section UX authoring**: same incremental pattern as `/design-system` — each section written to file before moving on
|
||||
- **Separate HUD and screen files**: `design/ux/hud.md` is the whole-game HUD; per-screen specs live in `design/ux/screens/`
|
||||
- **Pattern library enforced by /team-ui**: after this session, inventory drag-drop becomes a documented pattern in `design/ux/interaction-patterns.md` for all future screens to reference
|
||||
362
docs/examples/skill-flow-diagrams.md
Normal file
362
docs/examples/skill-flow-diagrams.md
Normal file
@@ -0,0 +1,362 @@
|
||||
# Skill Flow Diagrams
|
||||
|
||||
Visual maps of how skills chain together across the 7 development phases.
|
||||
These show what runs before and after each skill, and what artifacts flow between them.
|
||||
|
||||
---
|
||||
|
||||
## Full Pipeline Overview (Zero to Ship)
|
||||
|
||||
```
|
||||
PHASE 1: CONCEPT
|
||||
/start ──────────────────────────────────────────────────────► routes to A/B/C/D
|
||||
/brainstorm ──────────────────────────────────────────────────► design/gdd/game-concept.md
|
||||
/setup-engine ────────────────────────────────────────────────► CLAUDE.md + technical-preferences.md
|
||||
/prototype [core-mechanic] ───────────────────────────────────► prototypes/[name]-concept/REPORT.md
|
||||
│ PROCEED (validate idea BEFORE writing GDDs)
|
||||
▼
|
||||
/design-review [game-concept.md] ────────────────────────────► concept validated
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to systems-design
|
||||
│
|
||||
▼
|
||||
PHASE 2: SYSTEMS DESIGN
|
||||
/map-systems ────────────────────────────────────────────────► design/gdd/systems-index.md
|
||||
│
|
||||
▼ (for each system, in dependency order)
|
||||
/design-system [name] ──────────────────────────────────────► design/gdd/[system].md
|
||||
/design-review [system].md ─────────────────────────────────► per-GDD review comments
|
||||
│
|
||||
▼ (after all MVP GDDs done)
|
||||
/review-all-gdds ────────────────────────────────────────────► design/gdd/gdd-cross-review-[date].md
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to technical-setup
|
||||
│
|
||||
▼
|
||||
PHASE 3: TECHNICAL SETUP
|
||||
/create-architecture ────────────────────────────────────────► docs/architecture/master.md
|
||||
/architecture-decision (×N) ─────────────────────────────────► docs/architecture/[adr-nnn].md
|
||||
/architecture-review ────────────────────────────────────────► review report + docs/architecture/tr-registry.yaml
|
||||
/create-control-manifest ────────────────────────────────────► docs/architecture/control-manifest.md
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to pre-production
|
||||
│
|
||||
▼
|
||||
PHASE 4: PRE-PRODUCTION
|
||||
[UX — before epics, so specs exist when stories are written]
|
||||
/ux-design [screen/hud/patterns] ────────────────────────────► design/ux/*.md
|
||||
/ux-review ──────────────────────────────────────────────────► UX specs approved (HARD gate for /team-ui)
|
||||
|
||||
[Test infrastructure — scaffold before stories reference tests]
|
||||
/test-setup ─────────────────────────────────────────────────► test framework + CI/CD pipeline
|
||||
/test-helpers ───────────────────────────────────────────────► tests/helpers/[engine-specific].gd
|
||||
|
||||
[Vertical slice — before epics, validate full game loop]
|
||||
/vertical-slice ─────────────────────────────────────────────► prototypes/[name]-vertical-slice/REPORT.md
|
||||
/playtest-report ────────────────────────────────────────────► production/playtests/
|
||||
|
||||
[Stories + sprint plan — only after vertical slice PROCEEDS]
|
||||
/create-epics [layer] ───────────────────────────────────────► production/epics/*/EPIC.md
|
||||
/create-stories [epic-slug] ─────────────────────────────────► production/epics/*/story-*.md
|
||||
/sprint-plan new ────────────────────────────────────────────► production/sprints/sprint-01.md
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to production
|
||||
│
|
||||
▼
|
||||
PHASE 5: PRODUCTION (repeating sprint loop)
|
||||
/sprint-status ──────────────────────────────────────────────► sprint snapshot
|
||||
/story-readiness [story] ────────────────────────────────────► story validated READY
|
||||
│
|
||||
▼ (pick up and implement)
|
||||
/dev-story [story] ──────────────────────────────────────────► routes to correct programmer agent
|
||||
│
|
||||
▼ (during implementation, as needed)
|
||||
/code-review ────────────────────────────────────────────────► code review report
|
||||
/scope-check ────────────────────────────────────────────────► scope creep detected / clear
|
||||
/content-audit ──────────────────────────────────────────────► GDD content gaps identified
|
||||
/bug-report ─────────────────────────────────────────────────► production/qa/bugs/bug-NNN.md
|
||||
/bug-triage ─────────────────────────────────────────────────► bugs re-prioritized + assigned
|
||||
|
||||
[Team skills for feature areas — spawn when working a full feature]
|
||||
/team-combat / /team-narrative / /team-ui / /team-level / /team-audio
|
||||
|
||||
[QA cycle per sprint]
|
||||
/qa-plan ────────────────────────────────────────────────────► production/qa/qa-plan-sprint-NN.md
|
||||
/smoke-check ────────────────────────────────────────────────► smoke test gate (PASS/FAIL)
|
||||
/regression-suite ───────────────────────────────────────────► coverage gaps + missing regression tests
|
||||
/test-evidence-review ───────────────────────────────────────► evidence quality report
|
||||
/test-flakiness ─────────────────────────────────────────────► flaky test report
|
||||
│
|
||||
▼
|
||||
/story-done [story] ─────────────────────────────────────────► story closed + next surfaced
|
||||
/sprint-plan [next] ─────────────────────────────────────────► next sprint
|
||||
│
|
||||
▼ (after Production milestone)
|
||||
/milestone-review ───────────────────────────────────────────► milestone report
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to polish
|
||||
│
|
||||
▼
|
||||
PHASE 6: POLISH
|
||||
/perf-profile ───────────────────────────────────────────────► perf report + fixes
|
||||
/balance-check ──────────────────────────────────────────────► balance report + fixes
|
||||
/asset-audit ────────────────────────────────────────────────► asset compliance report
|
||||
/tech-debt ──────────────────────────────────────────────────► docs/tech-debt-register.md
|
||||
/soak-test ──────────────────────────────────────────────────► soak test protocol + results
|
||||
/localize ───────────────────────────────────────────────────► localization readiness report
|
||||
/team-polish ────────────────────────────────────────────────► polish sprint orchestrated
|
||||
/team-qa ────────────────────────────────────────────────────► full QA cycle sign-off
|
||||
/gate-check ─────────────────────────────────────────────────► PASS → advance to release
|
||||
│
|
||||
▼
|
||||
PHASE 7: RELEASE
|
||||
/launch-checklist ───────────────────────────────────────────► launch readiness report
|
||||
/release-checklist ──────────────────────────────────────────► platform-specific checklist
|
||||
/changelog ──────────────────────────────────────────────────► CHANGELOG.md
|
||||
/patch-notes ────────────────────────────────────────────────► player-facing notes
|
||||
/team-release ───────────────────────────────────────────────► release pipeline orchestrated
|
||||
│
|
||||
▼ (post-launch, ongoing)
|
||||
/hotfix ─────────────────────────────────────────────────────► emergency fix with audit trail
|
||||
/team-live-ops ──────────────────────────────────────────────► live-ops content plan
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Chain: /design-system in Detail
|
||||
|
||||
How a single GDD gets authored, reviewed, and handed to architecture:
|
||||
|
||||
```
|
||||
systems-index.md (input)
|
||||
game-concept.md (input)
|
||||
upstream GDDs (input, if any)
|
||||
│
|
||||
▼
|
||||
/design-system [name]
|
||||
│
|
||||
├── Pre-check: feasibility table + engine risk flags
|
||||
│
|
||||
├── Section cycle × 8:
|
||||
│ question → options → decision → draft → approval → WRITE
|
||||
│ [each section written to file immediately after approval]
|
||||
│
|
||||
└── Output: design/gdd/[system].md (complete, all 8 sections)
|
||||
│
|
||||
▼
|
||||
/design-review design/gdd/[system].md
|
||||
│
|
||||
├── APPROVED → mark DONE in systems-index, proceed to next system
|
||||
├── NEEDS REVISION → agent shows specific issues, re-enter section cycle
|
||||
└── MAJOR REVISION → significant redesign needed before next system
|
||||
│
|
||||
▼ (after all MVP GDDs + cross-review)
|
||||
/review-all-gdds
|
||||
│
|
||||
└── Output: gdd-cross-review-[date].md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Chain: UX / UI Pipeline in Detail
|
||||
|
||||
UX specs are authored in Phase 4 (Pre-Production), before epics are written, so
|
||||
that story acceptance criteria can reference specific UX artifacts.
|
||||
|
||||
```
|
||||
design/gdd/*.md (UI/UX requirements extracted)
|
||||
design/player-journey.md (emotional arc, if authored)
|
||||
│
|
||||
▼
|
||||
/ux-design hud → design/ux/hud.md
|
||||
/ux-design screen [name] → design/ux/screens/[name].md
|
||||
/ux-design patterns → design/ux/interaction-patterns.md
|
||||
│
|
||||
▼
|
||||
/ux-review design/ux/
|
||||
│
|
||||
├── APPROVED → UX specs ready, proceed to /create-epics
|
||||
├── NEEDS REVISION → blocking issues listed → fix → re-run review
|
||||
└── MAJOR REVISION → fundamental UX problems → redesign before epics
|
||||
│
|
||||
▼ (after APPROVED — in Phase 5 when implementing UI features)
|
||||
/team-ui
|
||||
│
|
||||
├── Phase 1: /ux-design (if any specs still missing) + /ux-review
|
||||
├── Phase 2: visual design (art-director)
|
||||
├── Phase 3: layout implementation (ui-programmer)
|
||||
├── Phase 4: accessibility audit (accessibility-specialist)
|
||||
└── Phase 5: final review
|
||||
|
||||
Note: /ux-design and /ux-review belong in Phase 4 (Pre-Production).
|
||||
/team-ui belongs in Phase 5 (Production) when a UI feature is being built.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Chain: Dev Story Flow in Detail
|
||||
|
||||
How a story moves from backlog to closed:
|
||||
|
||||
```
|
||||
/story-readiness [story]
|
||||
│
|
||||
├── READY → Status: ready-for-dev → pick up for implementation
|
||||
├── NEEDS WORK → agent shows specific gaps → resolve → re-run readiness
|
||||
└── BLOCKED → ADR still Proposed, or upstream story incomplete
|
||||
│
|
||||
▼ (after READY)
|
||||
/dev-story [story]
|
||||
│
|
||||
├── Reads: story file, linked GDD requirement, ADR decisions, control manifest
|
||||
├── Routes to: gameplay-programmer / engine-programmer / ui-programmer / etc.
|
||||
│
|
||||
└── Implementation begins
|
||||
│
|
||||
▼ (optional, during/after implementation)
|
||||
/code-review → architectural review of changeset
|
||||
/scope-check → verify no scope creep vs. original story criteria
|
||||
/test-evidence-review → validate test files and manual evidence quality
|
||||
│
|
||||
▼
|
||||
/story-done [story]
|
||||
│
|
||||
├── COMPLETE → Status: Complete, sprint-status.yaml updated, next story surfaced
|
||||
├── COMPLETE WITH NOTES → complete but some criteria deferred (logged)
|
||||
└── BLOCKED → acceptance criteria cannot be verified → investigate blocker
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Chain: Story Lifecycle (Backlog to Closed)
|
||||
|
||||
How a story gets from backlog to closed (summary view):
|
||||
|
||||
```
|
||||
/create-epics [layer]
|
||||
│
|
||||
└── Output: production/epics/[slug]/EPIC.md
|
||||
│
|
||||
▼
|
||||
/create-stories [epic-slug]
|
||||
│
|
||||
└── Output: production/epics/[slug]/story-NNN-[slug].md
|
||||
(Status: Ready or Blocked if ADR is Proposed)
|
||||
│
|
||||
▼
|
||||
/story-readiness [story]
|
||||
│
|
||||
├── READY → /dev-story → implement → /story-done
|
||||
├── NEEDS WORK → resolve gaps → re-run
|
||||
└── BLOCKED → fix upstream dependency first
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Skill Chain: QA Pipeline in Detail
|
||||
|
||||
```
|
||||
[Phase 4 — one-time infrastructure setup]
|
||||
/test-setup ────────────────────────────────────────────────────► test framework scaffolded + CI/CD wired
|
||||
/test-helpers ──────────────────────────────────────────────────► tests/helpers/[engine].gd (GDUnit4, NUnit, etc.)
|
||||
|
||||
[Phase 5 — per-sprint QA cycle]
|
||||
/qa-plan [sprint or feature]
|
||||
│
|
||||
├── Reads: story files, GDDs, acceptance criteria
|
||||
├── Classifies each story by test type:
|
||||
│ Logic → automated unit test (BLOCKING)
|
||||
│ Integration → integration test or documented playtest (BLOCKING)
|
||||
│ Visual/Feel → screenshot + lead sign-off (ADVISORY)
|
||||
│ UI → manual walkthrough or interaction test (ADVISORY)
|
||||
│ Config/Data → smoke check (ADVISORY)
|
||||
└── Output: production/qa/qa-plan-sprint-NN.md
|
||||
│
|
||||
▼
|
||||
/smoke-check
|
||||
│
|
||||
├── PASS → QA hand-off cleared
|
||||
└── FAIL → block sprint close → fix critical paths first
|
||||
│
|
||||
▼
|
||||
/regression-suite
|
||||
│
|
||||
└── Coverage gaps + list of fixed bugs without regression tests
|
||||
│
|
||||
▼
|
||||
/test-evidence-review
|
||||
│
|
||||
└── Validates evidence quality, not just existence
|
||||
│
|
||||
▼ (if CI run history available)
|
||||
/test-flakiness
|
||||
│
|
||||
└── Flaky test report + fix recommendations
|
||||
|
||||
[Phase 6 — extended stability testing]
|
||||
/soak-test ─────────────────────────────────────────────────────► soak test protocol + observed results
|
||||
/team-qa ───────────────────────────────────────────────────────► full QA cycle sign-off for release gate
|
||||
|
||||
[Ongoing — bug management]
|
||||
/bug-report ────────────────────────────────────────────────────► production/qa/bugs/bug-NNN.md
|
||||
/bug-triage ────────────────────────────────────────────────────► open bugs re-prioritized + assigned
|
||||
|
||||
[Meta — harness validation]
|
||||
/skill-test [lint|spec|catalog] ────────────────────────────────► skill file structural + behavioral check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Brownfield Onboarding Flow
|
||||
|
||||
For projects with existing work (use `/start` option D or run directly):
|
||||
|
||||
```
|
||||
/project-stage-detect → stage detection report
|
||||
│
|
||||
▼
|
||||
/adopt
|
||||
│
|
||||
├── Phase 1: detect what exists
|
||||
├── Phase 2: FORMAT audit (not just existence)
|
||||
├── Phase 3: classify gaps (BLOCKING / HIGH / MEDIUM / LOW)
|
||||
├── Phase 4: ordered migration plan
|
||||
├── Phase 5: write docs/adoption-plan-[date].md
|
||||
└── Phase 6: fix most urgent gap inline (optional)
|
||||
│
|
||||
▼
|
||||
/design-system retrofit [path] → fills missing GDD sections
|
||||
/architecture-decision retrofit [path] → fills missing ADR sections
|
||||
/gate-check → where are you in the pipeline?
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How to Read These Diagrams
|
||||
|
||||
| Symbol | Meaning |
|
||||
|--------|---------|
|
||||
| `──►` | Produces this artifact |
|
||||
| `│ ▼` | Flows into next step |
|
||||
| `├──` | Branch (multiple possible outcomes) |
|
||||
| `×N` | Runs N times (once per system, story, etc.) |
|
||||
| `(input)` | Read by the skill but not produced here |
|
||||
| `[optional]` | Not required for the gate to pass |
|
||||
| `WRITE` (caps) | File written to disk immediately |
|
||||
|
||||
---
|
||||
|
||||
## Common Entry Points
|
||||
|
||||
| Where you are | Run this |
|
||||
|---------------|---------|
|
||||
| Brand new, no idea | `/start` → `/brainstorm` |
|
||||
| Have a concept, no engine | `/setup-engine` |
|
||||
| Have concept + engine | `/map-systems` |
|
||||
| Mid-systems design | `/design-system [next system]` or `/map-systems next` |
|
||||
| All GDDs done | `/review-all-gdds` → `/gate-check` |
|
||||
| In technical setup | `/create-architecture` → `/architecture-decision` |
|
||||
| Starting UX design | `/ux-design screen [name]` or `/ux-design hud` |
|
||||
| Scaffolding tests | `/test-setup` → `/test-helpers` |
|
||||
| Have stories, ready to code | `/story-readiness [story]` → `/dev-story [story]` |
|
||||
| Story done | `/story-done [story]` |
|
||||
| Running QA for a sprint | `/qa-plan` → `/smoke-check` → `/regression-suite` |
|
||||
| Bug backlog needs sorting | `/bug-triage` |
|
||||
| Extended stability testing | `/soak-test` |
|
||||
| Not sure | `/help` |
|
||||
| Existing project | `/adopt` |
|
||||
181
docs/registry/architecture.yaml
Normal file
181
docs/registry/architecture.yaml
Normal file
@@ -0,0 +1,181 @@
|
||||
# Architecture Decision Registry
|
||||
#
|
||||
# PURPOSE: Single source of truth for key architectural stances that all ADRs
|
||||
# must agree on. Skills check this before writing new ADRs to detect conflicts
|
||||
# at authoring time — before stories are created against contradictory decisions.
|
||||
#
|
||||
# RULES:
|
||||
# - Register a stance when it constrains how OTHER systems must be built.
|
||||
# An internal implementation detail that affects only one ADR does not need
|
||||
# to be registered.
|
||||
# - Never delete entries — set status: superseded_by: ADR-NNNN instead.
|
||||
# - When a stance changes: update the entry, set revised: to today's date,
|
||||
# add a comment with the old value, and run /architecture-review to check
|
||||
# what ADRs are now invalidated.
|
||||
# - adr: is the authoritative source. All other ADRs that depend on this
|
||||
# stance list themselves in referenced_by.
|
||||
#
|
||||
# WRITTEN BY: /architecture-decision (Phase 5 — after ADR is approved)
|
||||
# READ BY: /architecture-decision (Step 2 — before authoring begins)
|
||||
# /architecture-review (Phase 4 — cross-ADR conflict baseline)
|
||||
# /create-stories (to embed architectural constraints in stories)
|
||||
# /dev-story (to check implementation against accepted stances)
|
||||
#
|
||||
# SEARCH PATTERNS (for skills using Grep):
|
||||
# All state owners: Grep pattern="^ - state:" path="docs/registry/architecture.yaml"
|
||||
# Who owns player_health: Grep pattern="state: player_health"
|
||||
# All signal contracts: Grep pattern="pattern: signal"
|
||||
# Budget for combat: Grep pattern="system: combat"
|
||||
# All forbidden patterns: Grep pattern="^ - pattern:" (forbidden_patterns section)
|
||||
# Superseded entries: Grep pattern="status: superseded"
|
||||
|
||||
version: 1
|
||||
last_updated: ""
|
||||
|
||||
# ─── STATE OWNERSHIP ─────────────────────────────────────────────────────────
|
||||
# Who is the authoritative owner of each piece of shared game state.
|
||||
# Conflicts: two ADRs claiming to own the same state = data race / authority bug.
|
||||
#
|
||||
# Register state when it is READ or WRITTEN by more than one system.
|
||||
# Internal-only state (never accessed by another system) does not need to be registered.
|
||||
#
|
||||
# Required fields: state, owner_system, adr, interface, referenced_by[], added
|
||||
# interface: how other systems access this state (read-only property, method call,
|
||||
# signal, shared resource, etc.) — this is the contract other ADRs depend on.
|
||||
|
||||
state_ownership: []
|
||||
|
||||
# Example:
|
||||
#
|
||||
# state_ownership:
|
||||
# - state: player_health
|
||||
# status: active # active | superseded_by: ADR-NNNN
|
||||
# owner_system: health-system
|
||||
# adr: docs/architecture/adr-0001-health-system.md
|
||||
# interface: "HealthComponent.current_health (read-only float, range 0–max_health)"
|
||||
# write_access: health-system-only # only owner can write; others read
|
||||
# referenced_by:
|
||||
# - docs/architecture/adr-0001-health-system.md
|
||||
# - docs/architecture/adr-0003-combat-system.md # reads to apply damage
|
||||
# - docs/architecture/adr-0007-ui-system.md # reads to display HUD
|
||||
# added: 2026-03-26
|
||||
# revised: ""
|
||||
|
||||
|
||||
# ─── INTERFACE CONTRACTS ──────────────────────────────────────────────────────
|
||||
# How systems are required to communicate with each other.
|
||||
# Conflicts: one ADR expects signal, another expects direct call = integration bug.
|
||||
#
|
||||
# Register a contract when the communication pattern between two systems is
|
||||
# explicitly decided in an ADR and other systems must follow the same pattern.
|
||||
#
|
||||
# pattern options: signal | direct_call | event_bus | shared_resource | rpc | none
|
||||
|
||||
interfaces: []
|
||||
|
||||
# Example:
|
||||
#
|
||||
# interfaces:
|
||||
# - contract: damage_delivery
|
||||
# status: active
|
||||
# pattern: signal # how the producer notifies consumers
|
||||
# producer: combat-system
|
||||
# consumers:
|
||||
# - health-system # receives damage_dealt signal, applies to health
|
||||
# - ui-system # receives damage_dealt signal, shows damage number
|
||||
# - audio-system # receives damage_dealt signal, plays SFX
|
||||
# adr: docs/architecture/adr-0003-combat-system.md
|
||||
# signal_signature: "damage_dealt(amount: float, target: Node, is_crit: bool)"
|
||||
# referenced_by:
|
||||
# - docs/architecture/adr-0003-combat-system.md
|
||||
# - docs/architecture/adr-0001-health-system.md
|
||||
# added: 2026-03-26
|
||||
# revised: ""
|
||||
|
||||
|
||||
# ─── PERFORMANCE BUDGETS ──────────────────────────────────────────────────────
|
||||
# Frame time allocations per system, against a defined total frame budget.
|
||||
# Conflicts: allocations that sum to more than the total budget = unshippable.
|
||||
#
|
||||
# Register a budget when an ADR explicitly claims a frame time allocation.
|
||||
# The registry lets /architecture-review verify the total doesn't exceed the target.
|
||||
|
||||
performance_budgets: []
|
||||
|
||||
# Example:
|
||||
#
|
||||
# performance_budgets:
|
||||
# - target_fps: 60
|
||||
# total_frame_budget_ms: 16.6
|
||||
# platform: PC # PC | Console | Mobile | All
|
||||
# adr: docs/architecture/adr-0000-performance-targets.md
|
||||
# added: 2026-03-26
|
||||
# revised: ""
|
||||
#
|
||||
# - system: combat
|
||||
# status: active
|
||||
# budget_ms: 2.0
|
||||
# adr: docs/architecture/adr-0003-combat-system.md
|
||||
# notes: "Includes physics queries, hitbox evaluation, and damage calculation"
|
||||
# referenced_by:
|
||||
# - docs/architecture/adr-0003-combat-system.md
|
||||
# added: 2026-03-26
|
||||
# revised: ""
|
||||
|
||||
|
||||
# ─── API DECISIONS ───────────────────────────────────────────────────────────
|
||||
# Which engine APIs are chosen for which purpose across the project.
|
||||
# Conflicts: two ADRs using different APIs for the same purpose = inconsistent
|
||||
# implementation that breaks when one system expects the other's API.
|
||||
#
|
||||
# Register an API decision when the choice is non-obvious and other ADRs might
|
||||
# make a different choice for the same purpose without knowing this was decided.
|
||||
|
||||
api_decisions: []
|
||||
|
||||
# Example:
|
||||
#
|
||||
# api_decisions:
|
||||
# - purpose: physics_raycast
|
||||
# status: active
|
||||
# api: PhysicsServer3D.space_get_direct_state()
|
||||
# not: RayCast3D node # explicitly banned for this purpose
|
||||
# adr: docs/architecture/adr-0002-physics-architecture.md
|
||||
# reason: "Direct server calls avoid per-frame node overhead; 3× faster in profiling"
|
||||
# referenced_by:
|
||||
# - docs/architecture/adr-0002-physics-architecture.md
|
||||
# - docs/architecture/adr-0003-combat-system.md
|
||||
# added: 2026-03-26
|
||||
# revised: ""
|
||||
|
||||
|
||||
# ─── FORBIDDEN PATTERNS ──────────────────────────────────────────────────────
|
||||
# Architecture anti-patterns explicitly banned by accepted ADRs.
|
||||
# When writing a new ADR, check this list — if the proposed approach matches
|
||||
# a forbidden pattern, it must be reworked before the ADR can be Accepted.
|
||||
#
|
||||
# Register a pattern when an ADR explicitly bans it AND other ADRs might
|
||||
# unknowingly use it (i.e., it's a tempting but wrong approach for this project).
|
||||
|
||||
forbidden_patterns: []
|
||||
|
||||
# Example:
|
||||
#
|
||||
# forbidden_patterns:
|
||||
# - pattern: autoload_singleton_coupling
|
||||
# status: active
|
||||
# description: "Systems must not directly reference Autoload singletons by name.
|
||||
# Use dependency injection or signals instead."
|
||||
# why: "Tight coupling to Autoloads makes systems untestable in isolation and
|
||||
# creates hidden dependencies that break when Autoload load order changes."
|
||||
# adr: docs/architecture/adr-0001-health-system.md
|
||||
# added: 2026-03-26
|
||||
#
|
||||
# - pattern: direct_cross_system_state_write
|
||||
# status: active
|
||||
# description: "A system must never write to state owned by another system.
|
||||
# Use signals or method calls on the owner instead."
|
||||
# why: "Direct state writes create race conditions and break the state
|
||||
# ownership model. The owning system must be the sole writer."
|
||||
# adr: docs/architecture/adr-0000-architecture-principles.md
|
||||
# added: 2026-03-26
|
||||
Reference in New Issue
Block a user