添加 claude code game studios 到项目
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user