概述
传统 Unity 开发基于 GameObject-Component 模型。当对象数量达到数万级别时,GameObject 的管理开销和单体脚本的性能瓶颈就暴露了。
ECS(Entity Component System) 是 Unity 推出的数据驱动架构方案,属于 DOTS(Data-Oriented Tech Stack) 的核心部分。ECS 的核心思路是 数据与逻辑分离、内存连续布局、多线程并行处理。
1. ECS 核心概念
1.1 对比传统模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 传统 OOP: Player(GameObject) ├── Transform(位置/旋转/缩放) ├── MeshRenderer(渲染) └── PlayerMovement(移动逻辑)
内存布局:每个 GameObject 分散在堆中 性能:数据不连续,CPU 缓存效率低
ECS: Entity(ID 只是一个 int) └── 组件数据存放在连续数组 ├── Translation[](所有实体的位置,连续排列) └── LocalTransform[](所有实体的变换,连续排列)
内存布局:同类型组件在连续数组,遍历时 CPU 缓存友好
|
| 对比 |
GameObject-Component |
ECS |
| 对象标识 |
GameObject 引用 |
Entity(int) |
| 数据存储 |
组件挂载在对象上 |
组件存放在 Archetype 块中 |
| 逻辑组织 |
MonoBehaviour |
System(继承 ISystem) |
| 数据处理 |
单线程 |
多线程 Job |
| 适合场景 |
< 5000 个对象 |
> 10000 个对象 |
1.2 安装 ECS 包
1 2 3
| Package Manager 安装: Entities(核心 ECS 框架) Entities.Graphics(ECS 渲染支持)
|
2. 定义组件
ECS 中的组件是 纯数据 结构体,没有任何方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| using Unity.Entities;
public struct Health : IComponentData { public float Value; public float MaxValue; }
public struct Speed : IComponentData { public float Value; }
public struct DeadTag : IComponentData { }
|
所有组件是值类型(struct),存储在 ArchetypeChunk 的连续数组中。
3. 创建 System
System 是 ECS 的逻辑部分,负责处理带有特定组件的 Entity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| using Unity.Entities; using Unity.Burst;
[BurstCompile] public partial struct HealthRegenSystem : ISystem { [BurstCompile] public void OnUpdate(ref SystemState state) { foreach (var (health, regen) in SystemAPI.Query<RefRW<Health>, RefRO<Regen>>()) { health.ValueRW.Value += regen.ValueRO.Amount * SystemAPI.Time.DeltaTime; health.ValueRW.Value = math.min(health.ValueRW.Value, health.ValueRO.MaxValue); } } }
|
4. 创建与销毁 Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class EntitySpawner : MonoBehaviour { [SerializeField] private int _count = 10000;
private void Start() { var world = World.DefaultGameObjectInjectionWorld; var entityManager = world.EntityManager;
var archetype = entityManager.CreateArchetype( typeof(LocalTransform), typeof(Health), typeof(Speed) );
var entities = new NativeArray<Entity>(_count, Allocator.Temp); entityManager.CreateEntity(archetype, entities);
for (int i = 0; i < entities.Length; i++) { entityManager.SetComponentData(entities[i], new LocalTransform { Position = Random.insideUnitSphere * 50, Rotation = quaternion.identity, Scale = 1f }); }
entities.Dispose(); } }
|
5. 性能对比
| 指标 |
GameObject (50K) |
ECS (50K) |
| 创建时间 |
~2.5s |
~0.3s |
| 每帧 Update 耗时 |
~25ms |
~0.5ms |
| 内存占用 |
~500MB |
~80MB |
| GC Alloc |
高 |
零 |
6. 何时使用 ECS
| 适合 ECS |
不适合 ECS |
| 大量同类对象(>5000) |
独特性强的对象(主角、Boss) |
| 需要极致性能的模拟 |
UI 交互逻辑 |
| 物理粒子系统 |
原型/快速迭代阶段 |
| AI 群体行为(群组) |
需要大量不同组件混合的场景 |
总结
- ECS = Entity(ID)+ Component(数据)+ System(逻辑)
- 组件是 struct 纯数据,保证内存连续和缓存友好
- System 通过 SystemAPI.Query 过滤实体,支持多线程并行
- 适合 大量同类对象 的场景,收益最大
- 不建议全盘替换,只在性能热点使用 ECS
下一篇将结合 Job System 与 Burst Compiler,进一步挖掘多核 CPU 的性能潜力。