Search Unity

Converting GameObject + adding DynamicBuffer of other entities data worked yesterday, not today?

Discussion in 'Entity Component System' started by MostHated, Mar 23, 2019.

  1. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Hey all,
    I am having a strange issue. Below is the primary code for conversion that each of my waypoints has on it. Yesterday this code worked just fine, I saved, closed Unity, committed to a local git repo I keep, and went to bed. I get home from work and fire it back up and run it again and now it doesn't work.

    Code (CSharp):
    1. namespace instance.id.ECS
    2. {
    3. [RequiresEntityConversion]
    4. public class WaypointComponentProxy : MonoBehaviour, IDeclareReferencedPrefabs, IConvertGameObjectToEntity
    5. {
    6. public GameObject waypointObject;
    7. public List<WaypointConnectionBuffer> waypointBuffers = new List<WaypointConnectionBuffer>();
    8.  
    9. public void DeclareReferencedPrefabs(List<GameObject> gameObjects)
    10. {
    11. gameObjects.Add(waypointObject);
    12. }
    13.  
    14. public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    15. {
    16. var waypointComponentSpawnData = GetComponent<WaypointComponentSpawnData>();
    17. var list = GetComponent<WaypointConnectionList>().connections;
    18. for (int i = 0; i < list.Count; i++)
    19. {
    20. waypointBuffers.Add(new WaypointConnectionBuffer(waypointComponentSpawnData.waypointData, list[i].waypointData));
    21. }
    22.  
    23. var waypointEntity = conversionSystem.GetPrimaryEntity(waypointObject);
    24. var waypointTag = waypointComponentSpawnData.waypointTag;
    25. var waypointData = waypointComponentSpawnData.waypointData;
    26. waypointData.entity = waypointEntity;
    27.  
    28. dstManager.AddComponentData(entity, waypointData);
    29. dstManager.AddComponentData(entity, waypointTag);
    30. dstManager.AddBuffer<WaypointConnectionBuffer>(entity);
    31. var wpConnectionBuffer = dstManager.GetBuffer<WaypointConnectionBuffer>(entity);
    32.  
    33. if (waypointBuffers.Count == 0) return;
    34. for (int i = 0; i < waypointBuffers.Count; i++)
    35. {
    36. wpConnectionBuffer.Add(waypointBuffers[i]);
    37. }
    38.  
    39. #if UNITY_EDITOR
    40. dstManager.SetName(entity, gameObject.name);
    41. #endif
    42. }
    43. }
    44. }

    Here is the code for WaypointComponentSpawnData:
    Code (CSharp):
    1. namespace instance.id.ECS
    2. {
    3. public class WaypointComponentSpawnData : MonoBehaviour
    4. {
    5. public WaypointTag waypointTag;
    6. public WaypointECS waypointData;
    7. public int wapointID;
    8. public float AdditionalCost;
    9. public WaypointType waypointType;
    10. public bool isBlocked;
    11. public bool isIgnored;
    12.  
    13. public void Awake()
    14. {
    15. waypointData = new WaypointECS
    16. {
    17. wapointID = wapointID,
    18. Position = transform.position,
    19. waypointType = waypointType,
    20. AdditionalCost = AdditionalCost,
    21. isBlocked = isBlocked,
    22. isIgnored = isIgnored
    23. };
    24.  
    25. waypointTag = new WaypointTag();
    26. }
    27. }
    28. }

    Here is the code for creating WaypointConnectionBuffer directly below, which then creates a WaypointConnections item, which is below that:

    Code (CSharp):
    1. namespace instance.id.ECS
    2. {
    3. [InternalBufferCapacity(3)]
    4. public struct WaypointConnectionBuffer : IBufferElementData
    5. {
    6. public WaypointConnections waypointConnections;
    7.  
    8. public WaypointConnectionBuffer(WaypointECS origin, WaypointECS destination)
    9. {
    10. waypointConnections = new WaypointConnections(origin, destination);
    11. }
    12. }
    13. }

    Code (CSharp):
    1. namespace instance.id.ECS
    2. {
    3. public struct WaypointConnections
    4. {
    5. public WaypointECS origin;
    6. public WaypointECS destination;
    7. public float Distance => origin.GetDistance(origin, destination);
    8.  
    9. public WaypointConnections(WaypointECS Origin, WaypointECS Destination)
    10. {
    11. origin = Origin;
    12. destination = Destination;
    13. }
    14. }
    15. }


    The last one, WaypointConnectionList, is just a public List<WaypointComponentSpawnData> in which I add the other waypoints in which that current waypoint is directly connected to so the pathfinding system knows which points it is connected to so it can take its next hop in finding its path. So each waypoint looks and is set up like this:


    All that being said, here is the real kicker. While they all worked properly yesterday, they all come up improperly except one of them, which is Waypoint 11 (which you will notice that it has it's top WaypointECS component setup properly and shows it's waypointID of 11. Since it does, the WaypointConnectionBuffer component has the correct Origin as well as Destination WaypointECS information in it) as seen in my image below. I noticed that since I set it's connecting waypoints to be ones that just so happened to be waypoints that were in the Entities Debugger list that were created prior to 11 being created. Most all other waypoints only reference ones farther down the hierarchy list, such as WP1 connects to 2 and 3, WP2 connects to 3, 4, and 5, and so on. Since 11 is the current end of the list, it references back to prior ones, which still doesn't make sense if those prior ones have already been turned into Entities, I would imagine their GameObject data was already destroyed when converted?



    When they don't work properly, sometimes the waypoints own ID will be 0 with a location of 0,0,0 and then the Origin (top most entry in the WaypointConnectionsBuffer) will be the same 0-0,0,0, or if the actual waypoint has its own ID, the destination waypoint within the Buffer will be 0-0,0,0


    I am guessing the issue might have to do with timing? I was messing with it for hours trying to get it to work the way I needed it to, which is what eventually lead to my creating the separate class of WaypointComponentSpawnData and having it create the WaypointECS struct within Awake() since it seemed that the Awake() function occurred prior to systems within ECS performing their actions, so I was trying to create the data ahead of time so that it would be ready for each waypoint when it got to them.

    I think though, that ConvertToEntity() being set to "Convert and Destroy" might be getting rid of the data that other components are needing before it is able to get around to creating the entities. This is the only thing I can think of that could account for it working one day and not the next when the only thing that technically changed was probably the amount of things I had open/running on my PC and perhaps my having closed and reopening Unity.

    I was considering trying to use another system outside of the waypoints themselves to gather a list of each of the waypoint objects so it would then have access to all of their components, but when I tried something similar last night, it just didn't end up working out with the ways I was trying.

    Might it be that perhaps that each individual waypoint having the WaypointComponentProxy() convertion process happening on themselves individually causing an issue? I thought about making a dedicated system that, instead of having each waypoint perform it's own conversion, it would have a list of all waypoints and would then convert each one. It feels like, though, that as each waypoint gets processed and converted to any Entity and it's GameObject is removed, that keeps the next one that needs to use that GameObjects data from being able to do so, even if I have tried to save it's data into a separate list or anything.

    Does anyone happen to have any recommendations? Is there perhaps a way I can delay the deletion of the GameObjects until all of the entities have been created so that their data is copied over and it no longer needs the data from the GameObjects?

    Didn't realize that this was going to become so long, sorry about that, lol.
    Thanks!
    -MH
     
    Last edited: Mar 23, 2019
    Mikael-H likes this.
  2. joseph-t83

    joseph-t83

    Joined:
    Mar 28, 2014
    Posts:
    22
    I think there is a lot of confusion about what declaring a reference prefab is for. I don't think it's for dragging a prefab from your scene into the inspector to get its Entity and data in the conversion process. I think it's for referencing a prefab in your Assets folder. Then when you declare it and get its primary Entity, a prefab entity with default components is created that can then be used for instantiation later in the game.

    So I think it's working as intended and the reason all the values are zero is because you're getting back the default prefab entity from GetPrimaryEntity, not the entity of the waypoint you have in your scene. If you compare the entity index from the one you get back from GetPrimaryEntity and the one you find in the entity inspector that has all the correct values, they will be different.

    At least, this is how it seems to work from my experience.
     
    digitaliliad likes this.
  3. digitaliliad

    digitaliliad

    Joined:
    Jul 1, 2018
    Posts:
    64
    Yeah, this really messed me up, haha.
     
  4. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    Is there any way that anyone knows of to be able to maintain a reference from one GameObject/Entity to another (multiple in my situation) once the conversion actually occurs? The fact that I can't seem to hold on to them seems to be my issue.

    I think it does come down to timing though as when I first started working with it yesterday and when I wrote this post, I was only able to get one of them to work. Later on throughout the evening after having Unity open for a while, having lots of other things opened, using the exact same code/process, I was then able to get all of them except for one of them.

    I tried to see if I could create my own ConvertToEntity and change the Convert and Destroy / CreateHierarchy to perhaps have a delay in it between when it created the entities and when it destroyed the original gameobject, as if I am able to do that, I think it might help with this, even though it might not be the ideal way to go about it. Upon trying to make my own though, I ran into issues with having to try and use internal methods that I was unable to access and I am not knowledgable enough yet to know what / how to go about creating a workaround/circumventing the restriction.

    I am hoping that there might just be something I am not thinking of. There is still a ton I don't know, such as I didn't know that ISharedComponentData could have containers in them. I know it's not the best way to go about it due to performance reasons, but if I can have a container within ISharedComponentData, perhaps I could have each waypoint have one and even though they would all have different data per waypoint, I might be able to use that to hold the list of connected waypoints.

    I would have to just test it later and see what actually ends up happening. I only would need it at the very start of the game to make sure each entity had it's list, then I could just convert that into a buffer on each entity and then remove the ISharedComponentData from them. It sounds like it might be worth a try, even if it's just to learn that I am wrong in what I believe ISharedComponentData is capable of, lol.
     
  5. MostHated

    MostHated

    Joined:
    Nov 29, 2015
    Posts:
    1,235
    So, I tried to go about this a slightly different way just to test and see what would happen.

    I put a Monobehaviour on a GameObject and added a List<GameObject> and then this was the code in it.

    Code (CSharp):
    1. public class WaypointComponentSpawner : MonoBehaviour
    2.      {
    3.          [SerializeField] private List<GameObject> waypointPrefab;
    4.          private EntityManager entityManager;
    5.          private WaypointComponentSpawnData waypointComponentSpawnData;
    6.          private List<WaypointComponentSpawnData> connectionsSpawnData;
    7.          private Entity waypointEntityPrefab;
    8.          [SerializeField] public List<WaypointConnectionBuffer> waypointBuffers = new List<WaypointConnectionBuffer>();
    9.  
    10.          void Start()
    11.          {
    12.              entityManager = World.Active.EntityManager;
    13.  
    14.              for (int i = 0; i < waypointPrefab.Count; i++)
    15.              {
    16.                  connectionsSpawnData = new List<WaypointComponentSpawnData>();
    17.                  waypointComponentSpawnData = waypointPrefab[i].GetComponent<WaypointComponentSpawnData>();
    18.                  connectionsSpawnData = waypointPrefab[i].GetComponent<WaypointConnectionList>().connections;
    19.                  waypointEntityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(waypointPrefab[i], World.Active);
    20.  
    21.                  var waypointEntity = entityManager.Instantiate(waypointEntityPrefab);
    22.                  var waypointTag = waypointComponentSpawnData.waypointTag;
    23.                  var waypointData = waypointComponentSpawnData.waypointData;
    24.                  waypointData.entity = waypointEntity;
    25.  
    26.                  entityManager.AddComponentData(waypointEntity, waypointData);
    27.                  entityManager.AddComponentData(waypointEntity, waypointTag);
    28. #if UNITY_EDITOR
    29.                  entityManager.SetName(waypointEntity, waypointComponentSpawnData.gameObject.name);
    30. #endif
    31.                  entityManager.AddBuffer<WaypointConnectionBuffer>(waypointEntity);
    32.                  var wpConnectionBuffer = entityManager.GetBuffer<WaypointConnectionBuffer>(waypointEntity);
    33.          
    34.                  waypointBuffers.Clear();
    35.                  for (int wb = 0; wb < connectionsSpawnData.Count; wb++)
    36.                  {
    37.                      waypointBuffers.Add(new WaypointConnectionBuffer(waypointComponentSpawnData.waypointData, connectionsSpawnData[wb].waypointData, waypointEntity, connectionsSpawnData[wb].waypointData.entity));
    38.                  }
    39.          
    40.                  if (waypointBuffers.Count == 0) return;
    41.                  for (int w = 0; w < waypointBuffers.Count; w++)
    42.                  {
    43.                      wpConnectionBuffer.Add(waypointBuffers[w]);
    44.                  }
    45.          
    46.                  var waypointECS = entityManager.GetComponentData<WaypointECS>(waypointEntity);
    47.                  Debug.LogFormat($"Entity Info from Entity: {waypointECS.entity}");
    48.              }
    49.  
    50.              for (int i = 0; i < waypointPrefab.Count; i++)
    51.              {
    52.                  waypointPrefab[i].gameObject.Destroy();
    53.              }
    54.  
    55.              var entities = entityManager.GetAllEntities();
    56.              for (int w = 0; w < entities.Length; w++)
    57.              {
    58.                  if (entityManager.GetComponentCount(entities[w]) == 0)
    59.                      entityManager.DestroyEntity(entities[w]);
    60.              }
    61.          }
    62.      }
    I just went and manually removed the original GameObjects. Now, compared to before, all of the waypoints become entities and they all have their appropriate data, the only thing is though, initially it creates 3 sets of entities. One set has no components at all, so I ran the last section of code in the above class. That still left one set of extra entities. What I noticed though is the extra set looks like it is a parent of the correct and working set of entities.

    Along with that though, I get 4 errors of :
    A Native Collection has not been disposed, resulting in a memory leak. Allocated from: ComponentChunkIterator.CreateArchetypeChunkArray() Library\PackageCache\com.unity.entities@0.0.12-preview.29\Unity.Entities\Iterators\ComponentChunkIterator.cs:191

    Then another from :
    ParentSystem.OnUpdate() Library\PackageCache\com.unity.entities@0.0.12-preview.29\Unity.Transforms\ParentSystem.cs:262

    and then some more.

    So Obviously this isn't the correct way to go about it, but strangely, it seems to be the closest to actually working, as I have all of my waypoint entities and they all have the right data associated with them in their IComponentData and in the IBufferElementData. I guess it is back to the drawing board again, anyways.