Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Pure ecs, how to copy all components from one entity to another

Discussion in 'Entity Component System' started by kstothert, Mar 3, 2019.

  1. kstothert

    kstothert

    Joined:
    Dec 8, 2016
    Posts:
    68
    say I have a system that checks if two entities are within a certain distance from eachother then I want to copy all the components from the source entity (example, a powerup with multiple components on it) to the receiver (the player).
     
    lclemens, Zaax and learc83 like this.
  2. zanius

    zanius

    Joined:
    Feb 5, 2018
    Posts:
    24
    If anyone can find a good solution to this I'd really love an answer too. I have entities that other entities can interact with and add AI to them. For example, interacting with a door would add a ShortMove component and an Animation component and then those systems would make the interacting entity move through the door with an animation. I can't really find a great way to do this though. I thought about using AddComponentObject and adding monobehaviour proxies that know how to do it but I don't think this is very performant and it seems hard to serialize.
     
    lclemens and kstothert like this.
  3. kstothert

    kstothert

    Joined:
    Dec 8, 2016
    Posts:
    68
    The way I solved it for now was just to leave those components on the other entity but add a link to that entity. It requires me to do lookups in the systems but it works for now.

    Code (CSharp):
    1.  
    2.   public struct LinkToSteeringPipeline : IComponentData {
    3.     public Entity Entity;
    4.   }
    5.  
    6.  
     
  4. gadgetfan

    gadgetfan

    Joined:
    Oct 1, 2018
    Posts:
    10
    I used reflection in this case. Just call GetComponentData and SetComponentData. Works for IComponentData components:
    Code (CSharp):
    1.  
    2. MethodInfo gcdMethodInfo = typeof(EntityManager).GetMethod("GetComponentData");
    3. MethodInfo gcdGenericMethodInfo = gcdMethodInfo.MakeGenericMethod(componentType);
    4. var gcdParameters = new object[] {srcEntity};
    5. object componentData = gcdGenericMethodInfo.Invoke(entityManager, gcdParameters);
    6. MethodInfo scdMethodInfo = typeof(EntityManager).GetMethod("SetComponentData");
    7. MethodInfo scdGenericMethodInfo = scdMethodInfo.MakeGenericMethod(componentType);
    8. var scdParameters = new[] {dstEntity, componentData};
    9. scdGenericMethodInfo.Invoke(entityManager, scdParameters);
    10.  
    Possible in the future there will be some API.
     
  5. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,580
    Uh, looks ugly :) and is not pure ECS nor can be put into burst.

    I am sure I have seen somewhere how to copy entities, but can not find it atm. However most trivial way is, to create entities from archetype on main thread, then in job get all of each component/buffer data from original entity and set in to new entities. Now you can put in parallel and burst if you like.
     
  6. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    I tried poking around the api but couldn't come up with a good "generic" solution to iterate over all components and set them without using reflection. I think the best you can do for now is to just manually copy each component you want. You can at least make it easier on yourself but it will only work on the main thread:
    Code (CSharp):
    1.         void CopyComponent<COMP>(EntityManager em, Entity a, Entity b)
    2.             where COMP : struct, IComponentData
    3.         {
    4.             var c = em.GetComponentData<COMP>(a);
    5.             em.SetComponentData(b, c);
    6.         }
    Then you can do:
    Code (CSharp):
    1. CopyComponent<Translation>(em,a,b);
    2. CopyComponent<Rotation>(em,a,b);
    Another option is to use Instantiate to create a new entity with an exact copy of all the original entity's components. You can then add a reference to the target entity in the new copy and work with that target entity in your systems. It's a pretty terrible workaround but it would save you from having to manually copy each component.
     
  7. Kuptsevych-Yuriy

    Kuptsevych-Yuriy

    Joined:
    Oct 1, 2008
    Posts:
    20
    Hi all, I want to copy components with data from one entity to another, but stuck. The problem is that I can't get ComponentData through ComponentType.

    A little code, for context.

    Code (CSharp):
    1.  HashSet<ComponentType> _effects = new HashSet<ComponentType>();
    2.  
    3.         _effects.Add(typeof(TestEffect));
    4.        
    5.         NativeArray<ComponentType> componentTypes = _entityManager.GetComponentTypes(_entity1);
    6.  
    7.         foreach (var componentType in componentTypes)
    8.         {
    9.  
    10.             if (_effects.Contains(componentType))
    11.             {
    12.                 _entityManager.AddComponent(_entity2, componentType);
    13.                
    14.                 //set component data from entity1 ???
    15.             }
    16.         }
     
  8. gadgetfan

    gadgetfan

    Joined:
    Oct 1, 2018
    Posts:
    10
    I used finally method of Sarkahn (see above). But it should be called with reflection, as I described in my post.
     
  9. Kuptsevych-Yuriy

    Kuptsevych-Yuriy

    Joined:
    Oct 1, 2008
    Posts:
    20
    Thanks. Sure I can use reflection or code generation. But isn't there a way out of the box for such a simple operation ?
     
    e199 likes this.
  10. ndesy

    ndesy

    Joined:
    Jul 22, 2019
    Posts:
    20
    You should probably rethink your code to know component type at compile time. It is a bit more more work but you won't probably achieve great performance otherwise.
     
  11. Kuptsevych-Yuriy

    Kuptsevych-Yuriy

    Joined:
    Oct 1, 2008
    Posts:
    20
    A little clarification. I want to make a trigger-detector system. There are components on the entity trigger which describe the effects when hit by a detector. When detector hits the trigger, effect components from trigger are copied to detector entity. Then desired systems activate.

    Anyway, at this time, I chose this option. (The dictionary can be filled through code generation.)


    Code (CSharp):
    1. private Dictionary<ComponentType, Action<EntityManager, Entity, Entity>> _conversion =
    2.         new Dictionary<ComponentType, Action<EntityManager, Entity, Entity>>();
    3.    
    4.    
    5. _conversion.Add(typeof(MyEffectComponent), CopyComponent<MyEffectComponent>);
    6.  
    7.  
    8. if (_conversion.TryGetValue(componentType, out var value)) value.Invoke(_entityManager, _entity1, _entity2);
    9.  
    10.  
    11. private void CopyComponent<T>(EntityManager em, Entity a, Entity b) where T : struct, IComponentData
    12. {
    13.    if (!em.HasComponent<T>(b)) em.AddComponent<T>(b);
    14.  
    15.    var c = em.GetComponentData<T>(a);
    16.  
    17.    em.SetComponentData(b, c);
    18. }
     
    Last edited: Oct 23, 2019
    Rekart likes this.
  12. GamerLordMat

    GamerLordMat

    Joined:
    Oct 10, 2019
    Posts:
    177
    I think the new easiest way of doing this is just to use the instantiate function:

    Code (CSharp):
    1. Entity copy = EntityManager.Instantiate(srcEntity);
    https://docs.unity3d.com/Packages/com.unity.entities@0.3/manual/ecs_entities.html
     
  13. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,695
    eatbuckshot likes this.
  14. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    This will create a new entity identical to the source entity, it will not merge them which is what I understand is the requirement from the OP.
     
    lclemens likes this.
  15. GamerLordMat

    GamerLordMat

    Joined:
    Oct 10, 2019
    Posts:
    177
    Ok thanks for the correction! I was searching for this answer I provided, I hope it helps someone anyway.
     
    Spemble and jmcusack like this.
  16. Rekart

    Rekart

    Joined:
    Jan 13, 2020
    Posts:
    22
    Take a look at "polymorphic components"
    https://forum.unity.com/threads/sources-available-dots-polymorphic-components.1132123/

    You could also probably use DynamicBuffers instead of ComponentData and call
    Code (CSharp):
    1. public DynamicBuffer<U> Reinterpret<U>() where U : struct
    to achieve some sort of polymorphic behaviour
     
    Kuptsevych-Yuriy likes this.