Unity3D 进阶教程 - 14 ECS 实体组件系统

概述

传统 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 的性能潜力。

ByteFisher
分享编程技术 · 记录钓鱼乐趣
扫码关注
▸ 扫码关注 ◂
分享: