Unity ECS Sample解析(1)
资料来源:https://github.com/Unity-Technologies/EntityComponentSystemSamples
使用工具:Unity,vscode,dnSPy
Unity版本:2020.1.0.f1
研究目的:研究ECS的工作原理
BoidSchool
用来生成鱼群的ECS结构
BoidSchool中的Prefab是名为TestBoidFish和TestBoidFish 1的Prefab.
BoidSchoolSpawnSystem利用了场景中的BoidSchool类型,使用Entities.WithStructuralChanges().ForEach便利BoidSchool.
然后再生成boidSchool.Count条鱼,利用SetBoidLocalToWorld设置具体位置.
namespace Samples.Boids
{
public struct BoidSchool : IComponentData
{
public Entity Prefab;
public float InitialRadius;
public int Count;
}
public class BoidSchoolSpawnSystem : SystemBase
{
[BurstCompile]
struct SetBoidLocalToWorld : IJobParallelFor
{
[NativeDisableContainerSafetyRestriction]
[NativeDisableParallelForRestriction]
public ComponentDataFromEntity<LocalToWorld> LocalToWorldFromEntity;
public NativeArray<Entity> Entities;
public float3 Center;
public float Radius;
public void Execute(int i)
{
var entity = Entities[i];
var random = new Random(((uint)(entity.Index + i + 1) * 0x9F6ABC1));
var dir = math.normalizesafe(random.NextFloat3() - new float3(0.5f, 0.5f, 0.5f));
var pos = Center + (dir * Radius);
var localToWorld = new LocalToWorld
{
Value = float4x4.TRS(pos, quaternion.LookRotationSafe(dir, math.up()), new float3(1.0f, 1.0f, 1.0f))
};
LocalToWorldFromEntity[entity] = localToWorld;
}
}
protected override void OnUpdate()
{
Entities.WithStructuralChanges().ForEach((Entity entity, int entityInQueryIndex, in BoidSchool boidSchool, in LocalToWorld boidSchoolLocalToWorld) =>
{
var boidEntities = new NativeArray<Entity>(boidSchool.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
Profiler.BeginSample("Instantiate");
EntityManager.Instantiate(boidSchool.Prefab, boidEntities);
Profiler.EndSample();
var localToWorldFromEntity = GetComponentDataFromEntity<LocalToWorld>();
var setBoidLocalToWorldJob = new SetBoidLocalToWorld
{
LocalToWorldFromEntity = localToWorldFromEntity,
Entities = boidEntities,
Center = boidSchoolLocalToWorld.Position,
Radius = boidSchool.InitialRadius
};
Dependency = setBoidLocalToWorldJob.Schedule(boidSchool.Count, 64, Dependency);
Dependency = boidEntities.Dispose(Dependency);
EntityManager.DestroyEntity(entity);
}).Run();
}
}
}
我们再来看通过语法糖生成的IL逆向代码:
namespace Samples.Boids
{
public class BoidSchoolSpawnSystem : SystemBase
{
protected override void OnUpdate()
{
ForEachLambdaJobDescription entities = base.Entities;
BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0 <>c__DisplayClass_OnUpdate_LambdaJob = default(BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0);
<>c__DisplayClass_OnUpdate_LambdaJob.ScheduleTimeInitialize(this);
base.CompleteDependency();
EntityQuery query = this.<>OnUpdate_LambdaJob0_entityQuery;
this.<>OnUpdate_LambdaJob0_profilerMarker.Begin();
try
{
<>c__DisplayClass_OnUpdate_LambdaJob.Execute(this, query);
}
finally
{
this.<>OnUpdate_LambdaJob0_profilerMarker.End();
}
}
protected internal override void OnCreateForCompiler()
{
base.OnCreateForCompiler();
this.<>OnUpdate_LambdaJob0_entityQuery = BoidSchoolSpawnSystem.<>GetEntityQuery_ForOnUpdate_LambdaJob0_From(this);
this.<>OnUpdate_LambdaJob0_profilerMarker = new ProfilerMarker("OnUpdate_LambdaJob0");
}
public static EntityQuery <>GetEntityQuery_ForOnUpdate_LambdaJob0_From(ComponentSystemBase componentSystem)
{
EntityQueryDesc[] array = new EntityQueryDesc[1];
EntityQueryDesc entityQueryDesc = new EntityQueryDesc();
array[0] = entityQueryDesc;
entityQueryDesc.All = new ComponentType[]
{
ComponentType.ReadOnly<BoidSchool>(),
ComponentType.ReadOnly<LocalToWorld>()
};
return componentSystem.GetEntityQuery(array);
}
private EntityQuery <>OnUpdate_LambdaJob0_entityQuery;
private ProfilerMarker <>OnUpdate_LambdaJob0_profilerMarker;
[BurstCompile]
private struct SetBoidLocalToWorld : IJobParallelFor
{
public void Execute(int i)
{
Entity entity = this.Entities[i];
Unity.Mathematics.Random random = new Unity.Mathematics.Random((uint)((entity.Index + i + 1) * 167160769));
float3 dir = math.normalizesafe(random.NextFloat3() - new float3(0.5f, 0.5f, 0.5f), default(float3));
float3 pos = this.Center + dir * this.Radius;
LocalToWorld localToWorld = new LocalToWorld
{
Value = float4x4.TRS(pos, quaternion.LookRotationSafe(dir, math.up()), new float3(1f, 1f, 1f))
};
this.LocalToWorldFromEntity[entity] = localToWorld;
}
[NativeDisableContainerSafetyRestriction]
[NativeDisableParallelForRestriction]
public ComponentDataFromEntity<LocalToWorld> LocalToWorldFromEntity;
public NativeArray<Entity> Entities;
public float3 Center;
public float Radius;
}
[DOTSCompilerGenerated]
private struct <>c__DisplayClass_OnUpdate_LambdaJob0
{
public void OriginalLambdaBody(Entity entity, int entityInQueryIndex, [In] ref BoidSchool boidSchool, [In] ref LocalToWorld boidSchoolLocalToWorld)
{
this.hostInstance.<OnUpdate>b__1_0(entity, entityInQueryIndex, boidSchool, boidSchoolLocalToWorld);
}
public unsafe static void PerformLambda(void* jobStructPtr, void* runtimesPtr, Entity entity)
{
ref BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes ptr = UnsafeUtility.AsRef<BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes>(runtimesPtr);
Entity entity2 = ptr.runtime_entity.For(entity);
int entityInQueryIndex = ptr.runtime_entityInQueryIndex.For(entity);
BoidSchool boidSchool2;
BoidSchool boidSchool = ptr.runtime_boidSchool.For(entity, out boidSchool2);
LocalToWorld localToWorld2;
LocalToWorld localToWorld = ptr.runtime_boidSchoolLocalToWorld.For(entity, out localToWorld2);
UnsafeUtility.AsRef<BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0>(jobStructPtr).OriginalLambdaBody(entity2, entityInQueryIndex, ref boidSchool, ref localToWorld);
}
public unsafe void Execute(ComponentSystemBase componentSystem, EntityQuery query)
{
BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes runtimes = this._lambdaParameterValueProviders.PrepareToExecuteWithStructuralChanges(componentSystem, query);
this._runtimes = &runtimes;
runtimes._entityProvider.IterateEntities((void*)(&this), (void*)this._runtimes, BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0._performLambdaDelegate);
}
public void ScheduleTimeInitialize(BoidSchoolSpawnSystem componentSystem)
{
this.hostInstance = componentSystem;
}
public BoidSchoolSpawnSystem hostInstance;
private BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders _lambdaParameterValueProviders;
[NativeDisableUnsafePtrRestriction]
private unsafe BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes* _runtimes;
public static StructuralChangeEntityProvider.PerformLambdaDelegate _performLambdaDelegate = new StructuralChangeEntityProvider.PerformLambdaDelegate(BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.PerformLambda);
private struct LambdaParameterValueProviders
{
public void ScheduleTimeInitialize(BoidSchoolSpawnSystem componentSystem)
{
this.forParameter_entity.ScheduleTimeInitialize(componentSystem, true);
this.forParameter_entityInQueryIndex.ScheduleTimeInitialize(componentSystem, true);
this.forParameter_boidSchool.ScheduleTimeInitialize(componentSystem, true);
this.forParameter_boidSchoolLocalToWorld.ScheduleTimeInitialize(componentSystem, true);
}
public BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes PrepareToExecuteWithStructuralChanges(ComponentSystemBase p0, EntityQuery p1)
{
BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes result = default(BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes);
result._entityProvider.PrepareToExecuteWithStructuralChanges(p0, p1);
result.runtime_entity = this.forParameter_entity.PrepareToExecuteWithStructuralChanges(p0, p1);
result.runtime_entityInQueryIndex = this.forParameter_entityInQueryIndex.PrepareToExecuteWithStructuralChanges(p0, p1);
result.runtime_boidSchool = this.forParameter_boidSchool.PrepareToExecuteWithStructuralChanges(p0, p1);
result.runtime_boidSchoolLocalToWorld = this.forParameter_boidSchoolLocalToWorld.PrepareToExecuteWithStructuralChanges(p0, p1);
return result;
}
[ReadOnly]
private LambdaParameterValueProvider_Entity forParameter_entity;
[ReadOnly]
private LambdaParameterValueProvider_EntityInQueryIndex forParameter_entityInQueryIndex;
[ReadOnly]
private LambdaParameterValueProvider_IComponentData<BoidSchool> forParameter_boidSchool;
[ReadOnly]
private LambdaParameterValueProvider_IComponentData<LocalToWorld> forParameter_boidSchoolLocalToWorld;
public struct Runtimes
{
public StructuralChangeEntityProvider _entityProvider;
public LambdaParameterValueProvider_Entity.StructuralChangeRuntime runtime_entity;
public LambdaParameterValueProvider_EntityInQueryIndex.StructuralChangeRuntime runtime_entityInQueryIndex;
public LambdaParameterValueProvider_IComponentData<BoidSchool>.StructuralChangeRuntime runtime_boidSchool;
public LambdaParameterValueProvider_IComponentData<LocalToWorld>.StructuralChangeRuntime runtime_boidSchoolLocalToWorld;
}
}
}
}
}
Onupdate
我们可以了解到IL的代码已经和源码天壤之别,现在来分析下
IL逆向
protected override void OnUpdate()
{
ForEachLambdaJobDescription entities = base.Entities;
BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0 <>c__DisplayClass_OnUpdate_LambdaJob = default(BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0);
<>c__DisplayClass_OnUpdate_LambdaJob.ScheduleTimeInitialize(this);
base.CompleteDependency();
EntityQuery query = this.<>OnUpdate_LambdaJob0_entityQuery;
this.<>OnUpdate_LambdaJob0_profilerMarker.Begin();
try
{
<>c__DisplayClass_OnUpdate_LambdaJob.Execute(this, query);
}
finally
{
this.<>OnUpdate_LambdaJob0_profilerMarker.End();
}
}
源码
protected override void OnUpdate()
{
Entities.WithStructuralChanges().ForEach((Entity entity, int entityInQueryIndex, in BoidSchool boidSchool, in LocalToWorld boidSchoolLocalToWorld) =>
{
var boidEntities = new NativeArray<Entity>(boidSchool.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
Profiler.BeginSample("Instantiate");
EntityManager.Instantiate(boidSchool.Prefab, boidEntities);
Profiler.EndSample();
var localToWorldFromEntity = GetComponentDataFromEntity<LocalToWorld>();
var setBoidLocalToWorldJob = new SetBoidLocalToWorld
{
LocalToWorldFromEntity = localToWorldFromEntity,
Entities = boidEntities,
Center = boidSchoolLocalToWorld.Position,
Radius = boidSchool.InitialRadius
};
Dependency = setBoidLocalToWorldJob.Schedule(boidSchool.Count, 64, Dependency);
Dependency = boidEntities.Dispose(Dependency);
EntityManager.DestroyEntity(entity);
}).Run();
}
看起来已经不是同一份代码了,Entities.WithStructuralChanges().ForEach和lamda表达式到哪里去了.
答案就在<>c__DisplayClass_OnUpdate_LambdaJob.Execute(this, query);中
public unsafe void Execute(ComponentSystemBase componentSystem, EntityQuery query)
{
BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes runtimes = this._lambdaParameterValueProviders.PrepareToExecuteWithStructuralChanges(componentSystem, query);
this._runtimes = &runtimes;
runtimes._entityProvider.IterateEntities((void*)(&this), (void*)this._runtimes, BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0._performLambdaDelegate);
}
IterateEntities函数说明了这里并不是多线程处理的,使用时要多加注意.
public unsafe void IterateEntities(void* jobStruct, void* runtimes, StructuralChangeEntityProvider.PerformLambdaDelegate action)
{
try
{
int entityCount = this.EntityCount;
for (int i = 0; i != entityCount; i++)
{
Entity entity = this.For(i);
bool flag = entity != Entity.Null;
if (flag)
{
action(jobStruct, runtimes, entity);
}
}
}
finally
{
this._query.ReleaseGatheredEntities(ref this._gatherEntitiesResult);
}
}
其中_performLambdaDelegate就是下面函数
public unsafe static void PerformLambda(void* jobStructPtr, void* runtimesPtr, Entity entity)
{
ref BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes ptr = UnsafeUtility.AsRef<BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0.LambdaParameterValueProviders.Runtimes>(runtimesPtr);
Entity entity2 = ptr.runtime_entity.For(entity);
int entityInQueryIndex = ptr.runtime_entityInQueryIndex.For(entity);
BoidSchool boidSchool2;
BoidSchool boidSchool = ptr.runtime_boidSchool.For(entity, out boidSchool2);
LocalToWorld localToWorld2;
LocalToWorld localToWorld = ptr.runtime_boidSchoolLocalToWorld.For(entity, out localToWorld2);
UnsafeUtility.AsRef<BoidSchoolSpawnSystem.<>c__DisplayClass_OnUpdate_LambdaJob0>(jobStructPtr).OriginalLambdaBody(entity2, entityInQueryIndex, ref boidSchool, ref localToWorld);
}
可以看到其中OriginalLambdaBody就是lamda表达式
public void OriginalLambdaBody(Entity entity, int entityInQueryIndex, [In] ref BoidSchool boidSchool, [In] ref LocalToWorld boidSchoolLocalToWorld)
{
this.hostInstance.<OnUpdate>b__1_0(entity, entityInQueryIndex, boidSchool, boidSchoolLocalToWorld);
}
这里可以看到b__1_0这个函数,可惜dnspy并没有解析出来.
直接去看il2cpp output的代码:
// System.Void Samples.Boids.BoidSchoolSpawnSystem::<OnUpdate>b__1_0(Unity.Entities.Entity,System.Int32,Samples.Boids.BoidSchool&,Unity.Transforms.LocalToWorld&)
IL2CPP_EXTERN_C IL2CPP_METHOD_ATTR void BoidSchoolSpawnSystem_U3COnUpdateU3Eb__1_0_mD857D466DB5D861FD717ABADB04442458A302DC1 (BoidSchoolSpawnSystem_tE41A950D814AC56EA06ED2356EAC22C71348012E * __this, Entity_tA8DE691EC83EB40E80A611C2E6D8C90C3C97AAF4 ___entity0, int32_t ___entityInQueryIndex1, BoidSchool_t0E6F71F9F614EFCD375DFBA4BBC9A4CCA94361E7 * ___boidSchool2, LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 * ___boidSchoolLocalToWorld3, const RuntimeMethod* method)
{
static bool s_Il2CppMethodInitialized;
if (!s_Il2CppMethodInitialized)
{
il2cpp_codegen_initialize_method (BoidSchoolSpawnSystem_U3COnUpdateU3Eb__1_0_mD857D466DB5D861FD717ABADB04442458A302DC1_MetadataUsageId);
s_Il2CppMethodInitialized = true;
}
NativeArray_1_t587B0E555A435D1A1EACD16A8F3C3EBCF3497F5E V_0;
memset((&V_0), 0, sizeof(V_0));
ComponentDataFromEntity_1_t4BFB53C0F238D124D1498C4139A2699C220B890E V_1;
memset((&V_1), 0, sizeof(V_1));
SetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1 V_2;
memset((&V_2), 0, sizeof(V_2));
EntityManager_t375E301E0D409D57A32EB6EEFEE4DCFA936B3FD0 V_3;
memset((&V_3), 0, sizeof(V_3));
SetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1 V_4;
memset((&V_4), 0, sizeof(V_4));
LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 V_5;
memset((&V_5), 0, sizeof(V_5));
{
// var boidEntities = new NativeArray<Entity>(boidSchool.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
BoidSchool_t0E6F71F9F614EFCD375DFBA4BBC9A4CCA94361E7 * L_0 = ___boidSchool2;
int32_t L_1 = L_0->get_Count_2();
NativeArray_1__ctor_mC76CAC41CA00C16A626EB0CDAE4684A692566BE2((NativeArray_1_t587B0E555A435D1A1EACD16A8F3C3EBCF3497F5E *)(&V_0), L_1, 3, 0, /*hidden argument*/NativeArray_1__ctor_mC76CAC41CA00C16A626EB0CDAE4684A692566BE2_RuntimeMethod_var);
// Profiler.BeginSample("Instantiate");
Profiler_BeginSample_m72E4BD9503BC991BAFAED992FF1CA4504C741865_inline(_stringLiteralD517C7CD0B15AC7D7FED8521FC3CE0FC3C729432, /*hidden argument*/NULL);
// EntityManager.Instantiate(boidSchool.Prefab, boidEntities);
EntityManager_t375E301E0D409D57A32EB6EEFEE4DCFA936B3FD0 L_2 = ComponentSystemBase_get_EntityManager_mEA56D9178F087FFF35990C12D2195FF634413AA5(__this, /*hidden argument*/NULL);
V_3 = L_2;
BoidSchool_t0E6F71F9F614EFCD375DFBA4BBC9A4CCA94361E7 * L_3 = ___boidSchool2;
Entity_tA8DE691EC83EB40E80A611C2E6D8C90C3C97AAF4 L_4 = L_3->get_Prefab_0();
NativeArray_1_t587B0E555A435D1A1EACD16A8F3C3EBCF3497F5E L_5 = V_0;
EntityManager_Instantiate_m1B56A6484833CE1493E4D08D41E9A3F35376D8A9((EntityManager_t375E301E0D409D57A32EB6EEFEE4DCFA936B3FD0 *)(&V_3), L_4, L_5, /*hidden argument*/NULL);
// Profiler.EndSample();
Profiler_EndSample_m78C76E709113E225D47687E26EA320E4FC548B71(/*hidden argument*/NULL);
// var localToWorldFromEntity = GetComponentDataFromEntity<LocalToWorld>();
ComponentDataFromEntity_1_t4BFB53C0F238D124D1498C4139A2699C220B890E L_6 = SystemBase_GetComponentDataFromEntity_TisLocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290_m22498B916F710E6FF82B815A6FE06558D1996E9C(__this, (bool)0, /*hidden argument*/SystemBase_GetComponentDataFromEntity_TisLocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290_m22498B916F710E6FF82B815A6FE06558D1996E9C_RuntimeMethod_var);
V_1 = L_6;
// var setBoidLocalToWorldJob = new SetBoidLocalToWorld
// {
// LocalToWorldFromEntity = localToWorldFromEntity,
// Entities = boidEntities,
// Center = boidSchoolLocalToWorld.Position,
// Radius = boidSchool.InitialRadius
// };
il2cpp_codegen_initobj((&V_4), sizeof(SetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1 ));
ComponentDataFromEntity_1_t4BFB53C0F238D124D1498C4139A2699C220B890E L_7 = V_1;
(&V_4)->set_LocalToWorldFromEntity_0(L_7);
NativeArray_1_t587B0E555A435D1A1EACD16A8F3C3EBCF3497F5E L_8 = V_0;
(&V_4)->set_Entities_1(L_8);
LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 * L_9 = ___boidSchoolLocalToWorld3;
LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 L_10 = (*(LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 *)L_9);
V_5 = L_10;
float3_t9500D105F273B3D86BD354142E891C48FFF9F71D L_11 = LocalToWorld_get_Position_m8FB60CB9859C480CEB3F9E8BDF5BCF5053B2135F((LocalToWorld_t9CECB89076D428C856FD9BE65391C0DE94B65290 *)(&V_5), /*hidden argument*/NULL);
(&V_4)->set_Center_2(L_11);
BoidSchool_t0E6F71F9F614EFCD375DFBA4BBC9A4CCA94361E7 * L_12 = ___boidSchool2;
float L_13 = L_12->get_InitialRadius_1();
(&V_4)->set_Radius_3(L_13);
SetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1 L_14 = V_4;
V_2 = L_14;
// Dependency = setBoidLocalToWorldJob.Schedule(boidSchool.Count, 64, Dependency);
SetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1 L_15 = V_2;
BoidSchool_t0E6F71F9F614EFCD375DFBA4BBC9A4CCA94361E7 * L_16 = ___boidSchool2;
int32_t L_17 = L_16->get_Count_2();
JobHandle_t8AEB8D31C25D7774C71D62B0C662525E6E36D847 L_18 = SystemBase_get_Dependency_m5F571303CFE0102AB40DF9277BBECA28CE052FAC(__this, /*hidden argument*/NULL);
JobHandle_t8AEB8D31C25D7774C71D62B0C662525E6E36D847 L_19 = IJobParallelForExtensions_Schedule_TisSetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1_mB9CD7D6F6010ADF5D28188B90014249EEB09EAE3(L_15, L_17, ((int32_t)64), L_18, /*hidden argument*/IJobParallelForExtensions_Schedule_TisSetBoidLocalToWorld_t825819EB0518E866FCB687E2533FFCFCB0EB19B1_mB9CD7D6F6010ADF5D28188B90014249EEB09EAE3_RuntimeMethod_var);
SystemBase_set_Dependency_m46343983A977DE641677E0C7032FE7ACCE4169D6(__this, L_19, /*hidden argument*/NULL);
// Dependency = boidEntities.Dispose(Dependency);
JobHandle_t8AEB8D31C25D7774C71D62B0C662525E6E36D847 L_20 = SystemBase_get_Dependency_m5F571303CFE0102AB40DF9277BBECA28CE052FAC(__this, /*hidden argument*/NULL);
JobHandle_t8AEB8D31C25D7774C71D62B0C662525E6E36D847 L_21 = NativeArray_1_Dispose_mD9324B5DA78797DB91D59E486F5D365CE0BDB1E4((NativeArray_1_t587B0E555A435D1A1EACD16A8F3C3EBCF3497F5E *)(&V_0), L_20, /*hidden argument*/NativeArray_1_Dispose_mD9324B5DA78797DB91D59E486F5D365CE0BDB1E4_RuntimeMethod_var);
SystemBase_set_Dependency_m46343983A977DE641677E0C7032FE7ACCE4169D6(__this, L_21, /*hidden argument*/NULL);
// EntityManager.DestroyEntity(entity);
EntityManager_t375E301E0D409D57A32EB6EEFEE4DCFA936B3FD0 L_22 = ComponentSystemBase_get_EntityManager_mEA56D9178F087FFF35990C12D2195FF634413AA5(__this, /*hidden argument*/NULL);
V_3 = L_22;
Entity_tA8DE691EC83EB40E80A611C2E6D8C90C3C97AAF4 L_23 = ___entity0;
EntityManager_DestroyEntity_m435E2D8727951379B9F0F9EA9A582E08FD2915A7((EntityManager_t375E301E0D409D57A32EB6EEFEE4DCFA936B3FD0 *)(&V_3), L_23, /*hidden argument*/NULL);
// }).Run();
return;
}
}
可以发现这就是真正lamda的内容.
SetBoidLocalToWorld
这个类就稍微说一下,这是生成每条鱼重要的函数,BoidSchoolSpawnSystem使用它生成每条鱼的Entity,多线程设置每条鱼的初始位置.
重要语法糖
protected internal override void OnCreateForCompiler()
{
base.OnCreateForCompiler();
this.<>OnUpdate_LambdaJob0_entityQuery = BoidSchoolSpawnSystem.<>GetEntityQuery_ForOnUpdate_LambdaJob0_From(this);
this.<>OnUpdate_LambdaJob0_profilerMarker = new ProfilerMarker("OnUpdate_LambdaJob0");
}
// Token: 0x06000012 RID: 18 RVA: 0x000024BC File Offset: 0x000006BC
public static EntityQuery <>GetEntityQuery_ForOnUpdate_LambdaJob0_From(ComponentSystemBase componentSystem)
{
EntityQueryDesc[] array = new EntityQueryDesc[1];
EntityQueryDesc entityQueryDesc = new EntityQueryDesc();
array[0] = entityQueryDesc;
entityQueryDesc.All = new ComponentType[]
{
ComponentType.ReadOnly<BoidSchool>(),
ComponentType.ReadOnly<LocalToWorld>()
};
return componentSystem.GetEntityQuery(array);
}
Unity会自动生成<>OnUpdate_LambdaJob0_entityQuery,它式收集BoidSchool类型的集合.被隐式的构造出来,再使用的时候要注意了.
BoidSchool和LocalToWorld他们都是由lamda表达式中的参数被感知到的.
总结
虽然源码不到百行,但最后展开的IL代码却有几百行.
Unity ECS在帮助我们减少代码量的同时,也掩盖了很多实现细节.对于熟悉的人工作效率提高,多于新接触的可能比较难以理解.
下次还要分享代码量更多的BoidSystem.