洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS。
Entity之后,咱们来到了Component组件。Component是ECS架构的三个基本元素之一。Component组件中包含了游戏或者应用的数据。Entity是组件集合的索引,System包含具体的逻辑行为。

ECS之Component组件

ECS中的组件是一个结构体,需要实现下列之一的接口:
  • IComponentData :最基本的,也是使用最多的接口,没有特殊需求的组件都可以实现这个接口。用于通用的目的和chunk components。
  • IBufferElementData :给entity关联dynamic(动态) buffer
  • ISharedComponentData :可以通过数据来对entity的原型进行分组
  • ISystemStateComponentData :给entity关联一个系统的状态,可以检测entity何时被创建或者销毁
  • ISharedSystemStateComponentData :同时包含 ISharedComponentData 和 ISystemStateComponentData 中的功能。
  • Blob assets :技术上来说不是一个component,你可以使用blob assets存储数据。Blob asset可以被一个或者多个component通过 BlobAssetReference 只读引用。你可以使用blob asset共享数据,也可以在C# job中访问。
之前在ECS核心概念那一节说过,EntityManager会使用 原型(archetype) 来组织不同的component组合。相同原型的entity在物理内存上都在一起,叫做一个 内存块 。同一个内存块中都是相同的组件原型。
上面这个图说明了ECS是如何根据原型来存储数据的。
下面说说 例外情况 :
  • Shared components和chunk components是例外情况,ECS不在chunk中存储他们。
  • 你可以在chunk之外存储dynamic buffer
虽然ECS不在内存块中存储上面类型的组件,但是进行实体查询的时候,你还是可以使用同样的方式对待他们。

通用目的的组件

Unity的ECS中的 ComponentData 是一个结构体,仅仅包含entity的实例数据。 ComponentData 不能包含方法,程序所有的逻辑需要写在System中。 ComponentData 有点类似于面向对象的Unity中,一个Component仅仅包含成员变量,但是没有成员方法、属性等等。
ECS的API提供了一个叫 IComponentData 的接口,你可以使用结构体实现这个接口来声明一个通用目的的组件类型。

IComponentData

传统的Unity组件是面向对象的MonoBehaviour类,同时会包含数据和方法。 IComponentData 是一个纯ECS风格的组件,只有数据,没有方法。你应该使用 结构体 来实现 IComponentData 接口,结构体在传递的时候会使用值传递,需要注意值传递在修改数据的时候有些不同,需要按照类似下面的代码对数据进行修改:
    
    
var transform = group . transform [ index ] ; // 读取 transform . heading = playerInput . move ; // 修改 transform . position += deltaTime * playerInput . move * settings . playerMoveSpeed ; group . transform [ index ] = transform ; // 写入
IComponentData 结构体中不能包含引用到托管对象的引用类型。因为 ComponentData 存在没有GC的内存块中,对性能有很大提升。

【选学】托管IComponentData

使用托管 IComponentData (即, IComponentData 使用 class 而不是 struct 进行声明)有助于将现有代码零散地移植到ECS,与托管数据进行交互或者为数据布局进行原型设计, ISharedComponentData 不能和托管数据交互。
这些组件的使用方式与值类型 IComponentData 相同。但是,ECS在内部以完全不同(而且比较慢)的方式来处理它们。如果不需要托管组件支持,可以在Player Settings里面(Edit > Project Settings > Player > Scripting Define Symbols)设置 UNITY_DISABLE_MANAGED_COMPONENTS 宏来禁用它。
由于托管 IComponentData 是托管类型,因此与值类型 IComponentData 相比,它具有以下性能缺点:
  • 不能与Burst编译器一起使用
  • 不能在Job结构体中使用
  • 它不能使用块内存
  • 需要垃圾回收
你应该尽可能不用托管组件,并尽可能使用blittable类型。
托管 IComponentData 必须实现 IEquatable<T> 接口并覆盖 Object.GetHashCode() 方法。此外,出于序列化的目的,托管组件需要有默认无参构造方法。
你必须在主线程上设置组件的值,可以使用 EntityManager 或 EntityCommandBuffer 。由于组件是引用类型,因此与ISharedComponentData不同,你可以更改组件的值而无需在块之间移动实体。(之前咱们知道修改ISharedComponentData的值会导致entity所在的内存块发生变化)
但是,尽管在逻辑上将托管组件与值类型的组件分开存储,但它们都会有 EntityArchetype 原型定义。这样,向实体添加新的托管组件仍然会导致ECS创建新的原型(如果尚不存在匹配的原型),并将实体移至新的Chunk。

总结

既然用了ECS,那就从一开始忘掉MonoBehaviour的组件的写法,拥抱ECS吧!

扩展阅读

【扩展学习】 在 洪流学堂 公众号回复 DOTS 可以阅读本系列所有文章,更有视频教程等着你!
呼~ 今天小新絮絮叨叨的真是够够的了。没讲清楚的地方欢迎评论,咱们一起探索。
我是大智(vx:zhz11235),你的技术探路者,下次见!
别走! 点赞 , 收藏 哦!
好,你可以走了。
Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐