今天跟同学们分享一下我找到的关于ECS相关的理论知识文章,可能比较枯燥,如果想看实操的请看我下方写的一些案例解析。
- Unity之浅析 Entity Component System (ECS)
- Unity 之 Pure版Entity Component System (ECS) 官方Rotation示例解析
- Unity 之 Pure版Entity Component System【ECS】 案例【GalacticConquest】解析【上】
- Unity 之 Pure版Entity Component System【ECS】 案例【GalacticConquest】解析【下】
有说的不正确或者不准确的地方欢迎留言指正
如果你有感觉不错的相关文章也可以留言推送给我,不胜感谢~
有什么有趣的写作技巧或者想法欢迎大家给我留言,大家的帮助是我写下去最有效的动力
参考资料 【有时间强力建议看一下】。。。。。这些资料里面讲述的概念笔者很多不怎么理解,哈哈哈
- 详解实体组件系统ECS
- 深入解读Job System(1)
- 深入解读Job System(2)
- 使用Unity ECS开发《我的世界》
- WIKI----Entity–component–system
- WIKI----面向数据的设计
- SIMD----单指令流多数据流
- 为实现极限性能的面向数据编程范式
- 面向数据的设计----Noel(汉化版)
- 面向数据的设计----Noel(原文需要*)
- 游戏设计模式——面向数据编程思想
- 2018/11/03更新 Job System介绍
- 2018/11/03更新 ECS 真的是「未来主流」的架构吗?
下面是笔者看完这些参考文章后对ECS的理解或引用其中文章的部分解释
一:在Unity中笔者对ECS的理解
- Entity:实体,笔者更喜欢说数据的小集合,例如transform中的、Position、Roitation、Scale,这三个数据组成的集合就可以称为实体。而且实体可以*的组装拆分,例如可以拆分为Position与Roitation、Position与Scale、Roitation与Scale甚至自己编写的数据集合。
- Component:组件。这里提到的组件与传统组件可不一样,传统的组件有点Entity+业务逻辑的意思,里面包含很多数据,但是在ECS中的Component 只是一条单一的数据,例如Position单一的数据是一个Component,Roitation单一的数据也是一个Component,不包含任何的业务逻辑,有点像传统类中的字段,仅仅就是一个数据。
- System:系统,这里的作用就是做行为逻辑操作的地方了,通过对Component*的组装成Entity,然后根据规则筛选出Entity获取到对应的对象,之后进行逻辑操作。而且在ECS中的System是不分场景的,例如有一个负责移动的System,他真的是负责所有场景中对应的移动,不像以往仅仅负责一个场景中一个物体或者子弹单类的移动。
二:一个有关CPU处理数据的概念:CPU多级缓存
什么是CPU缓存???
- 更详细来说,结构应该是:CPU<---->寄存器<---->CPU缓存<---->内存
- 可以看到CPU缓存是介于内存和寄存器之间的一个存储区域,此外它存储空间比内存小,比寄存器大。
为什么需要CPU多级缓存:
- CPU的运行频率太快了,而CPU访问内存的速度很慢,这样在处理器时钟周期内,CPU常常需要等待寄存器读取内存,浪费时间。
- 而CPU访问CPU缓存则速度快很多。为了缓解CPU和内存之间速度的不匹配问题,CPU缓存则预先存储好潜在可能会访问的内存数据。
CPU多级缓存预先存的是什么:
- 时间局部性:如果某个数据被访问,那么在不久的将来它很可能再次被访问。
- 空间局部性:如果某个数据被访问,那么与它相邻的数据很快也能被访问。
- CPU多级缓存根据这两个特点,一般存储的是访问过的数据+访问数据的相邻数据。
CPU缓存命中/未命中:
- CPU把待处理的数据或已处理的数据存入缓存指定的地址中,如果即将要处理的数据已经存在此地址了,就叫作CPU缓存命中。
- 如果CPU缓存未命中,就转到内存地址访问。
提高CPU缓存命中率
- 要尽可能提高CPU缓存命中率,就是要尽量让使用的数据连续在一起。
冷数据/热数据分割
有人可能认为这样能最大程度利用CPU缓存:把一个对象所有要用的数据(包括组件数据)都塞进一个类里,而没有任何用指针或引用的形式间接存储数据。
实际上这个想法是错误的,我们不能忽视一个问题:CPU缓存的存储空间是有限的
于是我们希望CPU缓存存储的是经常使用的数据,而不是那些少用的数据。这就引入了冷数据/热数据分割的概念了。
热数据:经常要操作使用的数据,我们一般可以直接作为可直接访问的成员变量。
冷数据:比较少用的数据,我们一般以引用/指针来间接访问(即存储的是指针或者引用)。
三:原来的OOP模式为什么越来越慢了?
oop模式的主要的思想就是万物皆对象,调用的方式几乎都是以对象为基础,以模块化编程的带来优势的同时,也有他的负面效果:冗余数据过多,包袱过重。举个例子:在旅行时是一个人带一张信用卡去还是带一堆的生活用品去会更方便呢?答案当然是前者,只需要带着有用的东西就可以了,无用的都可以丢掉。oop其实也是这个样子,在进行数据传输的时候(数据读取)总会带着一些无用的数据,不仅仅零零散散(传统方式的内存管理是离散式的),而且还占用空间(上文提到的CPU多级缓存),随着现在游戏的规模越来越大,摩尔定律的失效,单纯的提高主频达到好的计算效果变得越来越力不从心。
四:ECS为什么快?
传统方式的内存管理是离散式的,即物体和它的组件(Component)并非在同一个内存区段,每次存取都非常耗时。而ECS会确保遍历时所有的组件资料(Component Data)都紧密的连接再一起,称为Archetype,这样就能确保存取内存资料时以最快的速度存取(也提高命中率)。
数据量身定制化,不会有多余的数据,例如Position原有的Vector3换成了现在Float3,列表等换成了unity重新定的Native开头的数据结构,分为并发和非并发执行。IJob、IJobParallelFor或 IJobParallelForTransform会根据是否单核还是多核分别选择接口,SharedComponentData和ComponentData等等。这种量身定制或数据也带来了更多的选择性和灵活性,随之通用性也就降低。
这种多核心并行计算也符合现在硬件的发展趋势,摩尔定律的失效,以增加核心数量带来的计算力不断的提升是现在主流的现象。
五:ECS目前能做多好
Unity 特大城市Demo展示
官方相关地址
根据官方介绍,此项目包含450万网格渲染,5000辆动态汽车,10万种独特的声音效果,每栋建筑的零件达到20万个,并且还能以60fps的帧率稳定运行。另外,视频的结尾部分还公开了一段手机平台的演示,官方特别指出这绝不是使用手机播放了一段视频,而是真正用Unity生成APP,再在手机上运行的画面。这款demo和所有资源将于2019年发布。