Search Unity

Children Of A Gameobject Not Acquiring Localtoworld Upon Conversion To Entity

Discussion in 'Entity Component System' started by Abbrew, Apr 13, 2019.

  1. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I have a Component called Body that keeps track of several locations relevant to it, like Vantage, CenterBottom, GunPosition, etc. In Monobehaviour land, the Body GameObject has Vantage, CenterBottom, GunPosition, etc child GameObjects so that they move when the parent moves and all the other properties that come with a parent-child relationship. This relationship is visible from the scene; I can drag around CenterBottom for example. I want to preserve this relationship once the Body GameObject is converted to an entity. Here is the incomplete Body.
    Code (CSharp):
    1. [RequiresEntityConversion]
    2. public class BodyComponent : MonoBehaviour, IConvertGameObjectToEntity, IDeclareReferencedPrefabs
    3. {
    4.     [SerializeField]
    5.     private GameObject vantage;
    6.     [SerializeField]
    7.     private GameObject bottom;
    8.  
    9.     public void Convert(Entity entity, EntityManager dstManager, GameObjectConversionSystem conversionSystem)
    10.     {
    11.         dstManager.AddComponentData(entity, new Body
    12.         {
    13.             vantage = conversionSystem.GetPrimaryEntity(vantage),
    14.             bottom = conversionSystem.GetPrimaryEntity(bottom),
    15.         });
    16.      
    17.     }
    18.  
    19.     public void DeclareReferencedPrefabs(List<GameObject> referencedPrefabs)
    20.     {
    21.         referencedPrefabs.Add(vantage);
    22.         referencedPrefabs.Add(bottom);
    23.     }
    24. }
    25. [Serializable]
    26. public struct Body : IComponentData
    27. {
    28.     public Entity vantage;
    29.     public Entity bottom;
    30.  
    31.     public int3 GetVantagePosition(ComponentDataFromEntity<LocalToWorld> refs)
    32.     {
    33.         return (int3)refs[vantage].Position;
    34.     }
    35.  
    36.     public int3 GetBottomPosition(ComponentDataFromEntity<LocalToWorld> refs)
    37.     {
    38.         return (int3)refs[bottom].Position;
    39.     }
    40. }
    41.  
    In the inspector I attached BodyComponent and ConvertToEntity (Convert and Inject GameObject) to the Body GameObject. I then created the Vantage and CenterBottom children GameObjects, and dragged them into BodyComponent.vantage and BodyComponent.centerBottom, respectively. However, I am then bombarded with
    Code (CSharp):
    1. ArgumentException: A component with type:LocalToWorld has not been added to the entity.
    2. Unity.Entities.EntityDataManager.AssertEntityHasComponent (Unity.Entities.Entity entity, Unity.Entities.ComponentType componentType) (at Library/PackageCache/com.unity.entities@0.0.12-preview.30/Unity.Entities/EntityDataManager.cs:372)
    3. Unity.Entities.EntityDataManager.AssertEntityHasComponent (Unity.Entities.Entity entity, System.Int32 componentType) (at Library/PackageCache/com.unity.entities@0.0.12-preview.30/Unity.Entities/EntityDataManager.cs:378)
    4. Unity.Entities.ComponentDataFromEntity`1[T].get_Item (Unity.Entities.Entity entity) (at Library/PackageCache/com.unity.entities@0.0.12-preview.30/Unity.Entities/Iterators/ComponentDataFromEntity.cs:57)
    5. Body.GetVantagePosition (Unity.Entities.ComponentDataFromEntity`1[T] refs) (at Assets/Scripts/Actors/Humanoids/Body/BodyComponent.cs:41)
    6. StressSystem+GetDalmatianUsersJob.Execute (Unity.Entities.Entity entity, System.Int32 index, Body& body) (at Assets/Scripts/Actors/Humanoids/Stress/StressSystem.cs:227)
    7. Unity.Entities.JobForEachExtensions+JobStruct_Process_ED`2[T,U0].ExecuteChunk (Unity.Entities.JobForEachExtensions+JobStruct_Process_ED`2[T,U0]& jobData, System.IntPtr bufferRangePatchData, System.Int32 begin, System.Int32 end, Unity.Entities.ArchetypeChunk* chunks, System.Int32* entityIndices) (at Library/PackageCache/com.unity.entities@0.0.12-preview.30/Unity.Entities/IJobForEach.gen.cs:545)
    8. Unity.Entities.JobForEachExtensions+JobStruct_Process_ED`2[T,U0].Execute (Unity.Entities.JobForEachExtensions+JobStruct_Process_ED`2[T,U0]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.0.12-preview.30/Unity.Entities/IJobForEach.gen.cs:518)
    It seems that the children GameObjects were never converted to Entities, and thus would not have acquired LocalToWorld, causing the ComponentDataFromEntity attempt to fail. I understand that you're supposed to drag a Prefab, not an instance of a Prefab, into the inspector. However, I need to be able to dictate the location of Vantage and all that visually from the Scene view. Is there a way to do this?
     
  2. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    UPDATE: Changing (Convert and Inject GameObject) to (Convert and Destroy) on the Body GameObject made it work. All my systems are recognizing the LocalToWorld correctly. However, upon starting the application the Body GameObject disappears from both the Scene view and GameObject hierarchy. I need for the Body GameObject to be draggable in Scene view during play for debugging purposes.
     
  3. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    The most convenient way to use hierarchies of hybrid entities with the conversion workflow is to directly edit the "ConvertToEntity" script in the entities package.
     
    Abbrew likes this.
  4. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Thanks for pointing me towards the source code. According to ConvertToEntity's ConvertAndInjectOriginal method
    Code (CSharp):
    1. public static void ConvertAndInjectOriginal(GameObject root)
    2.         {
    3.             var gameObjectWorld = GameObjectConversionUtility.CreateConversionWorld(World.Active, default(Hash128), 0);
    4.             // only the root entity is added to the world
    5.             GameObjectEntity.AddToEntityManager(gameObjectWorld.EntityManager, root);
    6.  
    7.             GameObjectConversionUtility.Convert(gameObjectWorld, World.Active);
    8.  
    9.             var entity =GameObjectConversionUtility.GameObjectToConvertedEntity(gameObjectWorld, root);
    10.             InjectOriginalComponents(World.Active.EntityManager, entity, root.transform);
    11.  
    12.             gameObjectWorld.Dispose();
    13.         }
    only the root entity is added to the EntityManager and would exist. Also, elsewhere in that file it checks if the parent also has ConvertToEntity; if it does, then the current GameObject is not converted. Unfortunately Unity.Entities is readonly and would revert once you try to edit it. There could be a workaround where you have an extra GameObject between the parent and the child, but that seems hacky and would be rendered obsolete once Unity implements recursive conversion.
     
  5. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    You can make edits to scripts in packages persist if you open them from file explorer. The path should be something like C:\Users\[your user name]\AppData\Local\Unity\cache\packages\packages.unity.com\. The AppData folder might be hidden or require admin access.
     
  6. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    These are the edits I make:

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using Unity.Collections;
    5. using Unity.Collections.LowLevel.Unsafe;
    6. using UnityEditor;
    7. using UnityEngine;
    8.  
    9. namespace Unity.Entities
    10. {
    11.     public class ConvertToEntity : MonoBehaviour
    12.     {
    13.         public enum Mode
    14.         {
    15.             ConvertAndDestroy,
    16.             ConvertAndInjectGameObject
    17.         }
    18.  
    19.         public Mode ConversionMode;
    20.      
    21.         void Awake()
    22.         {
    23.             if (World.Active != null)
    24.             {
    25.                 // Root ConvertToEntity is responsible for converting the whole hierarchy
    26.                 if (transform.parent != null && transform.parent.GetComponentInParent<ConvertToEntity>() != null)
    27.                     return;
    28.              
    29.                 if (ConversionMode == Mode.ConvertAndDestroy)
    30.                     ConvertHierarchy(gameObject);
    31.                 else
    32.                     ConvertAndInjectOriginal(gameObject);
    33.             }
    34.             else
    35.             {
    36.                 UnityEngine.Debug.LogWarning("ConvertEntity failed because there was no Active World", this);
    37.             }
    38.         }
    39.      
    40.         static void InjectOriginalComponents(EntityManager entityManager, Entity entity, Transform transform)
    41.         {
    42.             foreach (var com in transform.GetComponents<Component>())
    43.             {
    44.                 if (com is GameObjectEntity || com is ConvertToEntity || com is ComponentDataProxyBase)
    45.                     continue;
    46.              
    47.                 entityManager.AddComponentObject(entity, com);
    48.             }
    49.         }
    50.  
    51.         public static void AddRecurse(EntityManager manager, Transform transform)
    52.         {
    53.             GameObjectEntity.AddToEntityManager(manager, transform.gameObject);
    54.          
    55.             /*var convert = transform.GetComponent<ConvertToEntity>();
    56.             if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
    57.                 return;*/
    58.              
    59.             foreach (Transform child in transform)
    60.                 AddRecurse(manager, child);
    61.         }
    62.  
    63.         public static bool InjectOriginalComponents(World srcGameObjectWorld, EntityManager simulationWorld, Transform transform)
    64.         {
    65.             /*var convert = transform.GetComponent<ConvertToEntity>();
    66.  
    67.             if (convert != null && convert.ConversionMode == Mode.ConvertAndInjectGameObject)
    68.             {
    69.                 var entity = GameObjectConversionUtility.GameObjectToConvertedEntity(srcGameObjectWorld, transform.gameObject);
    70.                 InjectOriginalComponents(simulationWorld, entity, transform);
    71.                 transform.parent = null;
    72.                 return true;
    73.             }*/
    74.          
    75.             for (int i = 0; i < transform.childCount;)
    76.             {
    77.                 if (!InjectOriginalComponents(srcGameObjectWorld, simulationWorld, transform.GetChild(i)))
    78.                     i++;
    79.             }
    80.  
    81.             return false;
    82.         }
    83.  
    84.         public static void ConvertHierarchy(GameObject root)
    85.         {
    86.             var gameObjectWorld = GameObjectConversionUtility.CreateConversionWorld(World.Active, default(Hash128), 0);
    87.  
    88.             AddRecurse(gameObjectWorld.EntityManager, root.transform);
    89.  
    90.             GameObjectConversionUtility.Convert(gameObjectWorld, World.Active);
    91.  
    92.             InjectOriginalComponents(gameObjectWorld, World.Active.EntityManager, root.transform);
    93.  
    94.             GameObject.Destroy(root);
    95.  
    96.             gameObjectWorld.Dispose();
    97.         }
    98.  
    99.         //should be the same as ConvertHierarchy method above, but without GameObject.Destroy(root)
    100.         public static void ConvertAndInjectOriginal(GameObject root)
    101.         {
    102.          
    103.             var gameObjectWorld = GameObjectConversionUtility.CreateConversionWorld(World.Active, default(Hash128), 0);
    104.  
    105.  
    106.  
    107.             AddRecurse(gameObjectWorld.EntityManager, root.transform);
    108.  
    109.  
    110.  
    111.             GameObjectConversionUtility.Convert(gameObjectWorld, World.Active);
    112.  
    113.  
    114.  
    115.             InjectOriginalComponents(gameObjectWorld, World.Active.EntityManager, root.transform);
    116.  
    117.  
    118.  
    119.             gameObjectWorld.Dispose();[/COLOR][/FONT][/LEFT]
    120.         }
    121.  
    122.     }
    123. }
    124.  

    The only thing is that it doesn't actually inject the hybrid components automatically, but you can use IConvertGameObjectToEntity's Convert method to handle which hybrid components are injected and which are destroyed.
     
    Last edited: Apr 13, 2019
    Abbrew likes this.
  7. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    That looks like it would work. However, updating the Entities package would wipe your suggested changes. Since Unity is planning on adding "recursive conversion" which may solve this issue it might be better to wait
     
  8. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    [FIXED] Adding the CopyTransformFromGameObject component to the entity during the conversion method allowed a converted GameObject to be draggable in the editor