ECS 简略版说明一:Entities and components

news2024/12/25 9:11:29

目录

Entities and components

Worlds and EntityManagers

Archetypes

Chunks

Queries

Entity ID's

IComponentData

Managed IComponentData components

DynamicBuffer components

Aspects

Allocator overview

Allocator.Temp

Allocator.TempJob

Allocator.Persistent

Deallocating an allocator

IsCreated property

NOTE


 

Entities and components

entity 是一个轻量级、未托管的 GameObject. Entities 和 GameObjects 相似但又有一些不同:

  • GameObject是托管的, entity 不是一个托管对象,而是一个唯一标识符号(id)
  • entity上依附的components 通常是值类型 struct values.
  • 一个 entity 只能有一个给定类型的组件. gameobject 上面可挂载多个同一个component
  • 尽管entity  component 可以包含方法,但通常不鼓励这样做
  • 一个实体没有parent概念。相反,标准的Parent组件包含对另一个实体的引用

Entity 组件是通过实现下面的接口实现的:

Kind of componentDescription
IComponentDataDefines the most common, basic kind of component type.
IBufferElementDataDefines a dynamic buffer (growable array) component type.
ISharedComponentDefines a shared component type, whose values can be shared by multiple entities.
ICleanupComponentDefines a cleanup component type, which facilitates proper setup and teardown of resources.

除此之外还有 (ICleanupSharedComponent and ICleanupBufferElementData) and chunk components (which are defined with IComponentData but added and removed from entities by a different set of methods).

A component type defined with IComponentData or IBufferElementData can be made 'enableable' by also implementing IEnableableComponent.

Worlds and EntityManagers

 World 是实体的集合. 一个 entity's ID 在它所在的world中是唯一的,两个world中拥有相同id的实体,没有任何关联。

world 拥有自己的 systems 集合, 是在main thread 上运行的,通常一帧一次,实体通常被所在world中的system来访问, (and the jobs scheduled by those systems), 但是这不是强制

The entities in a world are created, destroyed, and modified through the world's EntityManager. 方法有:

MethodDescription
CreateEntity()Creates a new entity.
Instantiate()Creates a new entity with a copy of all the components of an existing entity.
DestroyEntity()Destroys an existing entity.
AddComponent<T>()Adds a component of type T to an existing entity.
RemoveComponent<T>()Removes a component of type T from an existing entity.
HasComponent<T>()Returns true if an entity currently has a component of type T.
GetComponent<T>()Retrieves the value of an entity's component of type T.
SetComponent<T>()Overwrites the value of an entity's component of type T.
📝 NOTE
CreateEntityInstantiateDestroyEntityAddComponent, and RemoveComponent are structural change operations.

Archetypes

archetype 表示world中的一个特殊的组件的组合,拥有相同组件类型的实体,被认为archetypes是一致的。

实际上,添加或删除实体的组件会改变实体所属的原型,这就需要entitymanager将实体及其组件从旧原型移动到新原型,如果新的archetypes 不存在,EntityManager会创建一个

⚠ IMPORTANT
Moving too many entities between archetypes too frequently can add up to significant costs.

the archetype is only destroyed when its world is destroyed.

Chunks

相同archetypes 的实体,存储的地方称之为chunk, 每个chunk 16kb大小,每一个chunk 至多存储 128 entities (精确的数量取决于原型中组件类型的数量和大小).

在chunk 中 ,entity ID's以及每种类型的component,是分别存储在数组中的

For example, in the archetype for entities which have component types A and B, each chunk will store three arrays:

  • one array for the entity ID's
  • ...a second array for the A components
  • ...and a third array for the B components.

EntityManager来处理chunk 的创建和销毁:

  • EntityManager 只当一个实体被添加到一个已经存在的块都满了的情况下,才会创建一个新的块.
  •  EntityManager only destroys a chunk when the chunk's last entity is removed.和archetypes 的销毁不一样

Queries

EntityQuery 表示一个查询

📝 NOTE
The archetypes matching a query will get cached until the next time a new archetype is added to the world. Because the set of existing archetypes in a world tends to stabilize early in the lifetime of a program, this caching usually helps make the queries much cheaper.

Entity ID's

entity ID表示一个 Entity.

为了根据ID查询 entities , world 的EntityManager 维持了一个 entity metadata 的数组. 每个实体ID都对应该数组中的一个索引值,metadata中存储了一个指向存储实体所在块的指针,以及块中实体的索引. When no entity exists for a particular index, the chunk pointer at that index is null. Here, for example, no entities with indexes 1, 2, and 5 currently exist, so the chunk pointers in those slots are all null:

 

为了允许实体索引在实体被销毁后被重用, version number 在销毁后 incremented, 所以如果一个ID的version number 和当前存储的不一致,那么该 ID 指向的实体一定是被销毁了或者不存在。

IComponentData

IComponentData struct 期望是 unmanaged,所以不能包含 managed 字段类型. Specifically, the allowed field types are:

  • Blittable types (可移位类型,即 在托管和非托管类型上不需要做额外的操作)
  • bool
  • char
  • BlobAssetReference<T>, a reference to a Blob data structure
  • Collections.FixedString, a fixed-sized character buffer
  • Collections.FixedList
  • Fixed array (only allowed in an unsafe context)
  • Struct types that conform to these same restrictions.

有一个没有任何数据的组件叫 tag component. 它在做查询标记上很有用, For example, if all of our entities representing monsters have a Monster tag component, a query for the Monster component type will match all the monster entities.

Managed IComponentData components

一个类实现了 IComponentData 接口,表示它是一个 managed component type. 不像非托管的IComponentData structs一样, 这些 managed components 可以存储任何 managed objects.

一般来说,托管组件类型应该只在真正需要时使用,因为与非托管组件相比,它们会产生一些沉重的成本:

  • Like all managed objects, managed components cannot be used in Burst-compiled code.
  • Managed objects cannot normally be used safely in jobs.
  • 托管组件不是直接存储在chunk中,而是存储在一个大的数组里面,chunk里面只存储它在数组中的索引
  • 与所有托管对象一样,创建托管组件会产生垃圾收集开销

ICloneable, 当复制实例本身时,它所包含的任何资源都可以被正确地复制。  

IDisposable, 从实体中移除实例或销毁实体.

DynamicBuffer components

DynamicBuffer 是一个可变大小的数组组件类型

继承自IBufferElementData,即可声明一个Dynamic buffer.

The buffer of each entity stores a Length, a Capacity, and a pointer:

  • The Length 包含元素的真实数量,从0开始
  • The Capacity 数组的容量. It starts out matching the internal buffer capacity (which defaults to 128 / sizeof(Waypoint) but can be specified by the InternalBufferCapacity attribute on the IBufferElementData struct). Setting Capacity resizes the buffer.
  • The pointer indicates the location of the buffer's contents. Initially it is null, signifying that the contents are stored directly in the chunk. If the capacity is set to 超过了 the internal buffer capacity, a new larger array is allocated outside of the chunk, the contents are 复制到 external array, and the pointer is set to point to this new array. If the length of the buffer 超过了 the capacity of the external array, then the contents of the buffer are copied to another new, larger array outside the chunk, and the old array is disposed. The buffer can also be shrunk.

The internal buffer capacity and the external capacity (if present) are deallocated when the EntityManager destroys the chunk itself.

📝 NOTE
当dynamic buffer存储在chunk外时,chunk内部的空间就浪费掉了,访问元素通过额外的指针来访问,可以通过让元素的数量不超过 internal capacity避免,但是通常数量都是非常大的,这时可以设置internal capacity to 0, 表示所有的元素都存储在chunk外, 这样的只有访问时用额外的指针的代价,而避免了chunk内部空间的浪费

The EntityManager has these key methods for using dynamic buffers:

MethodDescription
AddComponent<T>()Adds a component of type T to an entity, where T can be a dynamic buffer component type.
AddBuffer<T>()Adds a dynamic buffer component of type T to an entity; returns the new buffer as a DynamicBuffer<T>.
RemoveComponent<T>()Removes the component of type T from an entity, where T can be a dynamic buffer component type.
HasBuffer<T>()Returns true if an entity currently has a dynamic buffer component of type T.
GetBuffer<T>()Returns an entity's dynamic buffer component of type T as a DynamicBuffer<T>.

DynamicBuffer<T> represents the dynamic buffer component of type T of an individual entity. Its key properties and methods include:

Property or MethodDescription
LengthGets or sets the length of the buffer.
CapacityGets or sets the capacity of the buffer.
Item[Int32]Gets or sets the element at a specified index.
Add()Adds an element to the end of the buffer, resizing it if necessary.
Insert()Inserts an element at a specified index, resizing if necessary.
RemoveAt()Removes the element at a specified index.

为了确保 job 安全,DynamicBuffer 的内容在scheduled jobs 不能访问,只能访问 只读的 buffer component type,  main thread 可以读取buffer。

structural change之后,需要重新获取该buffer.

DynamicBuffer<T> can be 'reinterpreted'. The target reinterpretation element type must have the same size as T.

Aspects

aspect 类似于对象封装器,封装了多个component,Aspects 可用于简化查询和与组件相关的代码,比如,The TransformAspect, groups together the standard transform components (LocalTransform, ParentTransform, and WorldTransform).

query中包含 aspect 和包含相同组件类型是一样的。

aspect is defined as a readonly partial struct implementing IAspect. The struct can contain fields of these types:

Field typeDescription
EntityThe wrapped entity's entity ID.
RefRW<T> or RefRO<T>A reference to the wrapped entity's T component.
EnabledRefRW<T> and EnabledRefRO<T>A reference to the enabled state of the wrapped entity's T component.
DynamicBuffer<T>The wrapped entity's dynamic buffer T component.
Another aspect typeThe containing aspect will encompass all the fields of the 'embedded' aspect.

These EntityManager methods create instances of an aspect:

MethodDescription
GetAspect<T>Returns an aspect of type T wrapping an entity.
GetAspectRO<T>Returns a readonly aspect of type T wrapping an entity. A read-only aspect throws an exception if you use any method or property that attempts to modify the underlying components.

Aspect instances can also be retrieved by SystemAPI.GetAspectRW<T> or SystemAPI.GetAspectRO<T> and accessed in an IJobEntityor a SystemAPI.Query loop.

⚠ IMPORTANT
You should generally get aspect instances via SystemAPI rather than the EntityManager: unlike the EntityManager methods, the SystemAPI methods register the underlying component types of the aspect with the system, which is necessary for the systems to properly schedule jobs with every dependency they need.

Allocator overview

allocator 负责分配 unmanaged memory.  Collections package 包含三种allocator:

  • Allocator.Temp: The fastest allocator, for short-lived allocations. You can't pass this allocator to a job.
  • Allocator.TempJob: A short-lived allocator that you can pass into jobs.
  • Allocator.Persistent: The slowest allocator for indefinite lifetime allocations. You can pass this allocator to a job.

Allocator.Temp

每一帧,main thread 都会创建一个 Temp allocator ,在帧结束时释放掉,每一个 job 也为每个线程创建一个Temp allocator,在job 结束时释放掉,Temp allocations 最小是64 bytes.

Temp allocations are only safe to use in the thread and the scope where they were allocated. While you can make Temp allocations within a job, you can't pass main thread Temp allocations into a job. For example, you can't pass a native array that's Temp allocated in the main thread into a job.

Allocator.TempJob

必须在4帧内释放掉 TempJob allocations . TempJob allocations 最小是 16 bytes.

For Native- collection types, the disposal safety checks throw an exception if a TempJob allocation lasts longer than 4 frames. For Unsafe- collection types, you must deallocate them within 4 frames, but Unity doesn't perform any safety checks to make sure that you do so.

Allocator.Persistent

Because Persistent allocations can remain indefinitely, safety checks can't detect if a Persistent allocation has outlived its intended lifetime. As such, you must deallocate a Persistent allocation when you no longer need it. Persistent allocations 最小是 16 bytes.

Deallocating an allocator

Each collection retains a reference to the allocator that allocated its memory. 释放的方法:

  • An Unsafe- collection's Dispose().
  • Native- collection's Dispose( ).
  • An enumerator's Dispose method does nothing. The method exists only to fulfill the IEnumerator<T> interface.

To dispose a collection after the jobs which need it have run, you can use the Dispose(JobHandle) method. This creates and schedules a job which disposes of the collection, and this new job takes the input handle as its dependency. Effectively, the method defers disposal until after the dependency runs:

NativeArray<int> nums = new NativeArray<int>(10, Allocator.TempJob);

// Create and schedule a job that uses the array.
ExampleJob job = new ExampleJob { Nums = nums };
JobHandle handle = job.Schedule();

// Create and schedule a job that will dispose the array after the ExampleJob has run.
// Returns the handle of the new job.
handle = nums.Dispose(handle);

IsCreated property

The IsCreated 在下面的情况返回false:

  • Immediately after creating a collection with its default constructor.
  • After Dispose has been called on the collection.
NOTE

不需要使用默认的 collection's 构造器,The constructor is only available because C# requires all structs have a public default constructor.

调用 Dispose 只设置调用的struct  IsCreated 为false, 对拷贝的 struct 并不生效. 下面情况IsCreated仍为true

  • Dispose was called on a different copy of the struct.
  • The underlying memory was deallocated via an alias.


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/686818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL性能测试及调优中的死锁处理方法

目录 前言&#xff1a; 1、死锁检测 2、死锁避免 3、死锁解决 前言&#xff1a; MySQL死锁是指多个会话同时请求相同资源时发生的一种资源争用现象&#xff0c;导致会话无法继续执行。死锁的发生会导致事务无法提交或者回滚&#xff0c;影响应用程序的正常运行。因此&#xff0…

consul简介与安装

一、Consul简介 Consul 是 HashiCorp 公司推出的开源产品&#xff0c;用于实现分布式系统的服务发现、服务隔离、服务配置&#xff0c;这些功能中的每一个都可以根据需要单独使用&#xff0c;也可以同时使用所有功能。Consul 官网目前主要推 Consul 在服务网格中的使用。 与其…

【人工智能】教你如何让 AI 赢得贪吃蛇游戏----强化学习(初探)

人工智能--AI贪吃蛇&#xff0c;每一个代码都有详细的注释&#xff0c;希望多多收藏&#xff0c;点赞&#xff0c;评论 1.前言&#xff1a;训练ai玩游戏的可行性2.代码实现思路&#xff1a;3.代码完整实现3.1 Game Game.py 完整实现3.1.1 安装pygame库3.1.2 编写游戏逻辑代码 3…

Sangfor华东天勇战队:某咖啡还原密钥

最近学习了密钥还原&#xff0c;复现下并记录思路 function wbaes_encrypt_ecb(){var module_base Module.findBaseAddress("libcryptoDD.so")var func_base module_base.add(0x17BD41)Interceptor.attach(func_base,{onEnter:function (args){console.log("E…

风控引擎如何快速接入不同的数据源?

目录 数据是风控引擎的重要组成 风控引擎的数据接入 风控引擎是一种基于数据分析和机器学习算法的系统&#xff0c;能够实时识别和处理各种风险问题&#xff0c;适用于金融、电商、智能制造、交通运输等各领域&#xff0c;能够提高企业的风险管理水平和业务效率。 风控引擎主…

我心中的TOP1编程语言—JavaScript

作为一名研发工程师&#xff08;程序员&#xff09;&#xff0c;平时工作中肯定会接触或了解很多编程语言。每个人都会有自己工作中常用的语言&#xff0c;也会有偏爱的一些编程语言。而我心中的最爱&#xff0c;毫无疑问&#xff0c;就是 JavaScript。 JavaScript 是一门编程…

23. 数据结构之位图

前言 之前在讲散列表的时候&#xff0c;提到过位图的概念。位图&#xff08;Bitmap&#xff09;作为一种特殊的数据结构&#xff0c;它使用一系列位来表示数据&#xff0c;每个位只有两个状态&#xff08;0或1&#xff09;。由于它的高效性和节省空间的特性&#xff0c;位图在…

SpringBoot的配置环境属性

SpringBoot的配置环境属性 在本文中&#xff0c;我们将讨论SpringBoot的配置环境属性。我们将了解如何使用这些属性来配置我们的应用程序&#xff0c;以便在不同的环境中运行。我们还将了解如何使用SpringBoot的配置文件来管理这些属性。最后&#xff0c;我们将介绍一些最佳实…

java的嵌套类(nested class)、内部类(inner class)的区别

嵌套类即nested class&#xff0c;内部类即Inner class。 概括来说&#xff0c;嵌套类的概念比内部类概念大。嵌套类包含内部类和非内部类。一个内部类一定是一个嵌套类&#xff0c;但一个嵌套类不一定是一个内部类。 在一个类内部或者接口内部声明的类是嵌套类。 下面这些类是…

《Java面向对象程序设计教程》课后编程题

文章目录 第 1 章 Java 概述第 2 章 Java 语言基础第 3 章 Java 面向对象编程第 4 章 Java 图形用户界面程序设计第 5 章 Applet 设计第 6 章 Java 输入输出流与文件操作第 7 章 Java 的多线程机制第 9 章 常用工具类与集合框架 第 1 章 Java 概述 试编写 Java 程序&#xff0…

Android studio新建项目运行遇到的问题

文章目录 The emulator process for AVD xxx has terminated原因&#xff08;环境变量问题&#xff09;解决其他原因 新建的练习项目更改SDK默认位置更改方法 The emulator process for AVD xxx has terminated 运行虚拟机时报此错误 原因&#xff08;环境变量问题&#xff0…

555 timer circuit

#1, Block & principle 1.1&#xff0c; The threshold and trigger levels normally are two- thirds and one-third, respectively, of VCC.(分压&#xff09; 1.2&#xff0c;These levels can be altered by use of the control-voltage terminal. When the trigger in…

生成式 AI:通信服务提供商云转型的下一阶段

【本文由Cloud Ace 整理发布。Cloud Ace 是谷歌云全球战略合作伙伴&#xff0c;拥有 300 多名工程师&#xff0c;也是谷歌最高级别合作伙伴&#xff0c;多次获得 Google Cloud 合作伙伴奖。作为谷歌托管服务商&#xff0c;我们提供谷歌云、谷歌地图、谷歌办公套件、谷歌云认证培…

spring boot引入swagger报错处理

目录 1. 报错说明 2. 查找原因 2.1 此前笔者的代码 3. 问题解决说明 4. 解决方案 4.1 在pom.xml引入springdoc包 4.2 创建配置文件&#xff08;可省略&#xff09; 4.3 在controller加入注解 4.4 查看接口文档 4.5 常用注解 1. 报错说明 在java项目中引入swagger 2.9.2…

Linux设置进程名称(标题) ( 7) -【Linux通信架构系列 】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the…

技术管理三板斧之第一板斧拿结果-定目标

一、现状&#xff1a; 去年年底今年年初&#xff0c;帮助一家公司做了一次大的系统重构&#xff0c;30多小伙伴&#xff0c;经历一次洗礼&#xff0c;对产品定位&#xff0c;技术选型&#xff0c;目标制定&#xff0c;任务分配&#xff0c;协同开发&#xff0c;测试上线&#x…

JS 启动一个计时器来跟踪某一个操作的占用时长

文章目录 需求分析代码 需求 JS 中想要记录一个操作的占用时长 分析 可以启动一个计时器console.time(name:string)来跟踪某一个操作的占用时长。每一个计时器必须拥有唯一的名字&#xff0c;页面中最多能同时运行 10,000 个计时器。 当以此计时器名字为参数调用 console.timeE…

蓝桥杯专题-试题版-【龟兔赛跑预测】【回形取数】【阶乘计算】【矩形面积交】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

计算机的性能指标

计算机的主要性能指标: 机器字长: 指参与运算的基本数位, 它是由加法器, 寄存器的数位决定的, 所以及其资产一般等于内部寄存器的大小 数据路通宽度: 数据总线一次能够并行传送信息的位数, 这里所说的数据通路宽度实际是指外部数据总线的宽度 主存容量: 一个存储器所能存储的…

C语言王国探险记之常量的四大护卫

王国探险记系列 文章目录&#xff08;3&#xff09; 前言 一、常量是什么&#xff1f; 二、常量的第一护卫&#xff1a;字面常量 1.什么是字面常量? 三、常量的第二护卫&#xff1a;const修饰的常变量 1.什么是const修饰的常变量? 2&#xff0c;证明const修饰的常变量…