Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question How to destroy entities and spawn game objects

Discussion in 'Entity Component System' started by MCillor, Nov 4, 2020.

  1. MCillor

    MCillor

    Joined:
    Aug 9, 2018
    Posts:
    5
    I've created a simple game using Mono Behaviour, now I want to convert parts of it to DOTS.
    I'm trying to recreate my enemy system in ECS and I can't find a simple way to destroy killed enemys.


    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
    Entities.ForEach((Entity enemy, in EnemyData data) => {
    if(data.health <= 0)
    {
    //Destroy(enemy);
    }
    }).Run();

    return default;
    }


    What's the best way to do this?

    Also, in the same place I want to destroy this entity, I'd like the spawn the loot. Currently my loot is a game Object prefab that when the player collides with it, goes to his inventory.
    I really wanted a way to just spawn a gameObject using System, because right now I don't want to mess with collision between two different worlds. Is there a way to do it? If not, what can I do to make the loot collectable.
     
    Zagule likes this.
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,984
    Here's a real-world example: https://github.com/Dreaming381/lsss...ms/Gameplay/DestroyShipsWithNoHealthSystem.cs
    That shows off destroying entities using an EntityCommandBuffer and instantiating new entities at the same time.

    For collision between worlds, you might consider invoking AddHybridComponent on the collider during conversion. Or perhaps AddHybridComponent on some MonoBehaviour that instantiates the prefab in Awake(). There's lots of ways to go about it.
     
    Zagule likes this.
  3. MCillor

    MCillor

    Joined:
    Aug 9, 2018
    Posts:
    5
    Unfortunately your code doesn't work, I have errors everywhere. I tried messing with the code but I can't make it work.


    public class EnemyLifeSystem : JobComponentSystem
    {
    BeginInitializationEntityCommandBufferSystem m_ecbSystem;

    protected override void OnCreate()
    {
    m_ecbSystem = World.GetExistingSystem<BeginInitializationEntityCommandBufferSystem>();
    }

    protected override JobHandle OnUpdate(JobHandle Dependency)
    {
    var ecbPackage = m_ecbSystem.CreateCommandBuffer();
    var ecb = ecbPackage.AsParallelWriter();

    Entities.ForEach((Entity enemy, int entityInQueryIndex, in EnemyData data) =>
    {
    if (data.health <= 0)
    {
    ecb.DestroyEntity(entityInQueryIndex, enemy);
    }
    }).Run();

    return default;
    }
    }
     
  4. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    You are using run so there not much point in using an ECS and you have no dependency to give it like in DreamingImLatios's code.

    If you use run you should just use the entity manager instead.
     
  5. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    Here's a quick rewrite of your code using EntityManager as WAYN_Games said (since you are using Run instead of Schedule/ScheduleParallel:

    EDIT: This won't work. It's missing ".WithStructuralChanges ()". I've provided a corrected example a little further down in the thread.

    Code (CSharp):
    1.  
    2. // use SystemBase insetad of JobComponentSystem
    3. public class EnemyLifeSystem : SystemBase
    4. {
    5.     // you don't need the ECB System if you're using EntityManager
    6.     /*
    7.     BeginInitializationEntityCommandBufferSystem m_ecbSystem;
    8.  
    9.     protected override void OnCreate()
    10.     {
    11.         m_ecbSystem = World.GetExistingSystem<BeginInitializationEntityCommandBufferSystem>();
    12.     }
    13.     */
    14.  
    15.     protected override void OnUpdate ()
    16.     {
    17.         // again, not using an ECB
    18.         /*
    19.         var ecbPackage = m_ecbSystem.CreateCommandBuffer();
    20.         var ecb = ecbPackage.AsParallelWriter();
    21.         */
    22.         Entities
    23.             // you don't need "int entityInQueryIndex" if you're not using parallel scheduling
    24.             //.ForEach((Entity enemy, int entityInQueryIndex, in EnemyData data) => {
    25.             .ForEach((Entity enemy, in EnemyData data) => {
    26.                 if (data.health <= 0) {
    27.                     // using EntityManager instead of an ECB
    28.                     //ecb.DestroyEntity(entityInQueryIndex, enemy);
    29.                     EntityManager.Destroy (enemy);
    30.                 }
    31.  
    32.             }).Run();
    33.     }
    34. }
    That setup will run the Job on the main thread (since it uses Run instead of Schedule or ScheduleParallel).

    EDIT: I just realized it was using JobComponentSystem as the base class, I've updated the code to use SystemBase as the base class instead.
     
    Last edited: Nov 7, 2020
    Zagule likes this.
  6. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    Here's an example of how to setup your code using Parallel Scheduling (running the job on multiple parallel threads):

    Code (CSharp):
    1. // using SystemBase insead of JobComponentSystem
    2. public class EnemyLifeSystem : SystemBase
    3. {
    4.     // are you sure you want to use the BeingInitializationEntityCommandBufferSystem? I'm not sure what
    5.     // your overall logic looks like, but this seems more like EndSimulationEntityCommandBufferSystem
    6.     // functionality. your choice though.
    7.     BeginInitializationEntityCommandBufferSystem m_ecbSystem;
    8.  
    9.     // setup the ECB System on create
    10.     protected override void OnCreate() {
    11.         m_ecbSystem = World.GetExistingSystem<BeginInitializationEntityCommandBufferSystem>();
    12.     }
    13.  
    14.     protected override void OnUpdate ()
    15.     {
    16.         // create the ECB (as a parallel writer)
    17.         var ecb = m_ecbSystem.CreateCommandBuffer().AsParallelWriter ();
    18.      
    19.         // update the dependency with the JobHandle for this job
    20.         Dependency = Entities
    21.             .ForEach((Entity enemy, int entityInQueryIndex, in EnemyData data) => {
    22.                 if (data.health <= 0) {
    23.                     ecb.DestroyEntity(entityInQueryIndex, enemy);
    24.                 }
    25.  
    26.             // schedule the Job as a parallel job, passing in the Dependency property from the system
    27.             }).ScheduleParallel (Dependency);
    28.          
    29.         // now add the updated Dependency JobHandle to the ECB system so that it is aware of it
    30.         m_ecbSystem.AddJobHandleForProducer (Dependency);
    31.     }
    32. }
     
    deus0 likes this.
  7. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,984
    I would agree with this advice if unmanaged systems were here, but unfortunately they aren't and using EntityManager in this context is very slow. I once had an algorithm that was conditionally enabling entities. I wrote a custom EnableCommandBuffer that recorded entities and during playback handled the LinkedEntityGroup. Because this enabled Burst, I saw a 20x speedup.

    Telling me you have errors and things don't work isn't a whole lot to go off of. Other than the fact that you are using JobComponentSystem instead of SystemBase (in my real-world example, SubSystem is a subclass of SystemBase), I don't actually see much wrong with your modified code.

    As a side note, please use the other code of code block than the one you are using. The other one gives the scroll bars, line count, and syntax highlighting.
    Have you tested this? I would think that would not work as you are missing WithStructuralChanges().
    I think he was using BeginInitializationEntityCommandBufferSystem because that's what my code uses. I have a single sync point there and no where else in my game. But you are right that it might be something worth changing for them. This example is also correct.
     
  8. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    Nope I didn't test it. That's what I get for trying to write something up quickly in notepad. You're right, that's not going to work without .WithStructuralChanges(). Here's the corrected code:


    Code (CSharp):
    1. public class EnemyLifeSystem : SystemBase
    2. {
    3.     // don't need the ECB System if you're using EntityManager
    4.     BeginInitializationEntityCommandBufferSystem m_ecbSystem;
    5.  
    6.     protected override void OnCreate()
    7.     {
    8.         m_ecbSystem = World.GetExistingSystem<BeginInitializationEntityCommandBufferSystem>();
    9.     }
    10.  
    11.     protected override void OnUpdate()
    12.     {
    13.         /*
    14.         var ecbPackage = m_ecbSystem.CreateCommandBuffer();
    15.         var ecb = ecbPackage.AsParallelWriter();
    16.         */
    17.         Entities
    18.             // you'll need to mark this WithStructuralChanges since it is making
    19.             // structural changes to entities (in this destroying them). This also means that this
    20.             // won't be Burst compiled.
    21.             .WithStructuralChanges ()
    22.             // you don't need "int entityInQueryIndex" if you're not using Parallel scheduling
    23.             //.ForEach((Entity enemy, int entityInQueryIndex, in EnemyData data) => {
    24.             .ForEach((Entity enemy, in EnemyData data) => {
    25.                 if (data.health <= 0) {
    26.                     // using EntityManager instead of an ECB
    27.                     //ecb.DestroyEntity(entityInQueryIndex, enemy);
    28.                     EntityManager.DestroyEntity (enemy);
    29.                 }
    30.  
    31.             }).Run();
    32.     }
    33. }

    Another thing to note about the code above using EntityManager, because it's marked as "WithStructuralChanges" it won't be Burst compiled.

    EDIT: Fixed typo. Changed "EntityManger.Destroy (enemy);" to "EntityManager.DestoryEntity (enemy);" as the method name is actually DestroyEntity () and not Destroy ().
     
    Last edited: Nov 9, 2020
  9. MCillor

    MCillor

    Joined:
    Aug 9, 2018
    Posts:
    5
    You guys were amazing. Thanks for the help and the patient.
    It works perfectly.
    The only thing I needed to change was changing "EntityManager.Destroy (enemy);" to "EntityManager.DestroyEntity (enemy);"
     
  10. DK_A5B

    DK_A5B

    Joined:
    Jan 21, 2016
    Posts:
    110
    Glad to hear it. That "Destroy" vs "DestoryEntity" was another sloppy typo on my part. Sorry about that. I'll edit my post in case someone else comes across this thread with a similar question.
     
  11. MCillor

    MCillor

    Joined:
    Aug 9, 2018
    Posts:
    5
    I tried this one too, just for the sake of it, and it also works!
    As recommended I used EndSimulationEntityCommandBufferSystem, and it worked perfectly, and it seems that it'll work with Burst.
    So if anyone wants to create a system to destroy entities, go with this code.
     
  12. Zagule

    Zagule

    Joined:
    Aug 23, 2016
    Posts:
    4
    Thanks, man! that's awesome. Solved my problem