Search Unity

  1. Unity 2020 LTS & Unity 2021.1 have been released.
    Dismiss Notice

Copying arbitrary component data between 2 entities

Discussion in 'Data Oriented Technology Stack' started by MartijnGG, Apr 19, 2020.

  1. MartijnGG

    MartijnGG

    Joined:
    May 3, 2018
    Posts:
    73
    Is it possible to copy a set of components from 1 Entity to another?

    I'm attempting to "spawn" a converted prefab ONTO an already existing entity, instead of creating a new entity out of it.

    Currently I can use `EntityManager.Instantiate(prefabSourceEntity)` to spawn a new entity, but I'd instead like to apply the prefab to an existing entity.
    My idea for this so far has been to do the Instantiate as normal (so that the LinkedEntityGroup entities also get spawned), merge the component types from both the prefab and target entity, merge them into a new archetype, and set this as the archetype for the target entity.
    The copy over the component data from the instantiated prefab, onto the target entity.

    Sadly this last bit seems to be harder then I thought.

    Does anyone have any ideas on how to solve this problem?
     
  2. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    2,172
    It's a pain. :mad:

    Unity really needs
    EntityManager.CopyComponent(Entity dst, Entity src, ComponentType type)


    In the meantime, you can try something like this. Although I wrote this before I knew about the asmref trick so that might be a cleaner solution. Also not all of this code has been vetted. But I know ICDs were working as of Entities 0.8.0 and I did not see any changes in 0.9.0 that would suggest it to still be broken.

    Code (CSharp):
    1. using System;
    2. using System.Collections.Generic;
    3. using System.Reflection;
    4. using Unity.Collections.LowLevel.Unsafe;
    5. using Unity.Entities;
    6.  
    7. namespace Latios
    8. {
    9.     /// <summary>
    10.     /// A toolkit for copying components based on ComponentType.
    11.     /// </summary>
    12.     public unsafe class EntityDataCopyKit
    13.     {
    14.         private EntityManager m_em;
    15.  
    16.         private delegate void EmSetSharedComponentDataRaw(Entity entity, int typeIndex, object componentData);
    17.         private delegate void EmSetComponentDataRaw(Entity entity, int typeIndex, void* data, int size);
    18.         private delegate void* EmGetComponentDataRawRO(Entity entity, int typeIndex);
    19.         private delegate void* EmGetComponentDataRawRW(Entity entity, int typeIndex);
    20.         private delegate object EmGetSharedComponentData(Entity entity, int typeIndex);
    21.         private delegate void* EmGetBufferRawRW(Entity entity, int typeIndex);
    22.         private delegate void* EmGetBufferRawRO(Entity entity, int typeIndex);
    23.         private delegate int EmGetBufferLength(Entity entity, int typeIndex);
    24.  
    25.         private EmSetSharedComponentDataRaw emSetSharedComponentDataRaw;
    26.         private EmSetComponentDataRaw       emSetComponentDataRaw;
    27.         private EmGetComponentDataRawRO     emGetComponentDataRawRO;
    28.         private EmGetComponentDataRawRW     emGetComponentDataRawRW;
    29.         private EmGetSharedComponentData    emGetSharedComponentData;
    30.         private EmGetBufferRawRW            emGetBufferRawRW;
    31.         private EmGetBufferRawRO            emGetBufferRawRO;
    32.         private EmGetBufferLength           emGetBufferLength;
    33.  
    34.         private Dictionary<Type, Type> m_typeTagsToTypesCache = new Dictionary<Type, Type>();
    35.  
    36.         public EntityDataCopyKit(EntityManager entityManager)
    37.         {
    38.             m_em = entityManager;
    39.  
    40.             var emType                  = typeof(EntityManager);
    41.             var methodInfo              = GetMethod("SetSharedComponentDataBoxedDefaultMustBeNull", 3);
    42.             emSetSharedComponentDataRaw = methodInfo.CreateDelegate(typeof(EmSetSharedComponentDataRaw), m_em) as EmSetSharedComponentDataRaw;
    43.             methodInfo                  = emType.GetMethod("SetComponentDataRaw", BindingFlags.Instance | BindingFlags.NonPublic);
    44.             emSetComponentDataRaw       = methodInfo.CreateDelegate(typeof(EmSetComponentDataRaw), m_em) as EmSetComponentDataRaw;
    45.             methodInfo                  = emType.GetMethod("GetComponentDataRawRO", BindingFlags.Instance | BindingFlags.NonPublic);
    46.             emGetComponentDataRawRO     = methodInfo.CreateDelegate(typeof(EmGetComponentDataRawRO), m_em) as EmGetComponentDataRawRO;
    47.             methodInfo                  = emType.GetMethod("GetComponentDataRawRW", BindingFlags.Instance | BindingFlags.NonPublic);
    48.             emGetComponentDataRawRW     = methodInfo.CreateDelegate(typeof(EmGetComponentDataRawRW), m_em) as EmGetComponentDataRawRW;
    49.             methodInfo                  = GetMethod("GetSharedComponentData", 2);
    50.             emGetSharedComponentData    = methodInfo.CreateDelegate(typeof(EmGetSharedComponentData), m_em) as EmGetSharedComponentData;
    51.             methodInfo                  = emType.GetMethod("GetBufferRawRW", BindingFlags.Instance | BindingFlags.NonPublic);
    52.             emGetBufferRawRW            = methodInfo.CreateDelegate(typeof(EmGetBufferRawRW), m_em) as EmGetBufferRawRW;
    53.             methodInfo                  = emType.GetMethod("GetBufferRawRO", BindingFlags.Instance | BindingFlags.NonPublic);
    54.             emGetBufferRawRO            = methodInfo.CreateDelegate(typeof(EmGetBufferRawRO), m_em) as EmGetBufferRawRO;
    55.             methodInfo                  = emType.GetMethod("GetBufferLength", BindingFlags.Instance | BindingFlags.NonPublic);
    56.             emGetBufferLength           = methodInfo.CreateDelegate(typeof(EmGetBufferLength), m_em) as EmGetBufferLength;
    57.         }
    58.  
    59.         /// <summary>
    60.         /// Copies the data stored in the componentType from the src entity to the dst entity.>
    61.         /// </summary>
    62.         /// <param name="src">The source entity</param>
    63.         /// <param name="dst">The destination entity</param>
    64.         /// <param name="componentType">The type of data to be copied</param>
    65.         /// <param name="copyCollections">Should collection components be shallow copied (true) or ignored (false)?</param>
    66.         public void CopyData(Entity src, Entity dst, ComponentType componentType, bool copyCollections = false)
    67.         {
    68.             //Check to ensure dst has componentType
    69.             if (!m_em.HasComponent(dst, componentType))
    70.                 m_em.AddComponent(dst, componentType);
    71.             if (componentType.IsSharedComponent)
    72.                 CopyScd(src, dst, componentType);
    73.             else if (componentType.IsBuffer)
    74.                 CopyBuffer(src, dst, componentType);
    75.             else
    76.             {
    77.                 if (copyCollections)
    78.                 {
    79.                     var type = componentType.GetManagedType();
    80.                     if (type.IsConstructedGenericType)
    81.                     {
    82.                         var genType = type.GetGenericTypeDefinition();
    83.                         if (genType == typeof(ManagedComponentSystemStateTag<>))
    84.                         {
    85.                             if (!m_typeTagsToTypesCache.TryGetValue(type, out Type managedType))
    86.                             {
    87.                                 managedType                  = type.GenericTypeArguments[0];
    88.                                 m_typeTagsToTypesCache[type] = managedType;
    89.                             }
    90.                             LatiosWorld lw = m_em.World as LatiosWorld;
    91.                             lw.ManagedStructStorage.CopyComponent(src, dst, managedType);
    92.                         }
    93.                         else if (genType == typeof(CollectionComponentSystemStateTag<>))
    94.                         {
    95.                             if (!m_typeTagsToTypesCache.TryGetValue(type, out Type managedType))
    96.                             {
    97.                                 managedType                  = type.GenericTypeArguments[0];
    98.                                 m_typeTagsToTypesCache[type] = managedType;
    99.                             }
    100.                             LatiosWorld lw = m_em.World as LatiosWorld;
    101.                             lw.CollectionComponentStorage.CopyComponent(src, dst, managedType);
    102.                         }
    103.                     }
    104.                 }
    105.                 CopyIcd(src, dst, componentType);
    106.             }
    107.         }
    108.  
    109.         private void CopyIcd(Entity src, Entity dst, ComponentType componentType)
    110.         {
    111.             if (componentType.IsZeroSized)
    112.                 return;
    113.  
    114.             var typeInfo = TypeManager.GetTypeInfo(componentType.TypeIndex);
    115.             var size     = typeInfo.SizeInChunk;
    116.             var data     = emGetComponentDataRawRO(src, componentType.TypeIndex);
    117.             emSetComponentDataRaw(dst, componentType.TypeIndex, data, size);
    118.         }
    119.  
    120.         private void CopyScd(Entity src, Entity dst, ComponentType componentType)
    121.         {
    122.             var data = emGetSharedComponentData(src, componentType.TypeIndex);
    123.             emSetSharedComponentDataRaw(dst, componentType.TypeIndex, data);
    124.         }
    125.  
    126.         private void CopyBuffer(Entity src, Entity dst, ComponentType componentType)
    127.         {
    128.             int length      = emGetBufferLength(src, componentType.TypeIndex);
    129.             var typeInfo    = TypeManager.GetTypeInfo(componentType.TypeIndex);
    130.             var elementSize = typeInfo.ElementSize;
    131.             var alignment   = typeInfo.AlignmentInBytes;
    132.  
    133.             FakeBufferHeader* dstHeader = (FakeBufferHeader*)emGetComponentDataRawRW(dst, componentType.TypeIndex);
    134.             FakeBufferHeader.EnsureCapacity(dstHeader, length, elementSize, alignment, FakeBufferHeader.TrashMode.RetainOldData, false, 0);
    135.             var dstBufferPtr = emGetBufferRawRW(dst, componentType.TypeIndex);
    136.             var srcBufferPtr = emGetBufferRawRO(src, componentType.TypeIndex);
    137.             UnsafeUtility.MemCpy(dstBufferPtr, srcBufferPtr, elementSize * length);
    138.         }
    139.  
    140.         private MethodInfo GetMethod(string methodName, int numOfArgs)
    141.         {
    142.             var methods = typeof(EntityManager).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);
    143.             foreach (var method in methods)
    144.             {
    145.                 if (method.Name == methodName && method.GetParameters().Length == numOfArgs)
    146.                     return method;
    147.             }
    148.             return null;
    149.         }
    150.     }
    151. }
    152.  
    153.  
     
    MartijnGG, RBogdy and eizenhorn like this.
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,211
    Yeah that pain, we also has similar bicycle. In addition they definitely need SwapEntities, when entities swapping all data between each other
     
    DreamingImLatios likes this.
  4. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    2,172
    EntityManager.SwapComponents almost does this combined with EntityManager.GetChunk. But there is no EntityManager.GetIndexInChunk. :(
     
  5. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,211
    Yep, it require iterate chunk for finging index (or many reflection, because internaly they have
    GetEntityInChunk(Entity) which return EntityInChunk which have IndexInChunk.
     
    DreamingImLatios likes this.
unityunity