欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ECS之Component组件

程序员文章站 2022-03-26 18:04:56
洪流学堂,让你快人几步。你好,我是跟着大智学Unity的萌新,我叫小新,最近在跟着大智学习DOTS。Entity之后,咱们来到了Component组件。Component是ECS架构的三个基本元素之一。Component组件中包含了游戏或者应用的数据。Entity是组件集合的索引,System包含具体的逻辑行为。ECS之Component组件ECS中的组件是一个结构体,需要实现下列之一的接口:IComponentData:最基本的,也是使用最多的接口,没有特殊需求的组件都可以实现这个接口。用.....

ECS之Component组件

洪流学堂,让你快人几步。你好,我是跟着大智学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:同时包含ISharedComponentDataISystemStateComponentData中的功能。
  • Blob assets:技术上来说不是一个component,你可以使用blob assets存储数据。Blob asset可以被一个或者多个component通过BlobAssetReference只读引用。你可以使用blob asset共享数据,也可以在C# job中访问。

之前在ECS核心概念那一节说过,EntityManager会使用原型(archetype)来组织不同的component组合。相同原型的entity在物理内存上都在一起,叫做一个内存块。同一个内存块中都是相同的组件原型。

ECS之Component组件

上面这个图说明了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()方法。此外,出于序列化的目的,托管组件需要有默认无参构造方法。

你必须在主线程上设置组件的值,可以使用 EntityManagerEntityCommandBuffer。由于组件是引用类型,因此与ISharedComponentData不同,你可以更改组件的值而无需在块之间移动实体。(之前咱们知道修改ISharedComponentData的值会导致entity所在的内存块发生变化)

但是,尽管在逻辑上将托管组件与值类型的组件分开存储,但它们都会有EntityArchetype原型定义。这样,向实体添加新的托管组件仍然会导致ECS创建新的原型(如果尚不存在匹配的原型),并将实体移至新的Chunk。

总结

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

扩展阅读

【扩展学习】洪流学堂公众号回复DOTS可以阅读本系列所有文章,更有视频教程等着你!


呼~ 今天小新絮絮叨叨的真是够够的了。没讲清楚的地方欢迎评论,咱们一起探索。

我是大智(vx:zhz11235),你的技术探路者,下次见!

别走!点赞收藏哦!

好,你可以走了。

本文地址:https://blog.csdn.net/zhenghongzhi6/article/details/109649521