谈谈Unity实体组件ECS与Jobs System

本文深入探讨Unity的ECS和Jobs System,旨在帮助IT游戏开发者理解这两个系统的使用和优势。通过分析Unity传统GameObject工作流程的缺点,阐述ECS如何优化内存引用和提高缓存效率,同时介绍了C# Jobs System如何解决多线程编程难题,提高并行处理能力。结合实际案例,展示了ECS和Jobs System结合使用带来的性能提升,对于处理大量游戏对象的场景尤其有效。
摘要由CSDN通过智能技术生成

Unity2018版本提供了ECS和Jobs System功能,网上也有很多这方面的技术介绍,本篇博客从Unity架构优化的角度给读者介绍关于ECS和Jobs System的使用,结合着实际案例希望让读者更容易理解它们,尤其是在IT游戏行业工作了两年以上的开发者,更应该掌握一些架构技术。
Unity 实体组件系统和 C# Job System 是两个不同的系统,但它们密不可分,若要了解这两个系统,我们先看看在 Unity 场景中创建游戏对象的工作流程如下所示:

  • 创建一个GameObject对象;
  • 在对象上添加组件:Renderer,Collider,Rigidbody physics;
  • 创建 MonoBehaviour 脚本并将其添加到对象中,以便在运行时控制和更改这些组件的状态属性;
    以上三个步骤执行,我们称为Unity的执行流程,作为Unity开发者来说,这个是最基本的流程。但是这种做法有它自己的缺点和性能问题。比如数据和逻辑是紧密耦合的,这意味着代码重用的频率较低,因为逻辑与特定数据相关联,无法单独分离出来。
    例如下图所示的 GameObject 和 Components 示例中,GameObject 依赖于 Transform、Renderer、Rigidbody 和 Collider 引用,在这些脚本中引用的对象分散在堆内存中。
    这里写图片描述
    游戏对象、其行为及其组件之间的内存引用,看下图:
    这里写图片描述
    Unity GameObject 场景可以让游戏在非常短的时间内完成原型构建并运行,这个也是Unity的特色可以让开发者快速入手,但它对于性能来说不太理想。我们再深层次的探讨这个问题,每个引用类型都包含可能不需要访问的许多额外数据,这些未使用的成员也占用了处理器缓存中的宝贵空间。比如我们继承的Mono就是一个典型的案例,如果只需要现有组件的极少功能接口函数或者变量,则可以将其余部分视为浪费空间,如下面的“浪费空间”图所示:
    这里写图片描述
    在上图中,粗体表示实际用于移动操作的成员,其余的就是浪费空间,若要移动 GameObject,脚本需要从 Transform 组件访问位置和旋转数据成员。当硬件从内存中获取数据时,缓存行中会填充许多可能无用的数据,如果只是为所有应该移动的GameObjects 设置一个只有位置和旋转成员的阵列,这将能够在很短的时间内执行,如何去掉无用的数据?ECS就是为解决此问题而设计的。
  • ECS实体组件系统
    Unity 的新实体组件系统可帮助消除低效的对象引用,我们考虑只包含它所需数据的实体,而不考虑带自己组件集合的GameObjects 。
    在下面的实体组件系统中,请注意 Bullet 实体没有附加Transform 或 Rigidbody 组件,Bullet 实体只是显式运行更新所需的原始数据,借助这个新系统,您可以将逻辑与各个对象类型完全分离。
    这里写图片描述
    这个系统具有很大的优势:它不仅可以提高缓存效率,缩短访问时间;它还支持在需要使用这种数据对齐方式的现代 CPU 中采用先进技术(自动矢量化/SIMD)这为游戏提供了所需的默认性能。如下图所示:
    这里写图片描述
    这里写图片描述
    上图请注意缓存行存储中的碎片和继承Mono系统生成的空间浪费,数据对比如下所示:
    这里写图片描述
    上图是将与单个移动操作相关的内存空间与实现相同目标的两个操作进行对比的结果。

  • C# Jobs System
    大多数使用多线程代码的人都知道编写线程安全代码很难,线程争抢资源可能会发生,但机会非常少,如果程序员没有想到这个问题,可能会导致潜在的严重错误。除此之外,上下文切换的成本很高,因此学习如何平衡工作负载以尽可能高效地运行是很困难的。新的 Unity C# Jobs System为您解决所有这些难题,如下图所示:
    这里写图片描述
    我们来看一下简单的子弹运动系统,大多数游戏程序员都会为 GameObject 编写一个管理器,如 Bullet Manager 中所示,通常,这些管理器会管理一个 GameObjects 列表,并每帧更新场景中所有子弹活动的位置。这非常符合使用 C# Jobs System的条件,由于子弹运动可以单独处理,因此非常适合并行化,借助 C# Jobs System,可以轻松地将此功能拉出来,并行运行不同的数据块,作为开发人员,您只需要专注于游戏逻辑代码即可。再介绍介绍实体组件系统和C# Jobs System二者的结合。

实体组件系统和 C# Jobs System的结合可以提供更强大的功能,由于实体组件系统以高效、紧凑的方式设置数据,因此Jobs System可以拆分数据阵列,以便可以高效地并行操作。但我如何使用这个新系统?
通过一个案例给读者介绍,下面是我们设计的游戏运行方式:

  • 玩家敲击空格键并在该帧中产生一定数量的船只。
  • 生成的每个船只都设置为屏幕边界内的随机 X 坐标。
  • 生成的每个船只都有一个移动功能,可将其发送到屏幕底部。
  • 一旦超过底部界限,生成的每个船只将重置其位置。
    这是一个比较简单的游戏逻辑,我们以此为例给读者介绍几种实现方式:
  • 继承Mono的设计
    这个是最常用的设计,作为游戏开发者,最容易掌握的,只需要检查每帧的空格键输入并触发 AddShips() 方法,这种方法在屏幕的左侧和右侧之间找到随机 X/Z 位置,将船的旋转角度设置为指向下方,并在该位置生成船只预制体。
void Update()
{
    if (Input.GetKeyDown("space"))
        AddShips(enemyShipIncremement);
}

void AddShips(int amount)
{
    for (int i = 0; i < amount; i++)
    {
        float xVal = Random.Range(leftBound, rightBound);
        float zVal = Random.Range(0f, 10f);

        Vector3 pos = new Vector3(xVal, 0f, zVal + topBound);
        Quaternion rot = Quaternion.Euler(
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海洋_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值