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

Adding unknown components to entities

Discussion in 'Entity Component System' started by MhmdAL1111, Jun 26, 2020.

  1. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    Is that possible in any way? Something like:
    Code (CSharp):
    1. IComponentData unknownComponent = GetUnknownComponent();
    2. ecb.AddComponent(entity, unknownComponent);
    If not, is there any alternative?
     
  2. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    436
    I do not think it is possible.
    What is your use case? Why you need something like that?
     
  3. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    I have a level which I create in the editor and save in a file, and in that level there are "units" and each unit will have unknown "extra" components or modifications to existing components (existing on the unit prefab). For now there are about 7 possible extra components but I will probably add more.. currently manually figuring out the concrete type using ifs and adding that instead..
     
  4. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    943
    Why not just generate authoring versions of your components and add them to those units then let conversion add them?
     
  5. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    The level creation in the editor is just unit "dummies". I'm taking those dummies and getting the necessary data, storing it in file, so I could later create the real units using that data. I am linking an enum UnitType stored in the file to the appropriate prefab after conversion. So I would instantiate that prefab and add any extra IComponentDatas to the instantiated entity.
     
  6. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    943
    I see. What you could also do is create a dictionary of EntityArchetype for each of your unit type. You then lookup that dictionary for the archetype every time you create a unit. This, however, will not included unique data for each component. It just allows you to create entities without knowing the actual components that it needs.
     
  7. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    I use this method in my skill/ability system and proposed it to some in the forum which have the same issue.

    It breaks abit the component are only data principle of DOTS but I'm fine with it.

    You can create an interface that require the implementation of a Convert method which take the entity, and entity manager as parameter.

    Make your IComponentData implement that interface and call it in your couversion system.
    Basically each component registers itself and htere is no issue bacause each know what they are.
     
    MhmdAL1111 and Micz84 like this.
  8. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    Interesting, that does work.. Thank you. I did not think of letting the components add themselves to the entity.. however all the methods implemented of the custom interface in the components look exactly the same ( ecb.AddComponent(entity, this) ) I wonder if there is a reason we can't just directly add IComponentData?
     
  9. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    943
    This is so that your components will remain as value types. Casting a struct to IComponentData will box it into a reference type. That's just how C# works.
     
    MhmdAL1111 likes this.
  10. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    I see.. makes sense, thank you.
     
  11. shiroto

    shiroto

    Joined:
    Nov 14, 2012
    Posts:
    42
    Here you go:
    Code (CSharp):
    1. using System.Reflection;
    2. using Unity.Entities;
    3. using UnityEngine.Assertions;
    4.  
    5. public static class ComponentDataUtility
    6. {
    7.     private static MethodInfo addComponentData;
    8.  
    9.     static ComponentDataUtility()
    10.     {
    11.         addComponentData = typeof(ComponentDataUtility).GetMethod("AddComponentData", BindingFlags.Public | BindingFlags.Static);
    12.     }
    13.  
    14.     public static void AddComponentData<T>(EntityManager entityManager, Entity entity, T data = default) where T : struct, IComponentData
    15.     {
    16.         entityManager.AddComponentData(entity, data);
    17.     }
    18.  
    19.     /// <summary>
    20.     /// Circumvents the necessity to have a known IComponent Type at compile time.
    21.     /// </summary>
    22.     public static void AddComponentDataWithReflection(EntityManager entityManager, Entity entity, object data)
    23.     {
    24.         Assert.IsNotNull(data);
    25.         MethodInfo function = addComponentData.MakeGenericMethod(data.GetType());
    26.         function.Invoke(null, new object[] { entityManager, entity, data });
    27.     }
    28. }
     
    BackgroundMover and MhmdAL1111 like this.
  12. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    I appreciate it, I had a feeling there was a way through reflection.. For what it's worth, your solution got me interested and I found another solution that also worked, using dynamic-type objects:
    Code (CSharp):
    1. public static void AddComponent<T>(Entity entity, EntityCommandBuffer ecb, T data) where T : struct, IComponentData
    2. {
    3.     ecb.AddComponent(entity, data);
    4. }
    5.  
    6. dynamic d = myComponentData;
    7. AddComponent(unitToAddTo, ecb, d);
    https://stackoverflow.com/questions/232535/how-do-i-use-reflection-to-call-a-generic-method

    Either way it seems it is a bad idea performance-wise to store components as IComponentData in the first place but for now I have no other choice so this is a good solution. Thank you
     
    WAYNGames likes this.
  13. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    I'll try the dynamic type solution for my porject thanks, it would avoid boilerplate code that make the code uggly :)
     
  14. MhmdAL1111

    MhmdAL1111

    Joined:
    Oct 25, 2017
    Posts:
    30
    Apparently you don't even have to make a different method for it... just do
    Code (CSharp):
    1. ecb.AddComponent(unitToAddTo, (dynamic)data);
     
    shiroto and WAYNGames like this.
  15. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    Thanks I'll try it and try to get some perf mesurement when I'll get the time.
     
    MhmdAL1111 likes this.
  16. shiroto

    shiroto

    Joined:
    Nov 14, 2012
    Posts:
    42
    I did a quick test and it seems using dynamic is indeed faster. The average time for executing the dynamic version was 0.000661s, while the reflection version was 0.000931s. In absolute terms not a huge difference, but still :)

    EDIT: One caveat though, the dynamic option doesn't seem to work with every component type. When I put in a Unity.Entities.WorldTime, I get the following exception: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: The type 'System.ValueType' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method
    Its probably because there is another struct inside the component type?
     
    Last edited: Jun 26, 2020
  17. WAYNGames

    WAYNGames

    Joined:
    Mar 16, 2019
    Posts:
    939
    shiroto likes this.
  18. FuzzyShan

    FuzzyShan

    Joined:
    Jul 30, 2012
    Posts:
    182
    I think another way is to just auto-generate glue code for every marked attribute of the IComponent struct, thus you can assign values before attaching them to the entity component.