menu 宵夜律者's Blog
分类 ECS 下的文章
message
2021-03-27 10:26:00

当一个实体被删除时,会有以下几步操作

  • 查找引用该实体的所有组件
  • 删除这些组件
  • 回收实体的 ID 以供重新使用

但当存在 State 组件时,实体不会被删除,只会删除除了 State 组件以外的组件
当配合使用 StateComponentData 与 ComponentData 时可以实现观察实体当前的状态

Tag(ComponentData)State(StateComponentData)
Tag 存在,State 缺失实体刚被创建,未被初始化
Tag 存在,State 存在实体已被初始化,可以使用
Tag 存在,State 存在实体处于删除状态

例如:

Entities
    .WithAll<Tag>()
    .WithNone<State>()
    .WithStructuralChanges()
    .ForEach((Entity entity) =>{
        // Spawn
}).Run();

Entities
    .WithAll<Tag>()
    .WithAll<State>()
    .ForEach((Entity entity) =>{
        // Update
}).Run();

Entities
    .WithNone<Tag>()
    .WithAll<State>()
    .ForEach((Entity entity) =>{
        // Destroy
}).Run();
message
2021-03-26 16:24:00

( EntityCommandBuffer 以下简称 ECB)

ECB 主要解决了两个问题

  • 第一个问题,在使用 Schedule 和 ScheduleParallel 操作使用 Job 时,是无法直接使用 EntityManager 的
  • 第二个问题,在进行结构化改变时,需要等待所有 Job 完成后在同步点进行操作

结构化改变包括

  • 创建实体
  • 删除实体
  • 向实体中添加组件
  • 从实体中删除组件
  • 修改共享组件的值

同步点

  • 在同步点进行的操作,会导致 Job 无法工作,所以应该尽量减少结构化改变的操作
  • 使用 ECB 可以将分散的结构化改变操作同步在同一个同步点内

当我们在使用 Job 时,为了防止多线程运行时对同一个实体进行了冲突的操作时,同步点的存在是必要的
例如:当线程 A 对实体 a1 的组件进行了删除操作,线程 B 又去获取实体 a1 被删除的组件时,就会发生错误
所以当我们使用了 Job 的多线程时,需要使用 ECB 来延迟结构化改变操作,统一到同步点内再进行操作

message
2021-02-27 18:56:00
方法/模式优点缺点
Convert  And Destroy转换后 删除 原有 GameObject1.转化为 Entity 后在 Hierarchy 面板将
2.无法调试部分无法被转化的组件将会被删除
Convert And Inject GameObject转换后 保留 原有 GameObject1.由于原 GameObject 没有被删除,会出现两个物体的情况
2.无法对子层级同时进行转化
Destroy & Inject 混合使用包含 Destroy 与 Inject 的优点1.转化为 Entity 后在 Hierarchy 面板将无法调试
2.由于原 GameObject 没有被删除,会出现两个物体的情况
Companion Game Object伴随方法可以指定 GameObject 组件不被转化为 Entity 的组件,兼容 Prefab 1.转化为 Entity 后在 Hierarchy 面板将无法调试
SubScene可以将整个场景直接转化为 Entity,且可以在 Hierarchy 面板直接进行调试

方法一 — 通过给 GameObject 挂载 Convert To Entity 组件来进行转换

挂载后,运行时将会自动转化为 Entity ,但并非所有组件都能被转化为 Entity 的 Component
可以通过更改 Conversion Mode 来更改转化的模式
Convert And Destroy 模式会将该 GameObject 的子层级一并转化,且保持父子级关系,但如遇到无法转化的组件时会直接删除
Convert And Inject Game Object 模式在转化时会保留原有组件,但会导致 Mesh Renderer 与 Hybrid Renderer 同时渲染,出现两个相同物体的情况,同时该模式无法转化子层级对象

方法二 — Destroy & Inject 混合使用

父物体与子物体同时挂载 Convert To Entity 组件
父物体为 Destroy 模式,子物体为 Inject 模式 (经测试,父物体为 Inject,子物体为 Destroy 时,子物体将不会进行转化)
此时将会同时获得两种模式的好处,在保持父子级关系的同时,子物体的组件会被保留

方法三 — 继承 IConvertGameObjectToEntity 接口,实现 Convert 方法

该方法通常用于编写 Authoring ,在 Convert 可以加入我们想要在转化时同时加入的 Component 信息
这样我们可以还在 Inspector 窗口更改参数,方便调试
注意:GameObject 依然需要挂载 Convert To Entity 组件
示例:

public struct CameraAimInfo : IComponentData {
    public float distance;
    public float theta;
}

public class CameraAuthoring : MonoBehaviour, IConvertGameObjectToEntity {
    public float distance;
    public float theta;

public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
        dstManager.AddComponentData(entity, new CameraAimInfo() {
            distance = distance,
            theta = theta
        });
    }
}

方法四 — Companion Game Object 伴随方法

它会创建一个新的 GameObject 来"伴随"(companion)被转换的数据,并用它来选择你想保留的组件

当我们的 GameObject 中有几个不想被转化或无法转化的组件时,可以使用 AddHybridComponent 这个方法

示例:

public class Test : MonoBehaviour, IConvertGameObjectToEntity {
    public Button button;
    public Image image;
    public Text text;
    public ParticleSystem particleCompanion;
    public ParticleSystemRenderer rendererCompanion;

public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
    conversionSystem.AddHybridComponent(button);
    conversionSystem.AddHybridComponent(text);
    conversionSystem.AddHybridComponent(image);
    conversionSystem.AddHybridComponent(particleCompanion);
    conversionSystem.AddHybridComponent(rendererCompanion);
    }
}

方法五 — [GenerateAuthoringComponent]

这个并不算转化方法,只是能够在转化的时候更方便的加上 Component
可以通过添加  [GenerateAuthoringComponent] 属性标签让 ComponentData 可以被直接挂载

注意:字段的访问类型必须为 public,且依然需要挂载 Convert To Entity

示例:

[GenerateAuthoringComponent]
public struct SpeedComponent : IComponentData {
    public float speed;
}

[GenerateAuthoringComponent]
public struct TagPlayer : IComponentData {}

GenerateAuthoringComponent测试.png

方法六 — 使用 SubScene 组件可以实现整个场景的 Entity 转化

通过 SubScene 转化的物体可以实现一边在 Hierarchy 中修改 GameObject,一边实时的同步到 LocalToWorld / Translation 中,并且不会出现两个物体的情况,仅由 Hybrid Renderer 负责渲染,尽管物体已经被转化为 Entity 并且 Destroy,但在 Hierarchy 中仍然可以选中物体并同步移动。

注意:在 Scene 窗口想要调试 Entity 场景需要勾选 SubScene 右侧的 Toggle

参考文章:Game Object Conversion and SubScene

message
2021-01-30 18:08:00

Run

Run 是直接在主线程上执行的,这个没什么需要解释的

Schedule 和 ScheduleParallel的区别

Schedule 和 ScheduleParallel 都是放在多线程上并发执行的

我目前的理解是, Schedule 是在每一个 Foreach 后生成一个 Job
而 ScheduleParallel 划分 Job 是根据 Chunk 生成的,Chunk 是按 EntityArchetype 分配的
当一个  Chunk 的空间满了后,会再生成一个 Chunk,而这每一个 Chunk 都会成为一个 Job

所以从执行效率来看,ScheduleParallel 的大多数情况应该是优于 Schedule 的
而当我们只有一个 Chunk 的时候,Schedule 和 ScheduleParallel 的效果应该是一样的

message
2021-01-21 21:13:00

使用 Convert To Entity 组件

这个可能是最简单方便的办法了,在场景中找到想要转换的 GameObject ,挂载 Convert To Entity 就可以进行转换了

需要提的一点是,这个组件有两种 Conversion Mode

Convert  And Destroy转换后 删除 原有 GameObject
Convert And Inject GameObject转换后 保留 原有 GameObject

使用 EntityManager 创建实体

这个应该是最常用的办法了,生成的Entity会生成在和 EntityManager 相同的 World 中

EntityManager.CreateEntity( )可以什么参数都不填,生成一个空的 Entity
EntityManager.CreateEntity(params ComponentType[])填入不定项个 ComponentType,可以直接生成带有这个 Component的Entity
EntityManager.CreateEntity(EntityArchetype)利用原型 Archetype 来生成 Entity
EntityManager.Instantiate(Entity)克隆一个现有的 Entity

创建一个Spawner来生成实体

我们可以创建一个用来生成的类 Spawner ,继承 MonoBehaviour ,并实现 IDeclareReferencedPrefabs ,  IConvertGameObjectToEntity 接口

这个时候需要我们去实现接口中的两个方法,其中DeclareReferencedPrefabs的操作是referencedPrefabs.Add(Prefab)
这个Prefab是我们外部加载的GameObject,这个方法会在场景中生成一个Entity类型的一个Prefab

之后我们来实现Convert这个方法
首先我们来创建一个ComponentData,我们要确保这个里面包含Entity字段,并在Convert这个方法里使用conversionSystem.GetPrimaryEntity()将这个 Prefab 转化为 Entity 加载进去

最后我们使用dstManager.AddComponentData()方法,将这个ComponentData挂载到我们之前创建好的Entity当中

此时,我们就可以利用这个Entity来创建我们外部加载的Prefab了~

加载中... 你已抵达世界之底
加载更多