Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

ECS Tear Down and Restart

Discussion in 'Entity Component System' started by GarthSmith, Jul 9, 2018.

  1. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Hello!

    We are using Unity's ECS in a single scene right now. I want to be able to clean up the ECS when we leave the scene. Then restart the ECS when we return to the scene. We are NOT using JobComponentSystem, yet.

    How do you guys handle this? Here are some options I have going through my head.
    • DestroyEverythingSystem: Have a
      ComponentSystem
      that does nothing but look for a
      DestroyEverythingComponentData
      to exist. If this component exists, then during
      DestroyEverythingSystem.OnUpdate()
      we will run
      PostUpdateCommands.DestroyEntity
      on all entities. (Including the Entity containing the DestroyEverythingComponentData.)
    • All Systems Clean Up Themselves: Define a
      LeavingSceneComponent : IComponentData
      . Every ComponentSystem will have a
      LeavingSceneGroup
      injected into it, so it can check and see if we are leaving the scene next frame. Once all ComponentSystems have had a chance to clean up, then the
      LeavingSceneSystem
      will actually change scenes. I would probably accomplish this by making a base system class that gets this group injected and implements
      protected void abstract OnLeavingScene();

    • Disable All Systems: Track all ComponentSystems we create, then go through each one and set
      oneOfManySystem.Enabled = false
      . Responsibility to destroy entities would be split across multiple systems. This would happen
      OnStopRunning()
    • World Destruction: Is there a way to just destroy the entire
      World
      and create a new one?
    • Your Great Idea: Something else?
    Thanks for the help everyone! Unity ECS is very exciting.
     
    Last edited: Jul 9, 2018
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,753
    Why not just loop EntityManager.GetAllEntities() and destroy them all? Your normal cleanup systems will run and dispose of everything nicely.

    Alternatively there is just Unity.Entities.World.DisposeAllWorlds();

    Or

    Unity.Entities.World.Active.Dispose();

    To completely dispose of a world.
     
    Last edited: Jul 10, 2018
    futurlab_peterh likes this.
  3. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    I ended up using GetAllEntities and looping through each entity. This seems to work pretty well!
    Code (csharp):
    1. private void OnDestroy()
    2. {
    3.    var entityManager = World.Active.GetOrCreateManager<EntityManager>();
    4.    var entityArray = entityManager.GetAllEntities();
    5.    foreach (var e in entityArray)
    6.       entityManager.DestroyEntity(e);
    7.    entityArray.Dispose();
    8. }
     
    Mikael-H and futurlab_peterh like this.
  4. Xitech_

    Xitech_

    Joined:
    Feb 19, 2013
    Posts:
    140
    Since the above code is deprecated. Here's a new shorter version, thanks to @GarthSmith

    Code (CSharp):
    1. void CleanWorldFromEntities()
    2.     {
    3.         var entityManager = World.Active.EntityManager;
    4.         foreach (var e in entityManager.GetAllEntities())
    5.             entityManager.DestroyEntity(e);
    6.     }
    Not sure if it helps to cach the entityManager to speed up the procces. It doesn't take long anyway ;)
     
    tarahugger likes this.
  5. sschoener

    sschoener

    Joined:
    Aug 18, 2014
    Posts:
    73
    You don't have to delete the entities one-by-one; you can delete them all at once:
    Code (CSharp):
    1. void DestroyAllEntities() {
    2.     EntityManager.DestroyEntity(EntityManager.UniversalQuery);
    3. }
    This will allow the underlying system to destroy all of them instead of tearing them down one by one.
     
  6. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    954
    Oh that's very efficient. Never even heard of UniversalQuery.
     
  7. kro11

    kro11

    Joined:
    Sep 23, 2019
    Posts:
    103
    GameObjectConversionSystem doesn't work when I move from another scene, but it works when I am loading this scene first. (Even without deleting entities). Any ideas?
     
    Mikael-H and florianhanke like this.
  8. Mikael-H

    Mikael-H

    Joined:
    Apr 26, 2013
    Posts:
    309
    I'm curious what the recommended way of e.g. switching back to a main menu where you have game object ui elements? Is it the
    Code (CSharp):
    1. EntityManager.DestroyEntity(EntityManager.UniversalQuery);
    route that is the best?
     
    florianhanke likes this.
  9. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,222
    That or some variation of it (I add a tag to the UniversalQuery so that I can instead delete a query with some components excluded for entities I want to keep around).
     
    jashan, Mikael-H and florianhanke like this.
  10. Mikael-H

    Mikael-H

    Joined:
    Apr 26, 2013
    Posts:
    309
    Thank you, this worked. I also in my case also had to dispose worlds since I am using dots netcode and the systems themselves have state (connections and whatnot), but for all normal cases I guess your version would be the best.
     
    adammpolak and florianhanke like this.
  11. adammpolak

    adammpolak

    Joined:
    Sep 9, 2018
    Posts:
    450
    @Mikael-H what did you end up doing? I am trying to solve the same problem.

    I want to move from "NavigationScene" (gameobject UI) to "MainScene" (ecs + netcode)

    I have a onclick function in NavigationScene:
    Code (CSharp):
    1.         var world = World.DefaultGameObjectInjectionWorld;
    2. #if !UNITY_CLIENT || UNITY_SERVER || UNITY_EDITOR
    3.         ClientServerBootstrap.CreateServerWorld(world, "ServerWorld");
    4.  
    5. #endif
    6.  
    7. #if !UNITY_SERVER
    8.         ClientServerBootstrap.CreateClientWorld(world, "ClientWorld");
    9. #endif
    10.  
    11. #if UNITY_EDITOR
    12.         if(Application.isPlaying)
    13. #endif
    14.             SceneManager.LoadSceneAsync(m_SceneName);
    15. #if UNITY_EDITOR
    16.         else
    17.             Debug.Log("Loading: " + m_SceneName);
    18. #endif
    This works, I found I need to create Client/Server world before loading the MainScene or the subscene converted entities don't make it into the Client/Server worlds.

    Now I want to get back from MainScene to NavigationScene (destroy the client/server worlds and all the entities). I have another function called to leave:
    Code (CSharp):
    1.     void  ClickedButton() {
    2.         World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
    3.         World.DisposeAllWorlds();
    4.         var bootstrap = new NetCodeBootstrap();
    5.         bootstrap.Initialize("defaultWorld");
    6. #if UNITY_EDITOR
    7.         if(Application.isPlaying)
    8. #endif
    9.             SceneManager.LoadSceneAsync(m_SceneName);
    10. #if UNITY_EDITOR
    11.         else
    12.             Debug.Log("Loading: " + m_SceneName);
    13. #endif
    14.     }
    I am able to get back and forth from NavigationScene to MainScene but was wondering if there was a better way to do it?
     
  12. Mikael-H

    Mikael-H

    Joined:
    Apr 26, 2013
    Posts:
    309
    Sorry, I don't remember... Gave up shortly after. I remember that this was something I had to fiddle a lot back and forth with and I was neverreally satisfied with how it worked. You should probably see if you can get the attention from one of the officials to learn how to really do this properly..
     
    adammpolak likes this.
  13. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    If I were to want to load up a new world from a save file on the same frame I destroyed the previous one. How do I get the clean up systems to run in between
    EntityManager.DestroyEntity(EntityManager.UniversalQuery);
    and
    LoadSaveFile()
    ?
     
  14. adammpolak

    adammpolak

    Joined:
    Sep 9, 2018
    Posts:
    450
    @Guedez do you want this to happen in a system or a monobehaviour?

    I was able to get my clean up/load new scene process working just by listing the functions sequentially in a monobehaviour

    World.DefaultGameObjectInjectionWorld.EntityManager.DestroyEntity(World.DefaultGameObjectInjectionWorld.EntityManager.UniversalQuery);
    World.DisposeAllWorlds();

    If you are doing it in a system, I would output the dependency of your clean up systems, and use it as a dependency for LoadSaveFile() and then run it in the EndSimulationEntityCommandBuffer.

    I ran into troubles trying to dispose of worlds in a system which was running in the world it tried to dispose. I believe you need to add it to PostUpdateCommands so it doesn't break.
     
    GabrielMachadoGomes likes this.
  15. Guedez

    Guedez

    Joined:
    Jun 1, 2012
    Posts:
    827
    In a system, I don't really destroy the world, so I need the proper clean up systems to run for when I load up a bunch of new entities.
    In the end I made an empty interface and ran through all the systems, the systems with the empty interface gets an Update, the others do not.
    I wish I could check for the entityqueries the systems created, there is half of the stuff I need to get it to work, but I currently can't check if an entityquery 'contains' a type, I can only check for exact matches