23 KiB
Unheimlich POC - Developer Guide
Unity Version: 6000.3
Last Updated: January 2025
Table of Contents
- Project Overview
- Core Systems
- Quick Start Guides
- Architecture Details
- Best Practices
- 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
- Rendering: Each portal has a camera that renders the view from the linked portal's perspective
- Display: The rendered view is projected onto the portal's screen mesh
- Teleportation: Objects crossing the portal plane are teleported to the linked portal
- Slicing: A custom shader prevents visual artifacts by hiding geometry on the wrong side
- 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:
-
Drag
PortalWithDoor.prefabinto your scene -
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")
-
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
- Create Portal A in Room A
- Create Portal B in Room B (make it a prefab)
- Set Portal A to load Room B prefab
- 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:
-
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
-
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)
-
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
-
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)
-
Add Trigger to Scene
- Drag
DialogueTrigger.prefabinto your scene - Position it where you want dialogue to trigger
- Assign your conversation to the trigger component
- Configure trigger type (zone, interact, or NPC)
- Drag
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
- Create the conversation asset
- Use "Create New Dialogue Node" to build your flow
- Edit each node's content and choices
- Link nodes by dragging references
- Test in play mode
- Iterate based on playthrough
Creating Custom Dialogue Triggers
If you need more control, you can create triggers from scratch:
Option 1: Zone Trigger (Automatic)
// 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)
// 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)
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
AutoCloseDelayto 0 for manual close (requires player to walk away or press ESC) - Set to 3-5 seconds for automatic closure
Localization
- Use the
ChoiceTextLocalizationKeyfield for localized text - Leave empty if not using localization
- The system falls back to
ChoiceTextif no key is provided
Animation Presets
Dialogue animations control how choice buttons appear and behave. Create presets for consistency:
-
Right-click → Create → Unheimlich → Animation → Dialogue Animation Settings
-
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
-
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:
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):
- Right-click → Create → Unheimlich → Dialogue → Dialogue Conversation
- Select the conversation asset
- Click "Create New Dialogue Node" button in the inspector
- Select the newly created node
- Fill in dialogue text
- Click "Add Choice" button for each player response option
- For each choice, create another node (repeat step 3) and drag it to "Next Node"
- Build your conversation flow by linking nodes
- Drag
DialogueTrigger.prefabinto scene and assign your conversation - Test in play mode
Manual Method (Alternative):
- Create conversation: Right-click → Unheimlich → Dialogue → Dialogue Conversation
- Create first node: Right-click → Unheimlich → Dialogue → Dialogue Node
- Fill node with text and add 1-2 choices
- Create additional nodes for each choice
- Connect choices to next nodes
- Drag
DialogueTrigger.prefabinto scene - Assign conversation to trigger
- Done - test in play mode
Adding a Portal Between Two Rooms
- Create two room prefabs with geometry
- Add
PortalWithDoor.prefabto each room - In Room A's portal:
- Set "Room Prefab To Load" to Room B
- Position "Room Spawn Point" appropriately
- In Room B's portal (in the prefab):
- Set "Room Prefab To Load" to Room A
- Place Room A in your starting scene
- Test - walking through portal loads Room B
Adding an Interactive Door
- Add
DoorInteractablecomponent to door GameObject - Set pivot point at door hinge
- Configure:
- Open angle (90 for standard door)
- Animation speed
- Lock state (if needed)
- Ensure door is on "Interactable" layer
- 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.