前进!前进!不择手段的前进!

一、ECS是什么

image.png

Entity(实体) Component(组件) System(系统)
是一种基于数据的设计(DOD Data-Oriented Design)

dodbook
ECS的泛泛之谈
深入浅出Unity ECS
ECS 真的是「未来主流」的架构吗?
面向对象编程的弊端是什么?
面向数据编程

  1. 实体:持有全局唯一标记
  2. 组件:仅持有数据 纯数据
  3. 系统:仅持有逻辑 纯函数
for (s in systems) {
    s.doSomething(data);  // data为对应系统需要使用的数据
}

Q:什么是逻辑帧?
A:任何一个持续运行的程序,内部都有一个死循环。每执行一次循环,称为一个逻辑帧

二、几种设计流派的理解

  • OOD(Object-Oriented Design)

    • 总结共同数据与逻辑,抽象到基类中
  • COD(Compoment-Oriented Design) 

    • 打包数据与逻辑,抽象成一个个的组件对象
    • 基于组件
    • 仅抽象出共有逻辑,规范行为
  • DOD(Data-Oriented Design)

    • 以COD为基础,再把数据、逻辑分离

三、流派总结

实际上基于组件与基于接口本质相同,都是为了解决传统基于对象导致基类过重的问题。
基于组件、基于接口也得到了广泛的应用。

基于对象设计:基于现实的抽象。对象持有自己的所有数据、行为。

  • 优点:容易理解。基于接口的设计方式已经足够好。行为的统一、类之间的解耦都很好。
  • 缺点:性能上限低。面向对象编程的弊端是什么?

基于数据设计:行为是行为,数据是数据。行为只处理数据。行为本身不属于某个具体的对象。 

  • 优点:可以做到极致的性能 面向数据编程
  • 缺点:难理解,高性能的工程实现很难

做ECS是为了优化性能,因为接口编程已经足够解耦了。

四、基于数据的设计,为什么可以做到高性能?

先看下传统写法为什么低效

for (p in players) {
    if (condition) {
        p.doSth();
    }
}
  • 数据内存上离散,缓存不友好
  • 分支预测不友好
  • 虚函数调用开销大

五、CPU缓存友好

CPU读取内存比较慢,所以设计了一套缓存
一次读取一个cache line长度的数据

缓存三要素

  • 在相对较慢的交互之间插入一个高速的缓存层
  • 缓存层需要有数据卸载机制
  • 预测式加载数据

广度:

  • 服务器的缓存技术
  • 客户端的缓存技术
  • 还有没有其他的?

深度:

六、分支预测

处理器猜测进入哪个分支,并且基于预测结果来取指、译码。
如果猜测正确,就能节省时间。
如果猜测错误,刷新流水线,在新的地址处取指、译码。

广度:

深度

七、虚函数调用开销

对每一个虚函数的调用都需要额外的指针寻址
虚函数通常不能被inline,当虚函数都是小函数时会有比较大的性能损失
每个对象都需要有一个额外的指针指向虚表

C++的静态分发(CRTP)和动态分发(虚函数多态)的比较

八、以上三个问题,需要怎么解决?

  1. 缓存友好

    • 语言需要支持POD类型
    • 需要三个基本池

      • 内存池 – 减少系统调用
      • 线程池 – 真正的并行
      • 对象池 – 数据连续存储的保证
  2. 分支预测

    • 任何语言都可以
  3. 虚函数调用开销

    • 需要支持简单函数的定义

九、再总结:那作为脚本工程师,应该集中精力关心哪里?

设计 >= 虚拟机版本 >> 语法


NotAPenny
5 声望1 粉丝

努力成为一个独立游戏制作人


引用和评论

0 条评论