Unity ECS+Jobs System笔记 访问数据3(七)
来源:https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/index.html
我会对官方文档内容略作整理,有需要可以查看官方文档
4、ComponentSystem
你可以使用ComponentSystem处理数据,ComponentSystem在主线程上运行,因此不利用多个CPU内核,在以下情况下使用ComponentSystems:
- 调试模式,有时更容易在主线程上观察代码运行时发生了什么,例如,可以记录Debug并绘制图形
- 当系统需要访问只能在主线程上运行的API时,这可以帮助你逐步将游戏系统转换为ECS,而不必从一开始就重写所有内容
- 系统执行的工作量所需的开销比使用Job系统少时
特别注意: 进行结构更改会强制完成所有作业,这一事件称为同步点,因为系统在等待同步点时无法利用所有可用的CPU内核所以可能导致性能下降。在ComponentSystem中,你应该使用一个post-update指令缓冲,虽然同步点仍然会出现,但所有结构变化都是批量发生的,因此影响略小。为了获得最大的运行效率,请使用JobComponentSystem和EntityCommandBuffer。在创建大量实体时,还可以使用单独的World创建实体,然后将这些实体传输到主游戏世界
通过ForEach进行迭代
ComponentSystem提供了Entities.ForEach函数,该函数简化了迭代一组实体的任务,在系统的OnUpdate()函数中调用ForEach,传入一个lambda函数,该函数会将相关组件作为参数并执行必要的工作
这有一个HelloCube的ForEach例子,为具有RotationQuaternion和RotationSpeed组件的实体设置旋转动画:
public class RotationSpeedSystem : ComponentSystem { protected override void OnUpdate() { Entities.ForEach( (ref RotationSpeed rotationSpeed, ref RotationQuaternion rotation) => { var deltaTime = Time.deltaTime; rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * deltaTime)); }); }
ForEach的lambda函数有最多六种类型的组件作为参数进行使用
如果你需要改变现有实体的结构,你可以添加一个实体组件到你的lambda函数的参数上,然后使用它来向你的ComponentSystem的PostUpdateCommands缓冲添加指令(如果允许在lambda函数内部进行结构更改,则可能会更改正在迭代的数组中的数据,从而导致错误)
例如,如果要从旋转速度当前为零的任何实体中删除RotationSpeed组件,可以按如下方式更改ForEach函数:
Entities.ForEach( (Entity entity, ref RotationSpeed rotationSpeed, ref RotationQuaternion rotation) => { var __deltaTime __= Time.deltaTime; rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), rotationSpeed.RadiansPerSecond * __deltaTime__)); if(math.abs(rotationSpeed.RadiansPerSecond) <= float.Epsilon) //Speed effectively zero PostUpdateCommands.RemoveComponent(entity, typeof(RotationSpeed)); });
OnUpdate()函数完成后,系统在更新后缓冲区后执行命令
Fluent Queries(快速查询)
你可以使用fluent-style查询来约束ForEach的lambda函数,使其只对那些满足某些约束的实体作用,这些查询可以指定是否对any、all或none性质的实体作用,约束可以链接在一起,对于C#的LINQ系统的用户来说应该非常熟悉
请注意,作为参数传递给ForEach的lambda函数的任何组件都会自动包含在WithAll中,并且不能显式包含在WithAll,WithAny或WithNone部分中
- 一个WithAll约束可以指定拥有所有组件的实体,例如在下面这个查询中,ComponentSystem为具有“Rotation”和“Scale”组件的所有实体执行lambda函数
Entities.WithAll<Rotation, Scale>().ForEach( (Entity e) => { // do stuff });
将WithAll用于必须存在于实体上但不需要读取或写入的组件(添加一个作为访问ForEach的lambda函数的参数所需要的组件),例如:
Entities.WithAll<SpinningTag>().ForEach( (Entity e, ref Rotation r) => { // do stuff });
- 一个WithAny约束可以指定在组件中至少拥有一个组件的实体,例如:ComponentSystem为同时具有Rotation和Scale组件,以及具有RenderDataA或RenderDataB(或两者)的实体执行以下lambda函数:
Entities.WithAll<Rotation, Scale>().WithAny<RenderDataA, RenderDataB>().ForEach( (Entity e) => { // do stuff });
请注意,无法通过WithAny知道一个指定实体有哪些组件,如果需要根据存在的组件来分别处理实体,则必须为每种情况创建特定查询,或者将JobComponentSystem与IJobChunk一起使用
- 一个WithNone约束可以排除在组件中至少拥有一个组件的实体,例如:ComponentSystem为没有Rotation组件的所有实体执行以下lambda函数:
Entities.WithNone<Rotation>().ForEach( (Entity e) => { // do stuff });
此外,你也可以通过指定WithAnyReadOnly和WithAllReadOnly来筛选那些组件,但是要确保它们是只读组件,这将确保它们没有标记在写入和ID被更改的时候
查询选项
你还可以对With设置一些额外选项:
条件 | 描述 |
---|---|
Default | 默认 |
IncludePrefab | 该查询不会隐式的排除具有Prefab组件的实体 |
IncludeDisabled | 该查询不会隐式的排除具有Disabled(已禁用)组件的实体 |
FilterWriteGroup | 查询会根据查询中指定的组件的WriteGroupAttribute属性筛选所选实体 |
ComponentSystem查询那些没有Rotation组件,包括Disabled(被禁用)的实体:
Entities.WithNone<Rotation>().With(EntityQueryOptions.IncludeDisabled).ForEach( (Entity e) => { // do stuff });
5、Manual iteration
你还可以在NativeArray中显式请求所有chunk,并使用诸如IJobParallelFor来处理它们的Job,如果你需要以某种方式管理chunk,而不是简单地迭代EntityQuery中的所有chunk,则建议使用此方法。如:
public class RotationSpeedSystem : JobComponentSystem { [BurstCompile] struct RotationSpeedJob : IJobParallelFor { [DeallocateOnJobCompletion] public NativeArray<ArchetypeChunk> Chunks; public ArchetypeChunkComponentType<RotationQuaternion> RotationType; [ReadOnly] public ArchetypeChunkComponentType<RotationSpeed> RotationSpeedType; public float DeltaTime; public void Execute(int chunkIndex) { var chunk = Chunks[chunkIndex]; var chunkRotation = chunk.GetNativeArray(RotationType); var chunkSpeed = chunk.GetNativeArray(RotationSpeedType); var __instanceCount __= chunk.Count; for (int i = 0; i < instanceCount; i++) { var rotation = chunkRotation[i]; var speed = chunkSpeed[i]; rotation.Value = math.mul(math.normalize(rotation.Value), quaternion.AxisAngle(math.up(), speed.RadiansPerSecond * DeltaTime)); chunkRotation[i] = rotation; } } } EntityQuery m_group; protected override void OnCreate() { var query = new EntityQueryDesc { All = new ComponentType[]{ typeof(RotationQuaternion), ComponentType.ReadOnly<RotationSpeed>() } }; m_group = GetEntityQuery(query); } protected override JobHandle OnUpdate(JobHandle inputDeps) { var rotationType = GetArchetypeChunkComponentType<RotationQuaternion>(); var rotationSpeedType = GetArchetypeChunkComponentType<RotationSpeed>(true); var chunks = m_group.CreateArchetypeChunkArray(Allocator.__TempJob__); var rotationsSpeedJob = new RotationSpeedJob { Chunks = chunks, RotationType = rotationType, RotationSpeedType = rotationSpeedType, DeltaTime = Time.deltaTime }; return rotationsSpeedJob.Schedule(chunks.Length,32,inputDeps); } }
在ComponentSystem中手动迭代
虽然不是推荐做法,但可以使用EntityManager类手动迭代实体或块,而这些迭代方法应该只用于测试或调试代码(或者只是用于测试)或者是在一个孤立的World中——在这个世界中你可以的完美控制所有实体
例如,以下这段代码遍历当前世界中的所有实体:
var entityManager = World.Active.EntityManager; var allEntities = entityManager.GetAllEntities(); foreach (var entity in allEntities) { //... } allEntities.Dispose();
而这段代码遍历活动世界中的所有块:
var entityManager = World.Active.EntityManager; var allChunks = entityManager.GetAllChunks(); foreach (var chunk in allChunks) { //... } allChunks.Dispose();
- Unity ECS+Jobs System笔记 访问数据4(八)
- Unity ECS+Jobs System笔记 写入组(九)
- SQL 复习笔记3数据控制语句(授权、授权、拒绝访问)
- Android深入探究笔记之十 -- 使用 ContentProvider 共享数据(二),访问与添加通讯录
- opencv视频学习第九课(访问cvMat数据)笔记整理
- ADO.NET笔记之数据访问技术总结
- SL复习笔记之平稳转型——基础篇(二、控件和数据访问)
- C++编程笔记:使用WinHTTP实现HTTP访问(解决接收UTF8数据乱码问题)
- 微软企业库5.0学习笔记(三十三)数据访问模块
- 学习springboot笔记(四)数据访问之JDBC
- Unity学习笔记:EventSystemAPI
- unity开发之三:www访问数据库(数据存储和数据获取)
- SL复习笔记之平稳转型——基础篇(二、控件和数据访问)
- 国科大大数据复习笔记 陈世敏 关系型数据管理系统(2)RBDMS系统架构 数据的存储与访问 B+Trees 查询索引index
- Enterprise Library 4.1学习笔记2----数据访问程序块
- Spring源码学习笔记---数据访问(二)
- Remoting笔记——错误:“由于安全限制,无法访问类型System.RunTime.Remoting.ObjRef”
- arcgis开发笔记【silverlight同步访问WCF和网站数据】
- OpenCV自学笔记2:访问图像数据
- 高性能javascript编程 学习笔记(数据访问)