[Unity ECS] 关于 DOTS:实体组件系统

63 篇文章 11 订阅

英文原文:

https://blog.unity.com/technology/on-dots-entity-component-system

  这是关于我们新的以数据为导向的技术栈(DOTS)的几篇文章之一,分享一些关于我们如何和为什么走到今天,以及我们接下来要去哪里的见解。

  在我的上一篇文章中,我谈到了 HPC# 和 Burst 作为 Unity 未来发展的底层基础技术。我喜欢将我们栈的这一层称为“游戏引擎的引擎”。任何人都可以使用这个栈来编写游戏引擎。我们可以。我们会。你也可以。不喜欢我们的?编写您自己的,或根据您的喜好修改我们的。

Unity的组件系统

  我们在上面建立的下一层是一个新的组件系统。Unity一直都是以组件的概念为中心。你在一个GameObject上添加一个Rigidbody组件,它就会开始下降。你给一个GameObject添加一个Light组件,它就会开始发射光线。添加一个AudioEmitter组件,这个GameObject就会开始发出声音。

  对于程序员和非程序员来说,这是一个非常自然的概念,并且易于构建直观的 UI。实际上,我对这个概念的成熟程度感到非常惊讶。太好了,我们想保留它。

  不合时宜的是我们如何实现我们的组件系统。它是以面向对象的思维方式编写的。组件和GameObjects是 "重型C++"对象。创建/删除它们需要一个mutex锁来修改id->objectpointers的全局列表。所有的GameObjects都有一个名字。每个对象都有一个指向C++对象的C#包装器。那个C#对象可以在内存的任何地方。C++对象也可以在内存中的任何地方。缓存缺失的情况很多。我们尽可能地减轻症状,但你能做的只有这么多。

  有了面向数据的思维方式,我们可以做得更好。从用户的角度来看,我们可以保持相同的好属性(添加一个刚体组件,这个东西会掉下来),但也可以通过我们的新组件系统获得惊人的性能和并行性。

  这个新的组件系统就是我们的实体组件系统(ECS)。非常粗略地说,你今天对 GameObject 所做的事情就是在新系统中对 Entity 所做的事情。组件仍称为组件。那么有什么不同呢?数据布局。

  让我们看看一些常见的数据访问模式
以传统方式在 Unity 中编写的典型组件可能如下所示:

class Orbit : MonoBehaviour
{
   public Transform _objectToOrbitAround;

   void Update()
   {
       //please ignore this math is all broken, that's not the point here :)
       var currentPos = GetComponent<Transform>().position;
       var targetPos = _objectToOrbitAround.position;
       GetComponent<RigidBody>().velocity += SomehowSteerTowards(currentPos,targetPos)
   }
}

  这种模式反复出现。一个组件必须在同一个 GameObject 上找到一个或多个其他组件并在其上读取/写入一些值。

这有很多问题:

  • 单个轨道组件调用 Update() 方法。下一次 Update() 调用可能是针对完全不同的组件,可能会导致该代码在下次必须为另一个 Orbit 组件运行此帧时从缓存中清除。
  • Update()必须使用GetComponent()去寻找它的Rigidbody。(它可以被缓存起来,但是你必须注意Rigidbody组件不会被销毁)。
  • 我们正在操作的其他组件在内存中的位置完全不同。

  ECS 使用的数据布局认识到这是一种非常常见的模式,并优化了内存布局以使此类操作快速进行。

ECS 数据布局

  ECS 将在内存中具有完全相同的组件集的所有实体组合在一起。它将这样的集合称为原型。原型的一个例子是:“Position & Velocity & Rigidbody & Collider”。 ECS 以 16k 的块为单位分配内存。每个块将仅包含单个原型实体的组件数据。

  在ECS的土地上,你必须静态地声明 "我想对所有同时拥有elocity 和 Rigidbody 以及 Orbit 组件的实体进行一些操作,而不是让用户更新方法在运行时搜索其他组件来进行操作。为了找到所有这些实体,我们只需找到符合特定 "组件搜索查询 "的所有原型。每个原型都有一个存储该原型的实体的Chunks列表。我们在所有这些Chunks上循环,在每一个Chunks里面,我们都在做一个紧密的内存线性循环,来读写组件数据。这种在每个实体上运行相同代码的线性循环也为Burst提供了可能的矢量化机会。

  在许多情况下,这个过程可以轻松地拆分为多个Job,从而使操作 ECS 组件的代码以接近 100% 的核心利用率运行。

  ECS 为您完成所有这些工作,您只需提供要在每个实体上运行的代码。 (如果你愿意,你可以手动进行块迭代。)

  当你从一个实体中添加/删除一个组件时,它就会转换原型。我们把它从当前的块中移到新的原型的块中,然后把前一个块中的最后一个实体换回来,以 “填补这个洞”。

  在ECS中,你也可以静态地声明你打算如何处理组件的数据。只读或读写。通过承诺(承诺被验证)只从Position组件中读取数据,ECS可以获得更有效的作业调度。其他同样想从Position组件中读取数据的Job就不必等待了。

  这种数据布局还允许我们处理我们长期存在的问题,即加载时间和序列化性能。为大场景加载/流式传输 ECS 数据不仅仅是从磁盘加载原始字节并按原样使用它们。

  这就是 Megacity 演示在几秒钟内就可以在手机上加载的原因。

快乐的“意外”

  虽然实体可以做今天游戏对象所做的事情,但由于它们是如此轻量级,所以可以做更多。事实上,什么才是真正的实体?在这篇文章的早期草稿中,我写道 “我们将实体存储在块中”,后来改成 “我们将实体的组件数据存储在块中”。这是一个很重要的区别,要意识到一个实体只是一个32位的整数。除了它的组件数据外,没有任何东西可以存储或分配给它。因为它们是如此便宜,你可以把它们用于游戏对象不适合的场景。比如在一个粒子系统中为每个单独的粒子使用一个实体。

HPC#、Burst、ECS。太棒了,但是我的游戏引擎呢?

  我们需要建立的下一个层是非常大的。这是 "游戏引擎 "层,由 “渲染器”、“物理学”、“网络”、“输入”、"动画 "等功能组成。这就是我们今天的大致情况。我们已经开始在这些部分工作,但它们不会在一夜之间准备好。

  这听起来可能很糟糕。在某种程度上它是,但在另一种情况下,它不是。因为 ECS 和基于它构建的所有东西都是用 C# 编写的,所以它可以在传统的 Unity 内部运行。因为它在 Unity 内部运行,所以您可以编写使用 ECS 之前功能的 ECS 组件。目前还没有纯 ECS 网格绘制系统。但是,您可以编写一个使用 ECS 之前的 Graphics.DrawMeshIndirect API 作为实现的 ECS MeshRenderSystem,同时等待纯 ECS 版本发布。这正是我们的 Megacity 演示使用的技术。 Loading/Streaming/Culling/LODding/Animation 是用纯 ECS 系统完成的,但最终的绘图不是。

  所以你可以混搭。这样做的好处是,您已经可以从 Burst 代码生成和游戏代码的 ECS 性能中获益,而不必等待我们发布所有子系统的纯 ECS 版本。不好的是,在这个过渡阶段,你可以看到并感受到这种摩擦,你“使用了两个粘在一起的不同世界”。

  我们会将所有源代码打包发送到我们的 ECS HPC# 子系统。您可以检查、调试、修改每个子系统,还可以更精细地控制何时要升级哪个子系统。例如,您可以升级物理子系统包而不升级其他任何东西。

Game Objects 会发生什么变化?

  Game Objects不会去任何地方。十多年来,人们已经成功地在其上发布了令人惊叹的游戏。这个基础不会被丢弃。

  将会改变的是,随着时间的推移,您将看到我们改进的精力从专门进入游戏对象世界转向 ECS 世界。

API 可用性/模板

  在看ECS的时候,人们提出的一个常见的、非常有道理的观点是,有很多类型的东西。大量的模板代码挡在你和你要实现的目标之间。

  即将进行许多改进,旨在消除对大多数样板的需求,并使表达您的意图变得更简单。我们还没有实现其中的许多,因为我们一直专注于基础性能,但我们认为 ECS 游戏代码没有充分的理由拥有太多样板代码,或者比编写 MonoBehaviour 更需要编写工作。

  Project Tiny 已经实现了其中的一些改进(例如基于 lambda 的迭代 API)。说到这个。。

Project Tiny 的 ECS 如何融入这一切?

  Project Tiny将在本博文所谈到的相同的C# ECS的基础上发货。Project Tiny将在几个方面成为我们ECS的一个重要里程碑。

  • 它将能够在一个完整的ECS专用环境中运行。一个没有过去包袱的新玩家。
  • 这意味着它也是纯粹的ECS,必须与一个真正的(微小的)游戏所需要的所有ECS子系统一起发货。
  • 我们将采用 Project Tiny 的编辑器支持对所有 ECS 场景进行实体编辑,而不仅仅是 tiny。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值