Files
Ipseity-Demo/Assets/Unheimlich_POC/DEVELOPER_GUIDE_V2.md
T

719 lines
23 KiB
Markdown

# Unheimlich POC - Developer Guide
**Unity Version:** 6000.3
**Last Updated:** January 2025
---
## Table of Contents
1. [Project Overview](#project-overview)
2. [Core Systems](#core-systems)
3. [Quick Start Guides](#quick-start-guides)
4. [Architecture Details](#architecture-details)
5. [Best Practices](#best-practices)
6. [Troubleshooting](#troubleshooting)
---
## Project Overview
This is a first-person horror exploration prototype featuring seamless portals, interactive dialogues, and environmental storytelling. The project uses Unity's Universal Render Pipeline and the New Input System.
### Key Features
- Seamless portal system with recursive rendering
- Node-based dialogue system with gaze interaction
- Custom dialogue editors for rapid content creation
- Dynamic room loading/unloading
- First-person controller with smooth movement
- Interactive objects (doors, items, NPCs)
- Item inspection system
- Environmental audio with portal proxying
### Project Structure
```
/Assets/Unheimlich_POC
├── /Scenes # Game scenes
├── /Scripts # C# scripts organized by namespace
│ ├── /Audio # Audio systems
│ ├── /Data # ScriptableObject definitions
│ ├── /Editor # Editor tools and inspectors
│ ├── /Gameplay # Core gameplay systems
│ │ ├── /Environment # Doors, interactables
│ │ ├── /Items # Inspectable items
│ │ ├── /Player # Player components
│ │ └── /Portals # Portal system
│ ├── /Handlers # Input and interaction handlers
│ ├── /Interactions # Dialogue, focus, floating words
│ ├── /Managers # Singleton managers
│ ├── /UI # UI components
│ └── /Utility # Helper functions
├── /Prefabs # Reusable prefabs
│ ├── /Dialogues # Dialogue triggers and UI
│ ├── /NPCS # NPC prefabs
│ ├── /Player # Player prefab
│ ├── /Portals # Portal prefabs
│ └── /Rooms # Room prefabs for portal loading
├── /Datas # ScriptableObject assets
│ ├── /Dialogues # Conversations and nodes
│ └── /Settings # Animation and system settings
└── /Materials # Materials and shaders
├── /Portal # Portal-specific materials
└── /Shaders # Custom shaders
```
---
## Core Systems
### Portal System
The portal system creates seamless transitions between spaces by rendering real-time views through portal surfaces and teleporting objects that cross the portal plane.
#### How It Works
1. **Rendering**: Each portal has a camera that renders the view from the linked portal's perspective
2. **Display**: The rendered view is projected onto the portal's screen mesh
3. **Teleportation**: Objects crossing the portal plane are teleported to the linked portal
4. **Slicing**: A custom shader prevents visual artifacts by hiding geometry on the wrong side
5. **Room Loading**: Rooms can be dynamically loaded/unloaded based on player distance
#### Key Components
**Portal.cs**
- Main portal logic
- Handles rendering, teleportation, and room loading
- Tracks objects (PortalTravellers) entering the portal
- Manages linked portal connection
**PortalTraveller.cs**
- Required on any object that should teleport through portals
- Handles smooth transition during crossing
- Automatically added to player
**PortalRoomManager.cs**
- Singleton that manages room loading/unloading
- Distance-based loading (default: 15 units)
- Supports room pooling to keep inactive rooms in memory
- Prevents unnecessary load/unload cycles
**PortalDoor.cs**
- Synchronized door between linked portals
- Opening one door opens the corresponding door on the other side
#### Using the Portal Prefab
The `PortalWithDoor.prefab` contains a pre-configured portal ready to use:
1. Drag `PortalWithDoor.prefab` into your scene
2. Set up the portal settings:
- **Linked Portal**: Leave empty if loading a room, or drag another portal for direct linking
- **Room Prefab To Load**: Assign the room prefab you want to load through this portal
- **Room Spawn Point**: Position where the room will be instantiated (relative to portal)
- **Linked Portal Path In Room**: Path to find the linked portal within the loaded room (default: "Portal")
3. Configure the door (optional):
- The door automatically syncs with the linked portal's door
- Adjust door swing direction and speed in the inspector
#### Creating Portal Rooms
Rooms loaded through portals should follow this structure:
```
RoomPrefab
├── Environment (walls, floor, props)
├── Portal (the exit portal back)
│ └── Configure to link back to original portal
└── Lighting (optional)
```
Best practices for room prefabs:
- Position the portal at a logical exit point
- Include nav mesh for AI (if needed)
- Keep the room self-contained (all required objects inside)
- Use relative positioning (portal spawn point defines world position)
#### Portal Settings Explained
**Main Settings**
- **Portal ID**: Unique identifier for this portal (used for dynamic linking)
- **Linked Portal**: Direct reference to the connected portal
- **Screen**: The mesh renderer that displays the portal view
- **Recursion Limit**: How many nested portal views to render (3 is usually enough)
**Advanced Settings**
- **Near Clip Offset**: Distance from portal plane for camera near clip (prevents z-fighting)
- **Near Clip Limit**: Minimum near clip distance (prevents rendering issues)
- **Transition Distance**: Distance from portal to start teleportation check
- **Load Room Distance**: How far ahead of the portal to spawn the room
**Room Loading**
- **Room Prefab To Load**: The room prefab to instantiate
- **Room Spawn Point**: Where to spawn it (uses portal forward direction)
- **Linked Portal Path**: Hierarchy path to find the portal in the spawned room
- **Linked Portal ID**: Alternative ID-based linking
#### Common Portal Scenarios
**Two-Way Portal Between Rooms**
1. Create Portal A in Room A
2. Create Portal B in Room B (make it a prefab)
3. Set Portal A to load Room B prefab
4. Portal B will auto-link to Portal A when Room B spawns
**One-Way Portal (no return)**
- Don't assign a room prefab or linked portal
- Portal only renders, doesn't teleport
- Useful for windows or vistas
**Portal Chain (A → B → C)**
- Each room prefab contains a portal to the next room
- Last room can portal back to start or end the chain
---
### Dialogue System
The dialogue system uses a node-based structure where conversations are built from linked `DialogueNode` assets. Players interact with choices by looking at them (gaze-based selection).
#### Architecture
**DialogueConversation** (ScriptableObject)
- Container for a complete conversation
- References the first DialogueNode to start from
- Can contain metadata like conversation name/description
**DialogueNode** (ScriptableObject)
- Single piece of dialogue with text and choices
- Contains:
- Dialogue text (what the NPC/narrator says)
- List of choices (what the player can respond with)
- Each choice has text and a reference to the next node
- Auto-close settings for end nodes
**DialogueChoiceData** (Serializable Class)
- Embedded in DialogueNode
- Contains choice button text
- Direct reference to next DialogueNode
- Optional condition for advanced filtering
#### Flow
```
Player interacts with trigger
↓
DialogueManager.StartConversation()
↓
Show current node's dialogue text
↓
Spawn floating choice buttons (if any)
↓
DialogueFocusSystem.Update() (raycasts for player gaze)
↓
ChoiceFocusHandler.ApplyFocusFeedback() (visual feedback)
↓
Player focuses on choice for required time
↓
DialogueManager.GoToNextNode()
↓
Repeat or end
```
#### Quick Setup: Using the DialogueTrigger Prefab
The fastest way to add dialogue is using the pre-made `DialogueTrigger.prefab`:
1. **Create Your Conversation**
- Right-click in Project → Create → Unheimlich → Dialogue → Dialogue Conversation
- Name it (e.g., `CONV_WelcomeMessage`)
- Select the conversation asset in the inspector
2. **Use the Conversation Editor**
- The custom editor provides an intuitive interface for building conversations
- Click **"Create New Dialogue Node"** button to add nodes directly
- New nodes are automatically created in the same folder as the conversation
- Nodes are automatically added to the conversation's node list
- The editor shows conversation overview (total nodes, starting node)
3. **Build Dialogue Nodes**
- Select a node asset to edit it
- The custom node editor provides:
- **Node Content**: Dialogue text and localization key
- **Choices**: List of player choices with next node references
- **Add Choice** button for quick choice creation
- **Clear All Choices** to reset a node
- For end nodes (no choices):
- Set auto-close delay (0 = manual close)
- The editor will show "end of conversation" status
- Warnings appear if choices are missing next node references
4. **Connect the Flow**
- Drag node assets into choice "Next Node" fields
- The first node in the conversation list is the starting point
- Use descriptive node names (e.g., `Greeting`, `Quest_Accept`, `Goodbye`)
5. **Add Trigger to Scene**
- Drag `DialogueTrigger.prefab` into your scene
- Position it where you want dialogue to trigger
- Assign your conversation to the trigger component
- Configure trigger type (zone, interact, or NPC)
#### Dialogue Editor Features
The custom editors significantly speed up dialogue creation:
**Conversation Editor (DialogueConversation)**
- One-click node creation with automatic naming
- Nodes created in the same folder as the conversation
- Conversation overview with node count and starting node
- Visual feedback for speaker setup
**Node Editor (DialogueNode)**
- Streamlined choice management (add, remove, clear all)
- Visual warnings for invalid node configuration
- End node detection with auto-close settings
- Localization field support
- Help boxes explain node behavior
**Best Workflow**
1. Create the conversation asset
2. Use "Create New Dialogue Node" to build your flow
3. Edit each node's content and choices
4. Link nodes by dragging references
5. Test in play mode
6. Iterate based on playthrough
#### Creating Custom Dialogue Triggers
If you need more control, you can create triggers from scratch:
**Option 1: Zone Trigger (Automatic)**
```csharp
// Add DialogueTriggerZone component to a GameObject with a trigger collider
// Assign your DialogueConversation asset
// Dialogue starts when player enters the zone
```
**Option 2: Interaction Trigger (Manual)**
```csharp
// Add NPCDialogueTrigger component to an NPC GameObject
// Make sure NPC has the "Interactable" layer
// Player presses E to start dialogue
```
**Option 3: Scripted Trigger (Code)**
```csharp
DialogueManager.Instance.StartConversation(
myConversation, // Your DialogueConversation asset
anchorTransform, // Where to spawn choices (NPC head, trigger position)
animationSettings, // Optional: DialogueAnimationSettings preset
customUI, // Optional: Use specific DialogueUI instance
ownerObject // Optional: Track which object owns this dialogue
);
```
#### Dialogue Node Best Practices
**Text Guidelines**
- Keep dialogue text under 200 characters for readability
- Use line breaks for longer dialogue
- Choice text should be 1-10 words maximum
**Node Structure**
- Use descriptive node names (e.g., "GreetPlayer_Friendly")
- End nodes should have no choices or set NextNode to null
- Branching nodes should have 2-4 choices maximum
**Auto-Close Settings**
- End nodes can auto-close after a delay
- Set `AutoCloseDelay` to 0 for manual close (requires player to walk away or press ESC)
- Set to 3-5 seconds for automatic closure
**Localization**
- Use the `ChoiceTextLocalizationKey` field for localized text
- Leave empty if not using localization
- The system falls back to `ChoiceText` if no key is provided
#### Animation Presets
Dialogue animations control how choice buttons appear and behave. Create presets for consistency:
1. Right-click → Create → Unheimlich → Animation → Dialogue Animation Settings
2. Configure:
- **Spawn Mode**: How choices are positioned (circle, player-facing, etc.)
- **Rotation**: Axis and speed of floating rotation
- **Distance Scaling**: Scale choices based on distance
- **Floating**: Gentle up/down motion
- **Player Follow**: Choices follow player position/rotation
3. Assign preset to:
- Specific triggers (overrides default)
- NPCs (for character-specific style)
- Zones (for location-specific feel)
For object descriptions (inspectable items), create separate presets:
- Right-click → Create → Unheimlich → Animation → Object Description Settings
- Configure spawn pattern and floating word behavior
#### Common Dialogue Patterns
**Simple Linear Dialogue**
```
Node: "Welcome"
└── Choice: "Thanks" → Node: "Goodbye" → null
```
**Branching Dialogue**
```
Node: "What brings you here?"
├── Choice: "Just exploring" → Node: "Casual_Response"
├── Choice: "I'm looking for someone" → Node: "Quest_Response"
└── Choice: "None of your business" → Node: "Rude_Response"
```
**Loop Back Pattern**
```
Node: "Ask_Questions"
├── Choice: "Tell me about yourself" → Node: "About_Me" → back to "Ask_Questions"
├── Choice: "What's this place?" → Node: "About_Place" → back to "Ask_Questions"
└── Choice: "Goodbye" → null
```
---
### Player System
The player uses Unity's New Input System with a first-person controller.
#### Components
**Player.cs**
- Singleton access point
- Manages player state
- Provides transform reference for other systems
**PlayerMovement.cs**
- Character Controller-based movement
- Walking, running, jumping
- Ground detection
- Slope handling
**PlayerLook.cs**
- Mouse-based camera control
- Aim lock during dialogue (optional)
- Sensitivity settings
- Smooth rotation
**InteractionHandler.cs**
- Raycast-based interaction detection
- Highlights interactable objects
- Triggers interaction on key press
- Displays interaction prompts
#### Input Actions
Defined in `InputActions` asset:
- **Move**: WASD / Left Stick
- **Look**: Mouse / Right Stick
- **Jump**: Space / South Button (A/X)
- **Interact**: E / West Button (X/Square)
- **Run**: Left Shift / Left Stick Click
- **Pause**: ESC / Start Button
---
### Interaction System
Objects can be made interactable by implementing the `IInteractable` interface:
```csharp
public interface IInteractable
{
void Interact(GameObject interactor);
string GetInteractionPrompt();
}
```
#### Built-in Interactables
**DoorInteractable**
- Opens/closes with animation
- Optional locked state
- Synchronized portal doors
**InspectableItem**
- Player picks up and examines item
- Camera zooms to item
- Can merge with object descriptions
**NPCDialogueTrigger**
- Starts dialogue on interaction
- Shows interaction prompt
- Can be combined with other NPC behaviors
---
## Quick Start Guides
### Adding a Simple Dialogue
**Using the Dialogue Editor (Recommended):**
1. Right-click → Create → Unheimlich → Dialogue → Dialogue Conversation
2. Select the conversation asset
3. Click "Create New Dialogue Node" button in the inspector
4. Select the newly created node
5. Fill in dialogue text
6. Click "Add Choice" button for each player response option
7. For each choice, create another node (repeat step 3) and drag it to "Next Node"
8. Build your conversation flow by linking nodes
9. Drag `DialogueTrigger.prefab` into scene and assign your conversation
10. Test in play mode
**Manual Method (Alternative):**
1. Create conversation: Right-click → Unheimlich → Dialogue → Dialogue Conversation
2. Create first node: Right-click → Unheimlich → Dialogue → Dialogue Node
3. Fill node with text and add 1-2 choices
4. Create additional nodes for each choice
5. Connect choices to next nodes
6. Drag `DialogueTrigger.prefab` into scene
7. Assign conversation to trigger
8. Done - test in play mode
### Adding a Portal Between Two Rooms
1. Create two room prefabs with geometry
2. Add `PortalWithDoor.prefab` to each room
3. In Room A's portal:
- Set "Room Prefab To Load" to Room B
- Position "Room Spawn Point" appropriately
4. In Room B's portal (in the prefab):
- Set "Room Prefab To Load" to Room A
5. Place Room A in your starting scene
6. Test - walking through portal loads Room B
### Adding an Interactive Door
1. Add `DoorInteractable` component to door GameObject
2. Set pivot point at door hinge
3. Configure:
- Open angle (90 for standard door)
- Animation speed
- Lock state (if needed)
4. Ensure door is on "Interactable" layer
5. Test - press E to open/close
---
## Architecture Details
### Namespaces
The project uses organized namespaces:
```
Unheimlich.Audio # Audio systems
Unheimlich.Data.Dialogues # Dialogue data structures
Unheimlich.Gameplay.Environment # Doors, props
Unheimlich.Gameplay.Items # Item systems
Unheimlich.Gameplay.Player # Player components
Unheimlich.Gameplay.Portals # Portal system
Unheimlich.Interactions.Core # Base interaction
Unheimlich.Interactions.Dialogues # Dialogue triggers
Unheimlich.Interactions.FloatingWords # Choice button system
Unheimlich.Managers # Singleton managers
Unheimlich.UI # UI components
Unheimlich.Utility # Helper functions
```
### Manager Systems
**DialogueManager**
- Singleton: `DialogueManager.Instance`
- Manages active dialogue state
- Coordinates between UI, focus, and data systems
- Handles conversation lifecycle
**PortalRoomManager**
- Singleton: `PortalRoomManager.Instance`
- Tracks loaded rooms
- Distance-based loading/unloading
- Room instance pooling
**Both managers**:
- Auto-initialize in scene
- Persist only within scene (no DontDestroyOnLoad)
- Register/unregister components as needed
### Audio System
**PortalAudioProxy**
- Creates virtual audio sources for sounds through portals
- Projects audio from other side of portal
- Maintains spatial positioning
- Automatically manages proxy lifecycle
**Usage**: Attach to portal GameObject - it automatically detects audio sources in loaded rooms and creates proxies.
---
## Best Practices
### Performance
**Portals**
- Limit recursion to 3 levels (more tanks performance)
- Use occlusion culling on portal cameras
- Don't place portals facing each other at close range
- Disable portal rendering when not visible
**Dialogues**
- Keep choice count to 2-4 per node
- Use animation presets instead of per-instance settings
- Reuse DialogueNodes where possible
**Room Loading**
- Keep room prefabs optimized (low poly)
- Use room pooling for frequently visited areas
- Set appropriate load/unload distances
### Organization
**Dialogue Assets**
- Group conversations by character or location in folders
- Use prefixes: `CONV_` for conversations, `NODE_` for individual nodes
- Name nodes descriptively (e.g., `NODE_Shopkeeper_Greeting`)
**Prefabs**
- Keep room prefabs self-contained
- Use variants for different states (open/closed doors, etc.)
- Nest prefabs (NPC prefab inside Room prefab)
**Scenes**
- One main scene with essentials (Player, Managers, UI)
- Room prefabs for everything else
- Use Scene references for cross-scene data
### Dialogue Design
**Structure**
- Start simple: linear dialogue for testing
- Add branches after core flow works
- End nodes should have clear outcomes
- Avoid dead-ends (always provide exit choice)
**Usability**
- Choice text must be clear and concise
- Don't put more than 5 choices on one node
- Position choice anchors carefully (near speaker's head)
- Test all dialogue paths
### Portal Setup
**Linking**
- Use direct references when possible (simpler debugging)
- Use ID-based linking for dynamic scenarios
- Always set a room spawn point (never rely on default position)
- Test both directions of portal travel
**Visuals**
- Position portal screen mesh to align with doorway
- Match portal door to environment art style
- Add particle effects or audio cues for activated portals
---
## Troubleshooting
### Dialogue Issues
**Choices don't appear**
- Check DialogueNode has choices assigned
- Verify NextNode references aren't null (unless it's an end node)
- Ensure ChoiceText is filled in
- Check DialogueManager exists in scene
**Can't select choices with gaze**
- Verify DialogueFocusSystem is active
- Check choice anchor is positioned correctly
- Ensure player camera is tagged "MainCamera"
- Lower focus time requirement for testing
**Dialogue doesn't start**
- Confirm conversation asset has a valid first node
- Check trigger collider is set to "Trigger"
- Verify player is on correct layer for detection
- Look for errors in Console
### Portal Issues
**Portal shows black/nothing**
- Check linked portal reference is set
- Ensure portal camera exists (auto-created on Awake)
- Verify render texture is created
- Check both portals have screen mesh assigned
**Teleportation doesn't work**
- Confirm object has PortalTraveller component
- Check portal collider is set to "Trigger"
- Verify linked portal exists and is active
- Ensure portals aren't too close to each other
**Room doesn't load**
- Confirm PortalRoomManager exists in scene
- Check room prefab assignment
- Verify load distance settings
- Look for errors during room instantiation
- Ensure linked portal path in room is correct
**Performance issues with portals**
- Lower recursion limit (try 1-2)
- Reduce portal camera far clip plane
- Simplify room geometry
- Check for multiple portals rendering same view
### General Issues
**Input not working**
- Verify Input System package is installed
- Check InputActions asset is assigned to player
- Ensure input actions are enabled
- Try reimporting InputActions asset
**Objects not interactable**
- Confirm object has "Interactable" layer
- Check InteractionHandler raycast layer mask
- Verify IInteractable interface is implemented
- Ensure collider exists on object
**Audio not playing through portals**
- Add PortalAudioProxy to portal GameObject
- Check audio sources are in loaded room
- Verify audio settings (3D sound, spatial blend)
- Check PortalRoomManager is loading rooms
---
## Additional Resources
For more detailed examples and patterns, see:
- `/Pages/Guide - Nouveau Système de Dialogue.md` - Dialogue system deep dive
- `/Pages/Tutorial - Créer un dialogue en 2 minutes.md` - Quick dialogue tutorial
- `/Pages/Exemples - Structures de Dialogue Avancées.md` - Advanced dialogue patterns
For implementation details:
- Check inline code comments in manager scripts
- Review prefab configurations for reference setups
- Examine existing dialogue nodes and conversations in `/Datas/Dialogues`
---
**Need Help?**
Check console for error messages - most issues will log helpful information.
Enable Gizmos in Scene view to see portal connections and trigger volumes.
Use the Unity Profiler to diagnose performance issues.