Is that possible in any way? Something like:
IComponentData unknownComponent = GetUnknownComponent();
ecb.AddComponent(entity, unknownComponent);
If not, is there any alternative?
Is that possible in any way? Something like:
IComponentData unknownComponent = GetUnknownComponent();
ecb.AddComponent(entity, unknownComponent);
If not, is there any alternative?
I do not think it is possible.
What is your use case? Why you need something like that?
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ā¦
Why not just generate authoring versions of your components and add them to those units then let conversion add them?
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.
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.
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.
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?
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.
I see⦠makes sense, thank you.
Here you go:
using System.Reflection;
using Unity.Entities;
using UnityEngine.Assertions;
public static class ComponentDataUtility
{
private static MethodInfo addComponentData;
static ComponentDataUtility()
{
addComponentData = typeof(ComponentDataUtility).GetMethod("AddComponentData", BindingFlags.Public | BindingFlags.Static);
}
public static void AddComponentData<T>(EntityManager entityManager, Entity entity, T data = default) where T : struct, IComponentData
{
entityManager.AddComponentData(entity, data);
}
/// <summary>
/// Circumvents the necessity to have a known IComponent Type at compile time.
/// </summary>
public static void AddComponentDataWithReflection(EntityManager entityManager, Entity entity, object data)
{
Assert.IsNotNull(data);
MethodInfo function = addComponentData.MakeGenericMethod(data.GetType());
function.Invoke(null, new object[] { entityManager, entity, data });
}
}
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:
public static void AddComponent<T>(Entity entity, EntityCommandBuffer ecb, T data) where T : struct, IComponentData
{
ecb.AddComponent(entity, data);
}
dynamic d = myComponentData;
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
Iāll try the dynamic type solution for my porject thanks, it would avoid boilerplate code that make the code uggly ![]()
Apparently you donāt even have to make a different method for it⦠just do ecb.AddComponent(unitToAddTo, (dynamic)data);
Thanks Iāll try it and try to get some perf mesurement when Iāll get the time.
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?
It seem dynamic is not able to cross assemblies :
WorldTime is in Entities but TimeData is in Core.
After re reading, itās more likely the issue is the fact that the WorldTime is internal to Entities wich is a diferrent assembly than the one you are using the dynamic in.
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.