Unity ECS+Jobs System笔记 ECS——Component(三)

来源:https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/index.html
我会对官方文档内容略作整理,有需要可以查看官方文档

2.组件——Component

组件中储存着与实体相关的数据(通过实体来索引组件及其数据)
ECS中的组件是具有以下接口的结构:

  • IComponentData
  • ISharedComponentData
  • ISystemStateComponentData
  • ISharedSystemStateComponentData

EntityManager会组织所有实体中的组件的组合并构成Archetype(原型),它将所有具有相同原型的的实体的组件储存在一块内存中,称之为chunk(区块),一个区块中的所有组件具有相同的原型。
SharedComponent(共享组件)是一种特殊的组件,可以使用它其中的一个特殊值来细分实体(用于区别其他实体),当为一个实体添加一个共享组件,EntityManager就会将所有特殊值相同的实体放置在相同的区块,共享组件允许系统一起处理相似的实体。
注意: 过度使用共享组件可能会导致较差的块利用率,因为它涉及基于原型和每个共享组件字段的每个唯一值组合扩展所需的内存块数量,所以需要避免向共享组件添加不必要的字段,可以通过Entity Debugger来查看当前的块利用率。
如果对实体添加或删除组件、更改SharedComponent的值,EntityManager会将实体移动到其他Chunk,在必要时会创建新Chunk。
系统状态组件的行为类似于普通组件或共享组件,但在销毁实体时,EntityManager不会删除任何系统状态组件,也不会在删除实体ID之前回收它们,这种差异允许系统在销毁实体时清理其内部状态或释放资源。

2.1、组件介绍

2.1.1、ComponentData

Unity中的ComponentData(也是标准ECS系统中的一个Component)对一个实体而言,它是只包括了数据的结构,他不能包含方法;对比旧的Unity系统而言,他有点像旧的Component系统,但是是一个只包含数据的Component。
UnityECS提供了一个IComponentData接口供使用。

  • 传统的Unity组件(包括MonoBehaviour)是一个面向对象的类,包含了数据和方法
  • IComponentData是一个ECS组件,因此它不包含任何方法,只包含数据
  • IComponentData是一个结构体而不是一个类,这意味着默认情况下它是通过值而不是通过引用复制的,需要使用以下模式来修改数据:
var transform = group.transform[index]; // Read

transform.heading = playerInput.move; // Modify
transform.position += deltaTime * playerInput.move * settings.playerMoveSpeed;

group.transform[index] = transform; // Write
2.1.2、SharedComponentData

IComponentData适用于实体之间不同的数据,例如存储所在的World,而ISharedComponentData在许多实体具有共同点时使用,例如在Boid演示中,需要实例化来自同一Prefab的RenderMesh的许多Boid实体,这些实体完全相同。

[System.Serializable]
public struct RenderMesh : ISharedComponentData
{
    public Mesh                 mesh;
    public Material             material;

    public ShadowCastingMode    castShadows;
    public bool                 receiveShadows;
}

最棒的是使用ISharedComponentData时,每个实体的内存成本几乎为零,我们使用ISharedComponentData时会将具有相同InstanceRenderer数据的实体分组在一起,然后有效地提取所有矩阵进行渲染。生成的代码简单有效,因为要访问的数据的布局是完全相同的。
使用SharedComponentData的注意点:

  • 具有相同SharedComponentData的实体被相同的Chunk给组织起来,而这些SharedComponentData的索引是通过Chunk进行储存的,而不是每个实体,因此SharedComponentData对每个实体的内存开销而言是零
  • 使用EntityQuery可以迭代所有具有相同类型的实体
  • 此外,可以使用EntityQuery.SetFilter()迭代具有特定SharedComponentData值的实体,由于数据布局,此迭代具有较低的开销
  • 使用EntityManager.GetAllUniqueSharedComponents可以取回所有添加在现有实体上的唯一SharedComponentData
  • SharedComponentData会自动引用计数
  • SharedComponentData应该很少改变,如果需要改变,就需要使用memcpy将那个实体所有的ComponentData拷贝到一个不同的Chunk上去
2.1.3、SystemStateComponents

SystemStateComponentData的目的是允许你跟踪系统内部的资源,并且有机会适当的创建和销毁所需的资源而不需要任何的回调
SystemStateComponentData和SystemStateSharedComponentData类似ComponentData和SharedComponentData
除了一个重要的方面:

  1. SystemStateComponentData不会在销毁实体时被删除

DestroyEntity的作用是:

  1. 根据特定的实体ID找到其所有组件的引用
  2. 删除这些组件
  3. 回收实体ID以便重用

但是,如果SystemStateComponentData存在,则不会被移除。这使系统有机会清除与实体ID相关联的任何资源或状态,只有当实体中所有的SystemStateComponentData被删除后,才会重用实体ID 。

动机
  • 系统有时需要保持ComponentData的内部状态,例如,分配资源时
  • 系统需要能够管理该状态,因为值和状态更改是由其他系统进行的,例如,当组件中的值更改时,或添加或删除相关组件时
  • “无回调”是ECS设计规则的重要元素
概念

所期望的SystemStateComponentData的一般用途是镜像用户组件,提供内部状态
例如,给定:

  • FooComponent(ComponentData,用户分配)
  • FooStateComponent(SystemComponentData,系统分配)
检测组件添加

当用户添加FooComponent时,FooStateComponent不存在
FooSystem会在没有FooStateComponent的状态下更新查询FooComponent组件
推断他们已经被添加了
此时,FooSystem将添加FooStateComponent和任何所需的内部状态

检测组件删除

当用户删除FooComponent时,FooStateComponent依然存在
FooSystem会在没有FooComponent的状态下更新查询FooStateComponent组件
推断他们已经被删除了
此时,FooSystem将删除FooStateComponent并修复任何所需的内部状态

检测销毁实体

DestroyEntity的作用是:

  1. 根据特定的实体ID找到其所有组件的引用
  2. 删除这些组件
  3. 回收实体ID以便重用

但是,SystemStateComponentData如果不删除,则在DestroyEntity删除最后一个组件之前不会回收实体ID。这使系统有机会以与移除组件完全相同的方式清理内部状态,我们在创建SystemStateComponentData的时候会给予一个ReadOnly的修饰符。

SystemStateComponent

SystemStateComponentData类似于ComponentData的用法

struct FooStateComponent : ISystemStateComponentData
{
}

SystemStateComponentData的修饰符的使用方式和一个普通组件的使用方式相同(using private, public, internal),然而,作为例外,一般而言,

SystemStateSharedComponent

SystemStateSharedComponentData类似于SharedComponentData的用法

struct FooStateSharedComponent : ISystemStateSharedComponentData
{
	public int Value;
}
2.1.4、Dynamic Buffers

DynamicBuffer是一种与实体相关联的,可变大小的弹性的组件,它表现为一种可以承受一定数量元素的组件,但是当它的内部容量耗尽就会分配一块堆内存。使用该方法时,内存管理是自动的,内存由DynamicBuffers联系,而DynamicBuffers由EntityManager管理,所以当删除DynamicBuffer组件时,也会自动释放任何关联的堆内存。
DynamicBuffers取代了已经被删除的fixed array

声明Buffer的元素类型

声明一个Buffer可以使用要放入的元素类型声明它

// This describes the number of buffer elements that should be reserved
// in chunk data for each instance of a buffer. In this case, 8 integers
// will be reserved (32 bytes) along with the size of the buffer header
// (currently 16 bytes on 64-bit targets)
[InternalBufferCapacity(8)]
public struct MyBufferElement : IBufferElementData
{
    // These implicit conversions are optional, but can help reduce typing.
    public static implicit operator int(MyBufferElement e) { return e.Value; }
    public static implicit operator MyBufferElement(int e) { return new MyBufferElement { Value = e }; }

    // Actual value each buffer element will store.
    public int Value;
}

虽然这看上去很奇怪,但是这种描述方式由两个好处:

  • 它支持多个float3类型的DynamicBuffer或任何其他常见的值类型,可以添加任意数量的利用相同值类型的数据到Buffers中去,只要元素被独一无二的包装在顶级结构体中
  • 我们可以在EntityArchetypes中包含Buffer元素类型,它通常会表现为一个组件
向实体添加Buffer类型

使用向组件类型添加Buffer类型组件的常规方法:

  • 使用AddBuffer()
entityManager.AddBuffer<MyBufferElement>(entity);
  • 使用原型
Entity e = entityManager.CreateEntity(typeof(MyBufferElement));
访问Buffer类型

有几种方法可以访问DynamicBuffers,这是对常规组件数据的并行访问方法

  • 仅主线程直接访问
DynamicBuffer<MyBufferElement> buffer = entityManager.GetBuffer<MyBufferElement>(entity);
基于实体的访问
  • 通过JobComponentSystem对每一个实体组件查询Buffers
    var lookup = GetBufferFromEntity<EcsIntElement>();
    var buffer = lookup[myEntity];
    buffer.Append(17);
    buffer.RemoveAt(0);
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
 一: 什么是ECS?       ECS是一种新的架构模式(当然是针对Unity本身来说),这是一个取代GameObject / Component 的模式。 其模式遵循组合优于继承原则,游戏内的每一个基本单元都是一个实体,每个实体又由一个或多个组件构成,每个组件仅仅包含代表其特性的数据(即在组件中没有任何方法)。系统便是来处理拥有一个或多个相同组件的实体集合的工具,其只拥有行为(即在系统中没有任何数据)。       ECS旨在比GameObject / MonoBehaviour更容易处理大量物体。ECS由于是面向数据的设计,所以很容易并行高速处理,以及与Unity开发的C#JobSystem/Burst Compiler一起工作,从而获取更高的项目运行性能。二:ECS总体发展历史       目前Unity公司由Mike Acton 大佬主持DOD(Data Oriented Design 面向数据设计)的全面改革与推进工作,目的是旨在进一步推进Unity系统本身与Unity项目的执行效率。    ECS总体发展:    A: 在Unity2018引入Entities之前,其实ECS框架早已经出现,但是ECS还是因为守望先锋采用了这个框架才被我们所熟知,后来Git上面出现了一个Entitas的插件可以直接用在Unity上面。Entitas-Unity(有多个语言的port版本,Entitas-Unity下统一称为Entitas) 是 Unity的一个ECS(Entity/Component/System)框架,是在github上面的一个开源项目。    B: 经过Unity公司的认可与改造,目前Unity2018.x 版本已经通过 Package Manager 提供了Unity版本的ECS插件,名称为:“Entities”。    C: Unity2019.x 版本,对“Entities”插件的API进行了进一步改造与完善,以更规范的API实现更加高效的与规范的编程模式。 三:ECS(一)轻松入门篇       本“ECS入门篇”旨在全面讲解ecs 的相关理论与简单Hello World 的编程模式,且通过Unity2018.x与Unity2019.x 两个不同API的编程模式,初步讲解ECS的编程规范与模式规范。        需要进一步学习ECS的小伙伴们,可以关注后续“ECS(二)小试牛刀”篇的进一步讲解。   《Unity ECS(二) 小试牛刀》https://edu.csdn.net/course/detail/27096 

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值