当一个实体被删除时,会有以下几步操作
但当存在 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();
( EntityCommandBuffer 以下简称 ECB)
ECB 主要解决了两个问题
结构化改变包括
同步点
当我们在使用 Job 时,为了防止多线程运行时对同一个实体进行了冲突的操作时,同步点的存在是必要的
例如:当线程 A 对实体 a1 的组件进行了删除操作,线程 B 又去获取实体 a1 被删除的组件时,就会发生错误
所以当我们使用了 Job 的多线程时,需要使用 ECB 来延迟结构化改变操作,统一到同步点内再进行操作
方法/模式 | 优点 | 缺点 |
---|---|---|
Convert And Destroy | 转换后 删除 原有 GameObject | 1.转化为 Entity 后在 Hierarchy 面板将 2.无法调试部分无法被转化的组件将会被删除 |
Convert And Inject GameObject | 转换后 保留 原有 GameObject | 1.由于原 GameObject 没有被删除,会出现两个物体的情况 2.无法对子层级同时进行转化 |
Destroy & Inject 混合使用 | 包含 Destroy 与 Inject 的优点 | 1.转化为 Entity 后在 Hierarchy 面板将无法调试 2.由于原 GameObject 没有被删除,会出现两个物体的情况 |
Companion Game Object | 伴随方法可以指定 GameObject 组件不被转化为 Entity 的组件,兼容 Prefab | 1.转化为 Entity 后在 Hierarchy 面板将无法调试 |
SubScene | 可以将整个场景直接转化为 Entity,且可以在 Hierarchy 面板直接进行调试 | 无 |
挂载后,运行时将会自动转化为 Entity ,但并非所有组件都能被转化为 Entity 的 Component
可以通过更改 Conversion Mode 来更改转化的模式
Convert And Destroy 模式会将该 GameObject 的子层级一并转化,且保持父子级关系,但如遇到无法转化的组件时会直接删除
Convert And Inject Game Object 模式在转化时会保留原有组件,但会导致 Mesh Renderer 与 Hybrid Renderer 同时渲染,出现两个相同物体的情况,同时该模式无法转化子层级对象
父物体与子物体同时挂载 Convert To Entity 组件
父物体为 Destroy 模式,子物体为 Inject 模式 (经测试,父物体为 Inject,子物体为 Destroy 时,子物体将不会进行转化)
此时将会同时获得两种模式的好处,在保持父子级关系的同时,子物体的组件会被保留
该方法通常用于编写 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
});
}
}
它会创建一个新的 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);
}
}
这个并不算转化方法,只是能够在转化的时候更方便的加上 Component
可以通过添加 [GenerateAuthoringComponent] 属性标签让 ComponentData 可以被直接挂载
注意:字段的访问类型必须为 public,且依然需要挂载 Convert To Entity
示例:
[GenerateAuthoringComponent]
public struct SpeedComponent : IComponentData {
public float speed;
}
[GenerateAuthoringComponent]
public struct TagPlayer : IComponentData {}
通过 SubScene 转化的物体可以实现一边在 Hierarchy 中修改 GameObject,一边实时的同步到 LocalToWorld / Translation 中,并且不会出现两个物体的情况,仅由 Hybrid Renderer 负责渲染,尽管物体已经被转化为 Entity 并且 Destroy,但在 Hierarchy 中仍然可以选中物体并同步移动。
注意:在 Scene 窗口想要调试 Entity 场景需要勾选 SubScene 右侧的 Toggle
Run 是直接在主线程上执行的,这个没什么需要解释的
Schedule 和 ScheduleParallel 都是放在多线程上并发执行的
我目前的理解是, Schedule 是在每一个 Foreach 后生成一个 Job
而 ScheduleParallel 划分 Job 是根据 Chunk 生成的,Chunk 是按 EntityArchetype 分配的
当一个 Chunk 的空间满了后,会再生成一个 Chunk,而这每一个 Chunk 都会成为一个 Job
所以从执行效率来看,ScheduleParallel 的大多数情况应该是优于 Schedule 的
而当我们只有一个 Chunk 的时候,Schedule 和 ScheduleParallel 的效果应该是一样的
这个可能是最简单方便的办法了,在场景中找到想要转换的 GameObject ,挂载 Convert To Entity 就可以进行转换了
需要提的一点是,这个组件有两种 Conversion Mode
Convert And Destroy | 转换后 删除 原有 GameObject |
Convert And Inject GameObject | 转换后 保留 原有 GameObject |
这个应该是最常用的办法了,生成的Entity会生成在和 EntityManager 相同的 World 中
EntityManager.CreateEntity( ) | 可以什么参数都不填,生成一个空的 Entity |
EntityManager.CreateEntity(params ComponentType[]) | 填入不定项个 ComponentType,可以直接生成带有这个 Component的Entity |
EntityManager.CreateEntity(EntityArchetype) | 利用原型 Archetype 来生成 Entity |
EntityManager.Instantiate(Entity) | 克隆一个现有的 Entity |
我们可以创建一个用来生成的类 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了~