欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 汽车 > 时评 > Dots 常用操作

Dots 常用操作

2025/6/24 1:37:10 来源:https://blog.csdn.net/sinat_34014668/article/details/143449646  浏览:    关键词:Dots 常用操作

游戏中有多个蚂蚁群落,每个蚂蚁属于一个群落,如何设计数据结构?

  • 方法1:为蚂蚁组件添加一个属性 ID,会造成逻辑中大量分支语句,如果分支语句逻辑不平衡可能带来 Job 调度问题,每个蚂蚁会有一份蚂蚁群落 ID 属性的拷贝,海量蚂蚁时造成存储空间的浪费
  • 方法2:使用 TagComponent 标记不同的群落,蚂蚁群落个数不固定,运行时添加 TagComponent 不合适
    TagComponent 个数太多,造成 Archetype 数量爆炸
  • 方法3:使用 Shared Component 处理蚂蚁群落 ID,所有具有相同蚂蚁群落的蚂蚁共享相同的群落 ID 值,创建时确定,运行时不变,不会带来共享属性值频繁更新带来的 Structual Change 的影响,充分利用 WithShareComponentFilter 方法,快速通过一次查询处理所有蚂蚁初始化位置逻辑

读取工具选择

  • SystemAPl.Query+Foreach
  • IJobEntity / lJobChunk + EntityQuery
  • ComponentLookup

写入工具选择

  • EntityManager API
  • Entity Command Buffer
  • Entity Command Buffer ParallexWritter
  • 根据查询结果直接对引用组件的数据写入

DOTS程序的两种模式

  • Hybrid 混合模式:混合使用托管与非托管数据组件
  • Pure 纯净模式:只使用非托管数据组件

在这里插入图片描述
定义宏:UNITY_DISABLE_MANAGED_COMPONENTS,会禁用托管对象

DOTS调试宏

  • ENABLE_UNITY_COLLECTIONS_CHECKS
  • UNITY_DOTS_DEBUG

在这里插入图片描述
为 release 版本生成调试信息

在这里插入图片描述
查看泄露点

Benchmark

Entity 创建的几种方式

  1. 子场景 Bake 方式
  2. 通过 EntityManager 实例化 Prefab 的 Entity 原型方式
  3. 通过 ECS 的 parallelwriter 实例化 ParallelWriter.Instantiate(index, protoType)
  4. 通过 EntityManager 的 CreateEntity 接口以及 RenderMeshUtility 手动创建 Entity 对象

效率由高到低,大致是 1 > 2 > 3 > 4

创建Entity方案推荐与细节

  • 静态场景用子场景烘焙,动态场景对象用主线程创建
  • 多线程创建只在对象初始化需要大量额外计算时使用,这种情况较少
  • 通过 Prefab 实例化 Entity 的方式会在场景中额外保存一个 Entity,其 Archetype 与实例化后的 Entity 不同,所以对于有海量 prefab 对象的项目,注意 Archetype 数量对效率的影响。
  • 完全通过脚本化生成 Entity 不是不能用,但尽量少用。

Component组件添加的几种方式

  1. EntityManager.AddComponent(EntityQuery query, new Component)
  2. EntityManager.AddComponent(NativeArray<Entity> entitie, new Component)
  3. Foreach + EntityManager.AddComponent
  4. Ecb.AddComponent(EntityQuery query, new Component)
  5. Ecb.AddComponent(NativeArray<Entity> entities, new Component)
  6. Foreach + Ecb.AddComponent(entity, new Component)
  7. Ecb + lJobChunk ScheduleParallel
  8. Ecb + lJobEntity ScheduleParallel

EntityManager 接口添加 Component 总结

  • 使用上面 1 方式查询添加组件是最高效的方式
  • 缓存 Entity 数组无论是访问还是做添加删除是没有必要的,性能差
  • 多次调用为单个 Entity 添加组件接口会导致多次 structural change
  • 如果为单个 Entity 添加多个组件尽量通过以下两个接口:
    • CreateArchetype
    • AddComponent(EntityQuery,ComponentTypeset)
  • 先添加普通 Component,再添加 Enableable 修饰的 Component 比颠倒二者添加效率高
  • 通过 Enableable 组件禁用与启用组件比直接添加删除组件性能要高,因为避免 structural change
  • Structual Change 的操作主线程比工作线程更有效
  • 标签组件的性能开销比数据组件要低。

数据的存储与传递方式

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

静态共享数据区交互

  • static readonly xxx,只读
  • SharedStatic<T>,可变数据,可读写,可以是非托管或托管数据

适合 SharedStatic<T> 的使用场景,网络数据读取

  • 不可避免要做随机访问
  • 网络数据要与 Entity 实体对象有映射关系
  • 有跨 System 访问的需求
  • 有数据共享和读写的需求
public struct SharedCubesEntityColorMap
{public static readonly SharedStatic<SharedCubesEntityColorMap> SharedValue = SharedStatic<SharedCubesEntityColorMap>.GetOrCreate<SharedCubesEntityColorMap>();public SharedCubesEntityColorMap(int capacity){entityColorMap = new NativeHashMap<Entity, float3>(capacity, Allocator.Persistent);}//保存 entity 和 color 的映射关系public NativeHashMap<Entity, float3> entityColorMap; 
}

假设每个 entity 都有一个颜色属性,需要做随机访问,初始化时记录映射关系

void Update()
{//获取某个entityfloat3 color = SharedCubesEntityColorMap.SharedValue.Data.entityColorMap[entity];//对颜色进行处理
}

之后就可以在 mono 或 System 中对共享数据进行读写

System复杂度拆分策略

  • 多个处理相同事务的 System 耗时远远小于 1ms,可以考虑将这些 System 合并成一个 System
  • 如果一个 System 处理的事务耗时大于 1ms,考虑使用 Job 并行或 burst 编译优化,优化后仍然远远大于 1ms,可以考虑对 System 做事务拆分
  • 耗时过低的 System 影响其中 Job 的并行程度,耗时过高的 System 影响 CPU 调度

Sub Scene 管理

在这里插入图片描述
Dots 中 Scene 结构,Section 用于对子场景进行分组,默认情况下,所有的 entity 都会在 section 0 上

在这里插入图片描述
可以在子场景物体上挂载 SceneSectionComponent 官方脚本,设置 Section Index,Section Index = 0 的会优先加载

在这里插入图片描述
Initialization 中的 Scene System Group

在这里插入图片描述

如何标识子场景

  • EntitySceneReference 直接引用子场景
  • Hash128 GUID 尽量避免使用
  • Entity 子场景加载后返回的 meta entity
[Serializable]
public struct SubscenesReferences : IComponentData
{//sub scene资源引用public EntitySceneReference cubeSceneReference;//sub scene加载后返回的entity,卸载时需要public Entity cubeSceneMetaEntity;public EntitySceneReference sphereSceneReference;public Entity sphereSceneMetaEntity;
}
public class SubscenesLoaderAuthoring : MonoBehaviour
{[SerializeField] public SubscenesReferences references;private class SubscenesLoaderBaker : Baker<SubscenesLoaderAuthoring>{public override void Bake(SubscenesLoaderAuthoring authoring){var entity = GetEntity(TransformUsageFlags.None);AddComponent(entity, authoring.references);}}
}
public partial struct ScenesLoadSystem : ISystem, ISystemStartStop
{[BurstCompile]public void OnCreate(ref SystemState state){state.RequireForUpdate<SubscenesReferences>();}[BurstCompile]public void OnUpdate(ref SystemState state){var references = SystemAPI.GetSingletonRW<SubscenesReferences>();//场景状态SceneSystem.SceneStreamingState ssstate = SceneSystem.GetSceneStreamingState(state.WorldUnmanaged, references.ValueRO.cubeSceneMetaEntity);if (ssstate == SceneSystem.SceneStreamingState.LoadedSuccessfully){//卸载子场景内容,移除 meta entity 上的 RequestSceneLoaded//SceneSystem.UnloadScene(state.WorldUnmanaged, references.ValueRW.cubeSceneMetaEntity);//完全卸载//SceneSystem.UnloadScene(state.WorldUnmanaged, references.ValueRW.cubeSceneMetaEntity, SceneSystem.UnloadParameters.DestroyMetaEntities);}ssstate = SceneSystem.GetSceneStreamingState(state.WorldUnmanaged, references.ValueRO.sphereSceneMetaEntity);if (ssstate == SceneSystem.SceneStreamingState.LoadedSectionEntities){//根据 meta entity 加载场景SceneSystem.LoadSceneAsync(state.WorldUnmanaged, references.ValueRW.sphereSceneMetaEntity);}}public void OnStartRunning(ref SystemState state){var references = SystemAPI.GetSingletonRW<SubscenesReferences>();if (references.ValueRO.cubeSceneReference.IsReferenceValid){references.ValueRW.cubeSceneMetaEntity =SceneSystem.LoadSceneAsync(state.WorldUnmanaged, references.ValueRO.cubeSceneReference);}if (references.ValueRO.sphereSceneReference.IsReferenceValid){references.ValueRW.sphereSceneMetaEntity =SceneSystem.LoadSceneAsync(state.WorldUnmanaged, references.ValueRO.sphereSceneReference, new SceneSystem.LoadParameters{//不自动加载AutoLoad = false});}}public void OnStopRunning(ref SystemState state) {}
}

在这里插入图片描述
子场景加载后,会生成两个额外的 entity,即 meta entity 和 section entity

public partial struct SceneSectionsLoadSystem : ISystem
{private float timer = 1f;[BurstCompile]public void OnUpdate(ref SystemState state){var sectionEntities = SystemAPI.GetSingletonBuffer<ResolvedSectionEntity>();NativeArray<Entity> sectionEntitiesArray = CollectionHelper.CreateNativeArray<Entity>(sectionEntities.Length, Allocator.Temp);for (int i = 0; i < sectionEntities.Length; i++){sectionEntitiesArray[i] = sectionEntities[i].SectionEntity;}timer -= SystemAPI.Time.DeltaTime;if (timer < 0){for (int i = 0; i < sectionEntitiesArray.Length; i++){var sectionState = SceneSystem.GetSectionStreamingState(state.WorldUnmanaged, sectionEntitiesArray[i]);if (sectionState == SceneSystem.SectionStreamingState.Loaded){state.EntityManager.RemoveComponent<RequestSceneLoaded>(sectionEntitiesArray[i]);if (i == sectionEntitiesArray.Length - 1)timer = 1.0f;}else if (sectionState == SceneSystem.SectionStreamingState.Unloaded){state.EntityManager.AddComponent<RequestSceneLoaded>(sectionEntitiesArray[i]);if (i == sectionEntitiesArray.Length - 1)timer = 1.0f;}}}sectionEntitiesArray.Dispose();}
}

section 的动态加载和卸载,实际开发中可以把地图分成不同的 scetion,然后按需加载

Scene section上Entity的交叉引用关系

  • Section 0 里的 Entity 可以被其他 Section 的 Entity 引用
  • 单个 Section 内的 Entity 之间可以彼此引用
  • 除 Section 0 外其他 Section 间的 Entity 彼此是不能被引用的

参考

《DOTS之路》系列课程

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词