?最近DOTS發(fā)布了正式的版本, 我們來分享一下DOTS里面Aspect機(jī)制,方便大家上手學(xué)習(xí)掌握Unity DOTS開發(fā)。
Aspect?機(jī)制概述
當(dāng)我們使用ECS開發(fā)的時候,編寫某個功能可能需要某個entity的一些組件,如果我們一個個組件的查詢出來,可能參數(shù)會寫很長。如果我們編寫某個功能的時候,需要entity的一些組件的引用,我們?nèi)绾胃咝У膩慝@得呢?Unity DOTS引入了Aspect機(jī)制。
Aspect是一個特殊的數(shù)據(jù)結(jié)構(gòu),可以把它理解為是entity中一些組件的引用Wrapper”包裝盒”,把entity中的一些組件的引用包含在一起。方便在System中通過這個Aspect來獲取entity當(dāng)中的組件的引用,高效方便的訪問entity中的一些組件數(shù)據(jù)。定義一個Aspect, 需要繼承自Iaspect Interface,?Aspect里面的成員可以包含以下的內(nèi)容:
Entity類型的引用;
RefRW 與RefRO組件數(shù)據(jù)的引用;
EnabledRefRW與EnabledRefRO的Enable Component組件數(shù)據(jù)的引用;
DynamicBuffer 類型數(shù)據(jù)buffer;
shared component 類型的組件的引用;
??????其它的Aspect類型;
?
Aspect定義與使用
定義一個Aspect,需要定義一個readonly的partial結(jié)構(gòu)體并繼承自接口類IAspect。
using?Unity.Entities;
readonly?partial?struct?MyAspect : IAspect
{
// Your Aspect code
?
}
結(jié)構(gòu)體里面的字段可以使用上面字段所規(guī)定的類型,?我們還可以把某個字段通過attribute設(shè)置為[Optional]。這樣這個字段在entity里面就不是必須的,如果某個entity類沒有這個可選字段,也能生成對應(yīng)的Aspect。如果想要DynamicBuffer字段為只讀,可以定義attibute [ReadOnly]。RefRO修飾的組件是只讀的,RefRW修飾的組件可讀寫。
在System中我們要基于定義好的Aspect類型來操作entity中的組件數(shù)據(jù),我們可以為Entity生成一個Aspect對象。通過API:SystemAPI.GetAspect來獲取entity對應(yīng)的Aspect對象。
// Throws if the entity is missing any of // the required components of MyAspect.
?MyAspect asp = SystemAPI.GetAspect(myEntity);
如果這個entity類型無法生成對應(yīng)的Aspect,那么asp就會返回null。當(dāng)我們在System中需要迭代所有Entity的某種Aspect,可以使用API:
SystemAPI.Query。參考代碼如下:
#region aspect-example
??struct?CannonBall?:?IComponentData
??{
????public?float3?Speed;
??}
?
??// Aspects must be declared as a readonly partial struct
??readonly?partial?struct?CannonBallAspect?:?IAspect
??{
????// An Entity field in an Aspect gives access to the Entity itself.
????// This is required for registering commands in an EntityCommandBuffer for example.
????public?readonly?Entity?Self;
?
????// Aspects can contain other aspects.
?
????// A RefRW field provides read write access to a component. If the aspect is taken as an "in"
????// parameter, the field behaves as if it was a RefRO and throws exceptions on write attempts.
????readonly?RefRW?Transform;
????readonly?RefRW?CannonBall;
?
????// Properties like this aren't mandatory. The Transform field can be public instead.
????// But they improve readability by avoiding chains of "aspect.aspect.aspect.component.value.value".
????public?float3?Position
????{
??????get?=> Transform.ValueRO.Position;
??????set?=> Transform.ValueRW.Position?= value;
????}
?
????public?float3?Speed
????{
??????get?=> CannonBall.ValueRO.Speed;
??????set?=> CannonBall.ValueRW.Speed?= value;
????}
??}
#endregion
??#region aspect-iterate
??public?partial?struct?MySystem?:?ISystem
??{
????public?void?OnUpdate(ref?SystemState?state)
????{
??????foreach?(var?cannonball?in?SystemAPI.Query())
??????{
????????// use cannonball aspect here
??????}
????}
??}
??#endregion
上面代碼中定義了一個struct CannonBall 的ComponentData,?定義了一個CannonBallAspect,包含了entity本身引用,以及所需要的其它組件的引用(字段里面還可以基于get/set)。System中通過查詢當(dāng)前World里面所有含有CannonBallAspect對象的entity,然后統(tǒng)一處理它們。
Aspect的代碼自動生成
不同類型的Entity可能有同一個類型的Aspect,那么Unity DOTS如何來處理呢?例如Entity類型A與Entity類型B,都有Aspect所定義的組件與引用,那么系統(tǒng)如何把A類型的Entity與B類型的Entity都生成它對應(yīng)的Aspcet對象呢?那么這個時候就需要通過掃描所有的代碼,來自動生成相關(guān)的代碼自動生成對應(yīng)的偽代碼如下:
MyAspect?CreateAspectWithEntityA(entity實(shí)例) {
?????Var myAspect = new MyAspect();
把A類entity實(shí)例對應(yīng)的ArchType的ComponentData塊的引用,生成一個MyAspect實(shí)例。
????Return myAspect;
}
MyAspect?CreateAspectWithEntityB(entity實(shí)例) {
?????Var myAspect = new MyAspect();
把B類entity實(shí)例對應(yīng)的ArchType的ComponentData塊的引用,生成一個MyAspect實(shí)例。
????Return myAspect;
}
entity是否具有某種Aspcet類型的Aspect,也會被快速的生成出來,這樣再查詢的時候都可以提升查詢的速度。具體可以參考相關(guān)源碼。
標(biāo)簽: