Search Unity

Convert many GameObjects and keep track on the converted entity

Discussion in 'Entity Component System' started by Green127, Aug 2, 2019.

  1. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    Hi, I'm currently working on a City builder with DOTS.

    But I have an issue with my prefab converted to entity.

    Here the script I have made:
    Code (CSharp):
    1.  
    2. // ==================================================================
    3. //  ■ DotsPrefabs.cs
    4. // ------------------------------------------------------------------
    5. //
    6. // ==================================================================
    7. using System.Collections.Generic;
    8. using Unity.Entities;
    9. using UnityEngine;
    10. using UnityEngine.Assertions;
    11. using Object = UnityEngine.Object;
    12.  
    13. // ------------------------------------------------------------------
    14. //  ♦
    15. // ------------------------------------------------------------------
    16. [RequiresEntityConversion]
    17. public class DotsPrefabs : MonoBehaviour, IDeclareReferencedPrefabs, IConvertGameObjectToEntity {
    18.     public List<Content> Contents;
    19.     public static readonly Dictionary<int, Entity> ConversionTable = new Dictionary<int, Entity>();
    20.     public static Entity GetEntityOf(GameObject prefab) => ConversionTable.ContainsKey(prefab.GetInstanceID()) ? ConversionTable[prefab.GetInstanceID()] : Global.NullEntity;
    21.     // --------------------------------------------------------------
    22.     //  ○
    23.     // --------------------------------------------------------------
    24.     public static Entity CreateInstanceOf(GameObject prefab) {
    25.         int id = prefab.GetInstanceID();
    26.         if (!ConversionTable.ContainsKey(id)) return Global.NullEntity;
    27.         Entity entity = ConversionTable[id];
    28.         //
    29.         if (!Global.ECS.EntityManager.Exists(entity))
    30.             Debug.LogError($"Entity doesn't exist for {prefab.name} -> {entity}");
    31.         //
    32.         entity = Global.ECS.EntityManager.Instantiate(entity);
    33.         return entity;
    34.     }
    35.     // --------------------------------------------------------------
    36.     //  ○
    37.     // --------------------------------------------------------------
    38.     public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs) {
    39.         foreach (Content content in this.Contents) {
    40.             foreach (Object obj in content.List) {
    41.                 if (obj is BuildingInfo buildingInfo) {
    42.                     Assert.IsNotNull(buildingInfo.Prefab);
    43.                     referencedPrefabs.Add(buildingInfo.Prefab);
    44.                 } else if (obj is VehicleInfo vehicleInfo) {
    45.                     Assert.IsNotNull(vehicleInfo.Prefab);
    46.                     referencedPrefabs.Add(vehicleInfo.Prefab);
    47.                 }
    48.             }
    49.         }
    50.     }
    51.     // --------------------------------------------------------------
    52.     //  ○
    53.     // --------------------------------------------------------------
    54.     public void Convert(Entity bank, EntityManager dstManager, GameObjectConversionSystem conversionSystem) {
    55.         List<Entity> citizenCars = new List<Entity>();
    56.         //
    57.         foreach (Content content in this.Contents) {
    58.             foreach (Object obj in content.List) {
    59.                 if (obj is BuildingInfo buildingInfo) {
    60.                     Entity entity = conversionSystem.GetPrimaryEntity(buildingInfo.Prefab);
    61.                     ConversionTable.Add(buildingInfo.Prefab.GetInstanceID(), entity);
    62.                 } else if (obj is VehicleInfo vehicleInfo) {
    63.                     Entity entity = conversionSystem.GetPrimaryEntity(vehicleInfo.Prefab);
    64.                     ConversionTable.Add(vehicleInfo.Prefab.GetInstanceID(), entity);
    65.                     citizenCars.Add(entity);
    66.                 }
    67.             }
    68.         }
    69.         //
    70.         Global.CitizenCars = citizenCars;
    71.     }
    72.     // -- END --
    73. }
    74.  
    Content (line 18) is a ScriptableObject that contain a list of UnityEngine.Object, I sort all my ScriptableObject like that.
    BuildingInfo (line 41) is a ScriptableObject that contain all sort of stuff related to a building. (price, id, prefab...)
    VehicleInfo (line 44) same but for Vehicles.

    In short: I have a ScriptableObject of type Content that contain all my buildings and another one with all my vehicles.
    There are a Gameobject in my scene with this script "DotsPrefabs" on it and a list of Content.

    This gameObject also have the script ConvertToEntity given by unity.

    What I want is when I start the scene all the prefab accessible through the BuildingInfo/VehicleInfo/Content get converted in entity and get referenced in the var ConversionTable.
    ConversionTable is a static Dictionary<int, Entity>, int is Prefab.GetInstanceID() and the Entity is the converted prefab.

    Like that I can call GetEntityOf() (line 24) to get a new entity instance of a certain prefab.

    All works good until unity 2019.2, so I guess something is not OK in my approach.
    (same issue in 2019.3 Alpha10)

    My problem is that at some point certain referenced entities in the ConversionTable are destroyed or moved... their Index/Version don't correspond to the prefab anymore and EntityManager.Exists return false on them (line 29).
    I did not touch these entities, I always access them through CreateInstanceOf, I'm sure only their instances is use in the game and not the prefab itself.

    How am I supposed to keep track of all the entity prefab ?

    Feel free to ask if you need more information.

    Thanks for reading and sorry for possible grammar mistake English is not my first language.
     
  2. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    Anyone ? I really need help here, I already lost more than 3 day of work on this problem...
     
  3. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    How are you determining that an Entity is null? Are you sure the lookup to your dictionary is valid and that your dictionary is in a valid state? Static variables are a really bad idea in Unity moving forward due to the ability to disable domain reload.
     
  4. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    The entity is not null, the index/version is still the same but when i try to Instantiate a new entity it fail and EntityManager.Exists return false too.

    What do you means by "dictionary in a valid state" ?

    I'm sure that at each game startup the dictionary is entirely reconstructed with correct Ids and Entities,
    every thing works as expected for few minutes then for some reason certain Entities get destroyed by the manager.

    I don't know why, none of my systems/jobs do this, and since the entity has been destroyed the value in the dictionary no longer corresponding to a valid prefab entity so if I try to instantiate I got an error message saying this entity does not exist anymore.
    Apparently the issue is after a certain quantity of instantiation the manager delete my prefab entities... do I have to explicitly saying to the manager to do not destroy them ?

    What can be the cause of this?
    Can an entity change index / version for any reason?
     
  5. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    I'd be curious whether the entity in the dictionary is an expected value. An Entity should not change index or version. It is a stable identifier. So either the entities in the dictionary are getting corrupted (common mistake is to assign stuff in an EntityCommandBuffer) or something is actually destroying the entities. Might be worth tagging all of your entities with ISystemStateComponentData to detect if an Entity is getting destroyed somewhere you didn't expect.
     
  6. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    Thanks for your help!

    To be sure none of my system is the cause of this I temporarily disable all of them, the only thing that's remain is the conversion, instantiation and deletion of instance.

    As you advised, I use ISystemStateComponentData to keep track of the prefab entities. I used this concept:
    https://docs.unity3d.com/Packages/com.unity.entities@0.1/manual/system_state_components.html

    What I have found: The entity is not deleted but they get corrupted somehow, even the entity debugger can't read them.

    A screenshot, just after starting the game:
    upload_2019-8-4_13-49-41.png
    StateComponent is the ISystemStateComponentData
    Only the root of prefab entities have this component.
    So 69 prefab entity is created and referenced in the ConversionTable.
    1107 child entities of this prefabs is present in the other group (the child contain all components like mesh/LOD/ect)

    All entities are in the correct group and valid and all works fine until I start to delete instance of entities.

    Here what I got:
    upload_2019-8-4_13-49-57.png

    As you can see the entities is still there but their name has changed and when I click on them the inspector show nothing.
    The entities affected by this bug seem random and not related to the instance I have created and deleted.

    It seems like this line is responsible for the errors EntityManager.DestroyEntity(entity)
    I have double check again, only instance of entities is destroyed never the prefab directly.

    This comportment don't make any sens to me.
     
  7. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I've had a look over it and I can't spot exactly what is going on/wrong however my instinct is telling me you really shouldn't be doing this in the middle of the conversion process.

    Instead should be setup in a bootstrap or system.
     
  8. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    I didn't do anything in the middle of the conversion.
    The conversion is done at start, than I use the converted entity referenced in the static var.
     
  9. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    I have not used the new conversion workflow much, but to me it seems your prefabs get deleted during/after conversion.

    Is there a pattern in the 69 entities (which keep in their supposed form)?
     
  10. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    This entities not corrupted. It’s how ISystemStateComponent works, until you remove system state component they will live. If you run code without ISSC you shouldn’t see this renamed entities.
     
  11. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    They get deleted randomly after i start to delete instances, some prefab get deleted even if the instance is from another prefab.

    I just try to replace ISystemStateComponentData by IComponentData for StateComponent and the result is the same.
    They stay in the group but they are unreadable.

    As explained in my previous link, deleted prefab with ISystemStateComponentData should go in the third group.
    upload_2019-8-4_18-14-40.png
     
  12. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Ah yes, just miss selected row
     
  13. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    The point I was trying to make was to use ISystemStateComponentData to see what entities were deleted in a frame. Having an ISystemStateComponentData that keeps some identifying values such as the name of the entity or index in the dictionary will help because you can then write a system that looks through all the deleted entities and if one is one of the prefabs, you can pause and look at the other entities deleted that frame and the other values of any game management entities you have around. That may help you pinpoint a bug in a system or something.
     
  14. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    Yes, that's what I thought, however, the entity do not get destroyed as expected they are just unreadable, and crash everything that's try to access them even the entity debugger can't...
     
  15. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    966
    I'm using a much simpler script with GameObjectConversionUtility.ConvertGameObjectHierarchy.
    Then I just have
    Code (CSharp):
    1. [RequiresEntityConversion]
    2. public class BuildingDataProxy : MonoBehaviour, IConvertGameObjectToEntity
    on my buildings. The class has a single value which references my BuildingDefinition class and I guess you could use your scriptable object there, then I just add the necessary components from the data to bring it into ECS.

    After the call to ConvertGameObjectHierarchy I get an entity that I store in a dictionary. I can get this entity and instantiate and destroy those without any issues.
     
  16. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,269
    That's because the "destroyed" entities are only partially destroyed and will be completely destroyed once you remove all the system state components from them. One of the components that gets destroyed is the component that holds the debug name of the entity, which is why I suggest copying that name into the system state component. Your system that detects the partially destroyed entities should check if the partially destroyed entity is a prefab entity (note that the prefab component will also be destroyed by this point as it is not a system state component). If it is, it should pause the game and let you examine what is going on. If not, it should remove the system state component data so that the entity can finish its destruction process and prevent what is effectively a memory leak (which I suspect is the real cause of the crashes you mentioned).
     
  17. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    Do you use LOD on your buildings ? Which version of unity and ECS's packages do you use ?

    I just pass the whole day again to try different approach but I keep facing strange bug related to RenderMeshSystemV2 and LOD components most of the time.

    I just rollback all the packages like it was with Unity 2019.1:
    Burst 1.1.2 > 1.0.4
    Collections 0.1.0 > 0.0.9p20
    Entities 0.1.0 > 0.0.12p33
    Hybrid Renderer 0.1.0 > 0.0.1p13
    Jobs 0.1.0 > 0.0.7p13
    Mathematics 1.1.0 > 1.0.1

    All the bugs is gone, every thing works just fine like it was before... I don't know if the bugs come from my code or a problem in the packages but... I think I will stop to update everything at day1 for a while.

    Finally! I can't continue my work on my project... Thank you all for your helps!

    If I find a better solution than this workaround, I'll post it here.
     
    Last edited: Aug 5, 2019
  18. Green127

    Green127

    Joined:
    Jul 18, 2018
    Posts:
    9
    UPDATE:

    Everything work as expected with 2019.2.1f1 and the latest version of all DOTS package mentioned before.
     
    korlinga and MostHated like this.