?最近DOTS發(fā)布了正式的版本, 我們來分享一下DOTS里面Baking核心機制,方便大家上手學習掌握Unity DOTS開發(fā)。今天給大家分享的Baking機制中的
Filter Baking Output
與
Prefab In Baking
。
Filter Baking Output 機制
在默認情況下,Baking會為每個GameObject生成的Entity與Component,?這些entity都會被創(chuàng)建到Conversion World里面。然后在創(chuàng)作的時候不是所有的GameObject都需要被轉換成Entity。例如:?在一個樣條曲線上,一個控制點在創(chuàng)作的時候被用到了,但是bake成ecs數(shù)據(jù)后可能就再也沒有用了,所以不需要把這個控制點Bake成entity。
為了不把這個GameObject Bake產(chǎn)生的Entity輸出到World并保存到entity scene里面,我們可以給這個Entity添加一個BakingOnlyEntity tag Component。當你添加了這個tag component后,Baking系統(tǒng)就不會把你這個entity存儲到entity scene里面,也不會把這個entity生成到運行的main World里面。我們可以直接在Baker函數(shù)里面添給entity 添加一個BakingOnlyEntity的組件數(shù)據(jù),也可以在Authoring GameObject里面添加一個?BakingOnlyEntityAuthoring的組件,這兩種方式都可以達到同樣的效果沒有區(qū)別。
你也可以給組件添加注解[BakingType],[TemporaryBakingType] attributes過濾掉掉組件component,讓它不輸出到entity中。
[BakingType]:被這個attributes注記的Component,將不會在Baking Output的時候輸出到entity,它的生命周期與entity一樣。
[TemporaryBakingType]:被這個attributes注記的Component會在Baking Output的時候不會輸出到entity。這個Component只會存活在Baker過程中,Baker結束以后就會銷毀。
我們有時候需要在Baking System里面批量處理一些組件數(shù)據(jù),處理完后這些組件就沒有用了。例如,baker 把一個bounding box 轉換成了ecs component 數(shù)據(jù),接下來我們定義了一個Baking System批量處理所有的entity,來計算entity的凸包。計算完成后原來的bounding box組件就可以刪除了,這種情況下就可以使用上面的Filter Component Bake output機制。
Prefab In Baking機制
在Baking的過程中,傳統(tǒng)的GameObject Prefab也被Bake成entity Prefab。Entity Prefab其實也是一個Entity,相比普通的Entity它多了幾個標記組件prefab tag。當我們查詢實體的時候,如果發(fā)現(xiàn)這個標記,就會被默認的查詢規(guī)則排除出來,所以默認查詢API查詢不到prefab entity。系統(tǒng)會提供一個LinkedEntityGroup?buffer會把prefab里面所有的孩子entity的關聯(lián)起來。當我們創(chuàng)建一個entity prefab的時候,也非常方便的能找到它的孩子相關。
生成entity prefab到entity scene以后,我們就可以像使用普通的Prefab創(chuàng)建GameObject一樣來創(chuàng)建entity到世界。但是使用enity prefab之前一定要確保它已經(jīng)被Baker到了entity scene。當預制體實例化到Authoring Scene中的時候,Baking把它當作普通的GameObject來進行轉換,不會把它當作預制體。
生成一個entity prefab你需要注冊一個Baker,在Bake函數(shù)里面添加一個依賴關系,讓這個依賴于Authoring GameObject Prefab。然后Prefab將會被bake出來。我們搞一個組件保存了entity prefab的一個引用,那么unity就會把這個entity prefab 序列化到subscene中。當需要使用這個entity prefab的時候就能獲取到。代碼如下:
public?struct?EntityPrefabComponent?:?IComponentData
??{
????public?Entity?Value;
??}
?
??public?class?GetPrefabAuthoring?:?MonoBehaviour
??{
????public?GameObject?Prefab;
??}
?
??public?class?GetPrefabBaker?:?Baker
??{
????public?override?void?Bake(GetPrefabAuthoring?authoring)
????{
??????// Register the Prefab in the Baker
??????var?entityPrefab?= GetEntity(authoring.Prefab,?TransformUsageFlags.Dynamic);
??????// Add the Entity reference to a component for instantiation later
??????var?entity?= GetEntity(TransformUsageFlags.Dynamic);
??????AddComponent(entity,?new?EntityPrefabComponent()?{Value?= entityPrefab});
????}
??}
??#endregion
?
?
?
在Baking的時候,當我們需要引用一個entity prefab, 可以使用EntityPrefabReference。這個會把它序列化到entity scene文件里面去。運行的時候直接load進來,就可以使用了。這樣可以防止多個subscene不用重復拷貝生成同一個Prefab。
#region InstantiateLoadedPrefabs
??public?partial?struct?InstantiatePrefabReferenceSystem?:?ISystem
??{
????public?void?OnStartRunning(ref?SystemState?state)
????{
??????// Add the RequestEntityPrefabLoaded component to the Entities that have an
??????// EntityPrefabReference but not yet have the PrefabLoadResult
??????// (the PrefabLoadResult is added when the prefab is loaded)
??????// Note: it might take a few frames for the prefab to be loaded
??????var?query?= SystemAPI.QueryBuilder()
????????.WithAll()
????????.WithNone().Build();
??????state.EntityManager.AddComponent(query);
????}
?
????public?void?OnUpdate(ref?SystemState?state)
????{
??????var?ecb?= new?EntityCommandBuffer(Allocator.Temp);
?
??????// For the Entities that have a PrefabLoadResult component (Unity has loaded
??????// the prefabs) get the loaded prefab from PrefabLoadResult and instantiate it
??????foreach?(var?(prefab,?entity)?in
???????????SystemAPI.Query>().WithEntityAccess())
??????{
????????var?instance?= ecb.Instantiate(prefab.ValueRO.PrefabRoot);
?
????????// Remove both RequestEntityPrefabLoaded and PrefabLoadResult to prevent
????????// the prefab being loaded and instantiated multiple times, respectively
????????ecb.RemoveComponent(entity);
????????ecb.RemoveComponent(entity);
??????}
?
??????ecb.Playback(state.EntityManager);
??????ecb.Dispose();
????}
??}
??#endregion
?
實例化entity prefab可以使用EntityManager與entity command buffer。
#region InstantiateEmbeddedPrefabs
??public?partial?struct?InstantiatePrefabSystem?:?ISystem
??{
????public?void?OnUpdate(ref?SystemState?state)
????{
??????var?ecb?= new?EntityCommandBuffer(Allocator.Temp);
?
??????// Get all Entities that have the component with the Entity reference
??????foreach?(var?prefab?in
???????????SystemAPI.Query>())
??????{
????????// Instantiate the prefab Entity
????????var?instance?= ecb.Instantiate(prefab.ValueRO.Value);
????????// Note: the returned instance is only relevant when used in the ECB
????????// as the entity is not created in the EntityManager until ECB.Playback
????????ecb.AddComponent(instance);
??????}
?
??????ecb.Playback(state.EntityManager);
??????ecb.Dispose();
????}
??}
??#endregion
實例化EntityPrefabReference,可以使用如下代碼:
?#region InstantiateLoadedPrefabs
??public?partial?struct?InstantiatePrefabReferenceSystem?:?ISystem
??{
????public?void?OnStartRunning(ref?SystemState?state)
????{
??????// Add the RequestEntityPrefabLoaded component to the Entities that have an
??????// EntityPrefabReference but not yet have the PrefabLoadResult
??????// (the PrefabLoadResult is added when the prefab is loaded)
??????// Note: it might take a few frames for the prefab to be loaded
??????var?query?= SystemAPI.QueryBuilder()
????????.WithAll()
????????.WithNone().Build();
??????state.EntityManager.AddComponent(query);
????}
?
????public?void?OnUpdate(ref?SystemState?state)
????{
??????var?ecb?= new?EntityCommandBuffer(Allocator.Temp);
?
??????// For the Entities that have a PrefabLoadResult component (Unity has loaded
??????// the prefabs) get the loaded prefab from PrefabLoadResult and instantiate it
??????foreach?(var?(prefab,?entity)?in
???????????SystemAPI.Query>().WithEntityAccess())
??????{
????????var?instance?= ecb.Instantiate(prefab.ValueRO.PrefabRoot);
?
????????// Remove both RequestEntityPrefabLoaded and PrefabLoadResult to prevent
????????// the prefab being loaded and instantiated multiple times, respectively
????????ecb.RemoveComponent(entity);
????????ecb.RemoveComponent(entity);
??????}
?
??????ecb.Playback(state.EntityManager);
??????ecb.Dispose();
????}
??}
??#endregion
在實例化EntityPrefabReference之前,Unity必須要先加載對應的entity prefab,然后才能使用它,添加RequestEntityPrefabLoaded組件能確保entity prefab被加載。Unity會PrefabLoadResult加載到帶有RequestEntityPrefabLoaded同一個entity上。
預制體也是entity,也可以被查詢到,如果需要把一個預制體被查詢到,可以在查詢條件上添加IncludePrefab。
?#region PrefabsInQueries
??????// This query will return all baked entities, including the prefab entities
??????var?prefabQuery?= SystemAPI.QueryBuilder()
????????.WithAll().WithOptions(EntityQueryOptions.IncludePrefab).Build();
#endregion
使用EntityManager與entity command buffer也可以銷毀一個預制體節(jié)點,代碼如下:
??????#region DestroyPrefabs
??????var?ecb?= new?EntityCommandBuffer(Allocator.Temp);
?
??????foreach?(var?(component,?entity)?in
???????????SystemAPI.Query>().WithEntityAccess())
??????{
????????if?(component.ValueRO.RadiansPerSecond?<= 0)
????????{
??????????ecb.DestroyEntity(entity);
????????}
??????}
?
??????ecb.Playback(state.EntityManager);
??????ecb.Dispose();
??????#endregion
?
?
標簽: