Search Unity

Does my code look okay? Basic spawner script.

Discussion in 'Entity Component System' started by giraffe1, Jul 24, 2020.

  1. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    This is my first time exploring ECS/DOTS. I am NOT a programmer so I not too sure on this stuff.

    Can anyone comment on my code? It seems to run and not log any errors. But I am not sure if I am laying out everything efficiently. Is there any improvements I should make?

    I couldn't find any examples/docs on good practices when it comes to using burst with a Entities.ForEach job as appose to IJobChuck. Is it okay to use burst with Entities.ForEach? I will eventually learn how to use IJobChuck but for learning purposes I want to do it later once I am more comfortable with this new way to coding.

    Another question is about public Entity prefab; in SpawnerData.cs. Is Entity considered as a value type or reference type? Is it considered blittable?

    Thanks for your help!

    Code (CSharp):
    1. public struct SpawnerData : IComponentData
    2. {
    3.     public Entity prefab;
    4.     public int count;
    5.     public float rate;      // Spawn rate
    6.     public float delay;     // Time delay before the first spawn
    7.     public float next;      // Time until the next spawn
    8. }

    Code (CSharp):
    1. public class SpawnerAuthoring : MonoBehaviour, IConvertGameObjectToEntity
    2. {
    3.     public GameObject prefab;
    4.     public int count = 10;
    5.     public float rate = 1f;
    6.     public float delay = 2f;
    7.     private float next = 0f;
    8.  
    9.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    10.     {
    11.         SpawnerData data = new SpawnerData();
    12.  
    13.         var conversionSetting = GameObjectConversionSettings.FromWorld(dstManager.World, conversionSystem.BlobAssetStore);
    14.         data.prefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(prefab, conversionSetting);
    15.  
    16.         data.count = count;
    17.         data.rate = rate;
    18.         data.delay = delay;
    19.  
    20.         if (delay == 0f)
    21.             data.next = 0f;
    22.         else
    23.             data.next = delay;
    24.  
    25.         dstManager.AddComponentData<SpawnerData>(entity, data);
    26.     }
    27. }

    Code (CSharp):
    1. public class SpawnerSystem : SystemBase
    2. {
    3.     private EndSimulationEntityCommandBufferSystem commandBufferSystem;
    4.  
    5.     protected override void OnCreate() { commandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>(); }
    6.  
    7.     protected override void OnUpdate()
    8.     {
    9.         var dT = Time.DeltaTime;
    10.         var commandBuffer = commandBufferSystem.CreateCommandBuffer().ToConcurrent();
    11.  
    12.         Entities.ForEach((Entity entity, int entityInQueryIndex, ref SpawnerData data) =>
    13.         {
    14.             // Check if it is time to spawn
    15.             if (data.next <= 0f)
    16.             {
    17.                 commandBuffer.Instantiate(entityInQueryIndex, data.prefab); // Spawn a prefab entity
    18.  
    19.                 data.count--;   // Reduce the spawn count
    20.  
    21.                 // Check if spawn count has reached 0
    22.                 if (data.count == 0)
    23.                 {
    24.                     commandBuffer.DestroyEntity(entityInQueryIndex, data.prefab);   // Destroy the reference entity
    25.                     commandBuffer.DestroyEntity(entityInQueryIndex, entity);        // Destroy this entity
    26.                 }
    27.  
    28.                 else
    29.                     data.next = data.rate;  // Reset the spawn timer
    30.             }
    31.  
    32.             else
    33.                 data.next -= dT;    // Reduce the time until next spawn
    34.  
    35.         }).WithBurst().ScheduleParallel();
    36.  
    37.         commandBufferSystem.AddJobHandleForProducer(Dependency);
    38.     }
    39. }
     
  2. Mockarutan

    Mockarutan

    Joined:
    May 22, 2011
    Posts:
    159
    Seems fair. I not sure how necessary it is to do this in parallel. It depends on how much you are spawning per frame. Are you spawning more than like 50 per frame?
     
  3. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    I am not spawning too much. Maybe 1 ai unit per half a second for about 40 - 50 units per spawner.

    Holy cow are you the same name on youtube? If so, I actually watched your series to get started with ECS. Amazing series. Your videos are probably the most helpful one I have come across so far.

    Thanks for your input!
     
    Last edited: Jul 24, 2020
  4. Holyren

    Holyren

    Joined:
    Jul 8, 2017
    Posts:
    35
    Looks similar to how I would do it. One thing I am thinking of is the fact that you access and read data.next every frame, however all other data is not being read as often. Therefore moving it to another IComponentData might improve performance by streamlining the memory and read/write. This is my guess.
     
    giraffe1 likes this.
  5. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    Yes, I was thinking about doing that too but wasn't sure if would be considered premature optimization.

    Thanks for your input!
     
  6. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,264
    1) You shouldn't need both delay and next fields. Just initialize next to be whatever the delay is during conversion.
    2) You should never need to call GameObjectConversionUtility inside IConvertGameObjectToEntity.Convert. Instead, use IDeclareReferencedPrefabs and ConversionSystem.{Try}GetPrimaryEntity. This will cause the prefab to be shared between spawners rather than create unique instances, which will reduce subscene size and runtime memory requirements. I have an example of what this looks like here: https://github.com/Dreaming381/lsss-wip/blob/master/Assets/_Code/Authoring/FactionAuthoring.cs
    3) Because of (2), you will not want to destroy the prefab entity anymore as it is shared. There are better (less scattered code and more performant) ways to clean up prefab entities. System is otherwise fine. I highly doubt the components and systems involved here are going to be chewing up much of your frame budget.
     
    giraffe1 likes this.
  7. giraffe1

    giraffe1

    Joined:
    Nov 1, 2014
    Posts:
    302
    1) Correct I didn't need delay in there. I have removed.

    2) and 3) I will study your github to understand.

    Thank you!
     
  8. Mockarutan

    Mockarutan

    Joined:
    May 22, 2011
    Posts:
    159
    Then you should use .Run() instead, .ScheduleParalell() has a bit of overhead, and you code will likely execute faster then the schedule code itself.

    And yes, I'm that guy on Youtube. Thanks a lot! That is that stuff that makes it really worth it! =) I'm on a sort of vacation right now, so the series will not return until in a little over a week. But it 's really nice to hear that people are finding it helpful!
     
    giraffe1 and florianhanke like this.