Search Unity

Unity locks up when creating multiple Attach components

Discussion in 'Entity Component System' started by LukePammant, Jan 29, 2019.

  1. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    I have encountered a strange issue that crashes Unity every time I try to instantiate a zombie. The code that I added is below. Basically all I'm trying to do is look through all of the zombies subcomponents for a GameObjectEntity and then using the AttachComponent to mark the root entity as it's parent.

    Perhaps I'm doing something I shouldn't be doing and I can do it differently w/ ECS. What I'm trying to accomplish is this - the zombie prefab has sub-gameobjects with things colliders that need a `TeamComponent` in order to tell if it should deal damage to the thing it's colliding with. There is an ability that allows the player to temporarily 'covert' a zombie to it's side so I need to change out it's `TeamComponent` to be the players `TeamComponent`. When doing this I need to get all of the `TeamComponents` on the zombie and change them all to the players team.

    P.S. I'm using Hybrid ECS.

    Code (CSharp):
    1.            
    2. var zombie =  _zombieDiContainer.InstantiatePrefabForComponent<ZombieView>(_settings.ZombiePrefab); //This is basically just a DI way of instantiating a Prefab using Zenject
    3. var entityComponent = zombie.GetComponent<GameObjectEntity>();
    4. var rootEntity = entityComponent.Entity;
    5. var manager = entityComponent.EntityManager;
    6.  
    7. //Start of new code that locks up Unity
    8. var subEntities = zombie.GetComponentsInChildren<GameObjectEntity>().Select(x => x.Entity);
    9. foreach (var subEntity in subEntities)
    10. {
    11.     var attacherEntity = manager.CreateEntity();
    12. /* maybe it's crashing due to multiple Attach components pointing to the same Parent? */
    13.     manager.AddComponentData(attacherEntity, new Attach
    14.     {
    15.         Child = subEntity,
    16.             Parent = rootEntity
    17.     });
    18. }
    19. // End of the new code
    20.  
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I think your issue is GetComponentsInChildren as it also returns components on the same gameobject

    Returns all components of Type type in the GameObject or any of its children.


    So it's also trying to make zombie a child of itself.

    e.g. it ends up being

    Code (CSharp):
    1. new Attach {
    2.     Child = rootEntity,
    3.     Parent = rootEntity,
    4. }
    which I suspect unity does not like.

    You need to filter out the root.
     
    LukePammant likes this.
  3. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    That fixed it. Thanks @tertle !
     
  4. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    @tertle - while your suggestion fixed the issue I was having it revealed that my thinking is off. It looks like the child components get a Parent component. Do you know - is there any way to go from Parent to Children in ECS?
     
  5. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    No, not yet. I made a similar post months ago when the attached was introduced, but that system hasn't been touched for a while. Joachim has expressed that it would be nice to have an API layer method of traversing the hierarchy though.

    My guess: GDC will be their next big push. :\
     
    LukePammant likes this.
  6. LukePammant

    LukePammant

    Joined:
    Mar 10, 2015
    Posts:
    50
    I created a quick system that adds a
    DynamicBuffer
    to the Parent entity with all of it's children. Here is the code if anyone is interested. Let me know if there is a better way to do it. Perhaps a way using
    IJobChunk
    or something?

    Code (CSharp):
    1.  
    2. using Unity.Burst;
    3. using Unity.Collections;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6. using Unity.Transforms;
    7.  
    8. public struct Children : IBufferElementData
    9. {
    10.     public Entity Child;
    11.  
    12.     public static implicit operator Entity(Children child) => child.Child;
    13.     public static implicit operator Children(Entity entity) => new Children { Child = entity };
    14. }
    15.  
    16. [UpdateBefore(typeof(TransformSystem))]
    17. [BurstCompile]
    18. public class BuildChildArraySystem : JobComponentSystem
    19. {
    20.     private ComponentGroup _filter;
    21.  
    22.     protected override void OnCreateManager()
    23.     {
    24.         _filter = GetComponentGroup(new EntityArchetypeQuery()
    25.         {
    26.             All = new ComponentType[] { typeof(Attach) },
    27.             Any = new ComponentType[] { },
    28.             None = new ComponentType[] { },
    29.         });
    30.     }
    31.  
    32.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    33.     {
    34.         var filterChunks = _filter.CreateArchetypeChunkArray(Allocator.TempJob);
    35.         var attachType = GetArchetypeChunkComponentType<Attach>();
    36.         foreach (var chunk in filterChunks)
    37.         {
    38.             foreach (var attachComponent in chunk.GetNativeArray(attachType))
    39.             {
    40.                 if (!EntityManager.HasComponent<Children>(attachComponent.Parent)){
    41.                     EntityManager.AddBuffer<Children>(attachComponent.Parent);
    42.                 }
    43.  
    44.                 var buffer = EntityManager.GetBuffer<Children>(attachComponent.Parent);
    45.                 buffer.Add(attachComponent.Child);
    46.             }
    47.         }
    48.  
    49.         filterChunks.Dispose();
    50.         return inputDeps;
    51.     }
    52. }
    53.  
    54.  
     
    Last edited: Jan 30, 2019
  7. RecursiveEclipse

    RecursiveEclipse

    Joined:
    Sep 6, 2018
    Posts:
    298
    Just to add another solution, there is a parent to child multihashmap in TransformSystem, which can be retrieved with reflection. This is a system I use to destroy an entire hierarchy:

    Code (CSharp):
    1. [UpdateAfter(typeof(TransformSystem))]
    2. [UpdateBefore(typeof(EndFrameBarrier))]
    3. public class DestroyHierarchySystem : JobComponentSystem {
    4.     [Inject] EndFrameBarrier barrier;
    5.     bool isInitialized;
    6.     NativeMultiHashMap<Entity, Entity> parentToChildTree;
    7.  
    8.     void Init() {
    9.         //Using reflection to get the existing ParentToChildTree HashMap of TransformSystem.
    10.         var transformSystem = World.GetExistingManager<TransformSystem>();
    11.         var parentToChildInfo = typeof(TransformSystem).GetField(
    12.             "ParentToChildTree", BindingFlags.NonPublic | BindingFlags.Instance);
    13.         parentToChildTree = (NativeMultiHashMap<Entity, Entity>)parentToChildInfo.GetValue(transformSystem);
    14.         isInitialized = true;
    15.     }
    16.  
    17.     [BurstCompile]
    18.     struct DestroyHierarchyJob : IJobProcessComponentDataWithEntity<DestroyEntityTag> {
    19.         public EntityCommandBuffer.Concurrent CommandBuffer;
    20.         [ReadOnly] public NativeMultiHashMap<Entity, Entity> ParentToChildTree;
    21.         [ReadOnly] public ComponentDataFromEntity<DestroyEntityTag> DestroyEntityTags;
    22.      
    23.         public void Execute(Entity entity, int jobIndex, [ReadOnly] ref DestroyEntityTag tag) {
    24.             DestroyTreeIterative(jobIndex, entity);
    25.         }
    26.      
    27.         void DestroyTreeIterative(int jobIndex, Entity parent) {
    28.             CommandBuffer.DestroyEntity(jobIndex, parent);
    29.             if(!ParentToChildTree.TryGetFirstValue(parent, out Entity foundChild, out var iterator)) {
    30.                 return;
    31.             }
    32.             do {
    33.                 DestroyTreeIterative(jobIndex, foundChild);
    34.                 //Check that child does not already have a tag so it is not deleted twice.
    35.                 if(!DestroyEntityTags.Exists(foundChild)) {
    36.                     CommandBuffer.DestroyEntity(jobIndex, foundChild);
    37.                 }
    38.             } while(ParentToChildTree.TryGetNextValue(out foundChild, ref iterator));
    39.         }
    40.     }
    41.  
    42.     protected override JobHandle OnUpdate(JobHandle inputDeps) {
    43.         if(!isInitialized) {
    44.             Init();
    45.         }
    46.         return new DestroyHierarchyJob {
    47.             CommandBuffer = barrier.CreateCommandBuffer().ToConcurrent(),
    48.             ParentToChildTree = parentToChildTree,
    49.             DestroyEntityTags = GetComponentDataFromEntity<DestroyEntityTag>(isReadOnly:true)
    50.         }.Schedule(this, inputDeps);
    51.     }
    52. }
    I add a DestroyEntityTag at the level I want and destroy the relevant entities, TransformSystem will clean up the hashmap the next time it runs.
     
    Last edited: Jan 30, 2019
    LukePammant likes this.