谈谈 ECS 架构
ECS怎么重新被发现的
几年前,守望先锋团队在GDC上做分享的时候,向大家安利了一发如何利用 Entity-Component-System
架构简化开发难度、提高效率的。这里有一些讨论:
如何评价《守望先锋》中的架构设计(ECS)? https://www.zhihu.com/question/61169850
然后业内就像商量好了似的,大家都站出来夸ECS,Unity甚至表示要将整个引擎架构和工作流往ECS上迁移,作为其 Performance by Default
的宏大计划中的重要组成部分。自此,ECS开始了全面大讨论,各类博客文章如雨后春笋般涌现。
本文作为一个漫谈而不是技术手册的语调,来说说ECS有啥好处、有啥不足。游戏服务端的同学也可以看一看,毕竟ECS的大体思想和微服务
有几分相似。
面向数据 vs 面向对象
ECS是一种以面向数据(DOD,Data Oriented Design)的架构。这里先简单说一下面向对象和面向数据两种设计思路。
OOP
(面向对象编程)是我们对现实世界的简单抽象,非常易于理解。但是,在大型项目中,往往并不是最好的解决方案,因为OOP:
- 并不是CPU友好的。OOP中的对象往往很臃肿,但有时只需要访问其中一个或少量几个数据,但如果要加载整个对象进cache,这很浪费。
- 数据分散在对象中,不易管理。
DOD
(面向数据设计)则是cache友好的,但不是那么符合直觉中好的设计。
- cache友好可以这么理解:N个对象,每个对象有M个成员,每个对象可表示为 Mx1的矢量。那么这些数据排成一个矩阵。则OOP是对矩阵按列分(供N列),而DOD是按行分(共M行)。显然DOD对cache更友好。
- 一般需要框架去支持,不然写起来不方便。
这些年似乎越来越多人开始觉得面向对象编程不够好(不够时尚),函数式编程才是本质(酷)。表面上看,ECS多少也借了这股风迎来自己的春天。其实是以下内在原因:
- 现代游戏的重点和复杂性逐渐转移到数据密集型计算和渲染上来了。ECS保持了DOD对cache友好的优点,性能提升突出。
- 游戏设计需求变化频繁,需要做大量的快速原型来验证玩法,而面向对象这种
scheme
太强了,很难提前设计一个很好的OOP结构。而不好的OOP结构对一个复杂项目而言,是一种灾难。
总之,面向数据和面向对象都是杰出的思考框架,而且一定程度上并不矛盾,可以分别以不同模块的形式存在于我们的游戏工程中(比如UI部分还是面向对象比较方便)。
ECS架构精髓(TODO)
Entity: 只是一个带有 ID
的容器,容纳若干Component
。
Component: 只有数据,没有逻辑。
System: 没有数据(状态),只有逻辑。一个System
只关心自己需要的数据,通过遍历这些数据,来进行逻辑处理。
ECS好在哪里(TODO)
- 速度。因为cache友好。
- 解耦。
- 组件复用方便,没有副作用。
ECS麻烦在哪里(TODO)
- 物理。
- 多个System的协同。
- 编辑器下游戏场景不直观。
结语
ECS对性能的提升是毋庸置疑的,不过其对于工程架构的想法很美好,实操起来会有其它问题要处理。因为复杂度只是被转移了,“脏”的东西依然存在。我们要做的是,把这些脏的东西放在一起,并以可控、可寻的方式来管理。没有一劳永逸的方法,关键的还是团队中的“人”。