CrowdSpawnerSystem.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. using GPUECSAnimationBaker.Engine.AnimatorSystem;
  2. using Unity.Burst;
  3. using Unity.Collections;
  4. using Unity.Entities;
  5. using Unity.Mathematics;
  6. using Unity.Transforms;
  7. namespace GPUECSAnimationBaker.Samples.SampleScenes.Festival.CrowdSpawnerSystem
  8. {
  9. [BurstCompile]
  10. public partial struct CrowdSpawnerSystem : ISystem
  11. {
  12. private BufferLookup<GpuEcsAnimationDataBufferElement> gpuEcsAnimationDataBufferLookup;
  13. private ComponentLookup<LocalTransform> localTransformLookup;
  14. [BurstCompile]
  15. public void OnCreate(ref SystemState state)
  16. {
  17. gpuEcsAnimationDataBufferLookup = state.GetBufferLookup<GpuEcsAnimationDataBufferElement>(isReadOnly: true);
  18. localTransformLookup = state.GetComponentLookup<LocalTransform>(isReadOnly: true);
  19. }
  20. [BurstCompile]
  21. public void OnUpdate(ref SystemState state)
  22. {
  23. gpuEcsAnimationDataBufferLookup.Update(ref state);
  24. localTransformLookup.Update(ref state);
  25. EndSimulationEntityCommandBufferSystem.Singleton ecbSystem =
  26. SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
  27. EntityCommandBuffer.ParallelWriter ecb = ecbSystem.CreateCommandBuffer(state.WorldUnmanaged).AsParallelWriter();
  28. float deltaTime = SystemAPI.Time.DeltaTime;
  29. state.Dependency = new CrowdSpawnerJob()
  30. {
  31. ecb = ecb,
  32. deltaTime = deltaTime,
  33. gpuEcsAnimationDataBufferLookup = gpuEcsAnimationDataBufferLookup,
  34. localTransformLookup = localTransformLookup
  35. }.ScheduleParallel(state.Dependency);
  36. }
  37. [BurstCompile]
  38. private partial struct CrowdSpawnerJob : IJobEntity
  39. {
  40. public EntityCommandBuffer.ParallelWriter ecb;
  41. [ReadOnly] public float deltaTime;
  42. [ReadOnly] public BufferLookup<GpuEcsAnimationDataBufferElement> gpuEcsAnimationDataBufferLookup;
  43. [ReadOnly] public ComponentLookup<LocalTransform> localTransformLookup;
  44. public void Execute(
  45. ref CrowdSpawnerUpdateComponent crowdSpawnerUpdate,
  46. in CrowdSpawnerComponent crowdSpawner,
  47. in DynamicBuffer<CrowdSpawnerAnimatorBufferElement> crowdSpawnerAnimators,
  48. in DynamicBuffer<CrowdSpawnerAnimatorPrefabBufferElement> crowdSpawnerAnimatorPrefabs,
  49. Entity crowdSpawnerEntity, [ChunkIndexInQuery] int sortKey)
  50. {
  51. if (crowdSpawner.rows != crowdSpawnerUpdate.rows || crowdSpawner.cols != crowdSpawnerUpdate.cols)
  52. {
  53. crowdSpawnerUpdate.updateTime -= deltaTime;
  54. if (crowdSpawnerUpdate.updateTime <= 0)
  55. {
  56. // First delete all existing entities
  57. foreach (CrowdSpawnerAnimatorBufferElement crowdSpawnerAnimator in crowdSpawnerAnimators)
  58. ecb.DestroyEntity(sortKey, crowdSpawnerAnimator.gpuEcsAnimator);
  59. DynamicBuffer<CrowdSpawnerAnimatorBufferElement> newCrowdSpawnerAnimators
  60. = ecb.SetBuffer<CrowdSpawnerAnimatorBufferElement>(sortKey, crowdSpawnerEntity);
  61. newCrowdSpawnerAnimators.Clear();
  62. // Calculate the base offset so that the square of entities is centered around the origin
  63. float3 baseOffset = new float3(
  64. -(crowdSpawnerUpdate.cols - 1) * crowdSpawner.spacing / 2f,
  65. 0f,
  66. -(crowdSpawnerUpdate.rows - 1) * crowdSpawner.spacing / 2f);
  67. for (int col = 0; col < crowdSpawnerUpdate.cols; col++)
  68. {
  69. for (int row = 0; row < crowdSpawnerUpdate.rows; row++)
  70. {
  71. newCrowdSpawnerAnimators.Add(new CrowdSpawnerAnimatorBufferElement()
  72. {
  73. gpuEcsAnimator = CreateNewAnimator(ref crowdSpawnerUpdate, crowdSpawner, sortKey,
  74. baseOffset, col, row, crowdSpawnerAnimatorPrefabs)
  75. });
  76. }
  77. }
  78. ecb.SetComponent<CrowdSpawnerComponent>(sortKey, crowdSpawnerEntity, new CrowdSpawnerComponent()
  79. {
  80. cols = crowdSpawnerUpdate.cols,
  81. rows = crowdSpawnerUpdate.rows,
  82. spacing = crowdSpawner.spacing
  83. });
  84. }
  85. }
  86. }
  87. private Entity CreateNewAnimator(ref CrowdSpawnerUpdateComponent crowdSpawnerUpdate, CrowdSpawnerComponent crowdSpawner,
  88. int sortKey, float3 baseOffset, int col, int row,
  89. in DynamicBuffer<CrowdSpawnerAnimatorPrefabBufferElement> crowdSpawnerAnimatorPrefabs)
  90. {
  91. // Select a random prefab from the available buffer
  92. Entity gpuEcsAnimatorPrefab = crowdSpawnerAnimatorPrefabs[
  93. crowdSpawnerUpdate.random.NextInt(0, crowdSpawnerAnimatorPrefabs.Length)].gpuEcsAnimatorPrefab;
  94. // Spawn a character
  95. Entity gpuEcsAnimator = ecb.Instantiate(sortKey, gpuEcsAnimatorPrefab);
  96. // set the position according to column, row & spacing values
  97. // Preserve the scale that was set in the prefab
  98. ecb.SetComponent(sortKey, gpuEcsAnimator, new LocalTransform()
  99. {
  100. Position = baseOffset + new float3(
  101. col * crowdSpawner.spacing + crowdSpawnerUpdate.random.NextFloat(-crowdSpawner.spacing / 4f, crowdSpawner.spacing / 4f)
  102. , 0,
  103. row * crowdSpawner.spacing + crowdSpawnerUpdate.random.NextFloat(-crowdSpawner.spacing / 4f, crowdSpawner.spacing / 4f)
  104. ),
  105. Rotation = quaternion.Euler(0, crowdSpawnerUpdate.random.NextFloat(-math.PI, math.PI), 0),
  106. Scale = localTransformLookup[gpuEcsAnimatorPrefab].Scale
  107. });
  108. // Pick a random animation ID from the available animations
  109. DynamicBuffer<GpuEcsAnimationDataBufferElement> animationDataBuffer = gpuEcsAnimationDataBufferLookup[gpuEcsAnimatorPrefab];
  110. int animationID = crowdSpawnerUpdate.random.NextInt(0, animationDataBuffer.Length);
  111. // Kick off the correct animation with a random time offset so to avoid synchronized animations
  112. ecb.SetComponent(sortKey, gpuEcsAnimator, new GpuEcsAnimatorControlComponent()
  113. {
  114. animatorInfo = new AnimatorInfo()
  115. {
  116. animationID = animationID,
  117. blendFactor = 0,
  118. speedFactor = 1f
  119. },
  120. startNormalizedTime = crowdSpawnerUpdate.random.NextFloat(0f, 1f),
  121. transitionSpeed = 0
  122. });
  123. return gpuEcsAnimator;
  124. }
  125. }
  126. }
  127. }