I’m am trying to dynamically create an entity with IComponentData components based on data deserialised from JSON.
This results in the following code:
EntityManager.AddComponentData(entity, componentData);```
The return type of compDef.CreateComponentData is IComponentData and this causes the following error:
```The type 'Unity.Entities.IComponentData' must be a non-nullable value type in order to use it as parameter 'T'```
Is there a way of working around this and adding components without knowing which type they are?
The types returned from that method are all structs and are all using non-blittable types. The error is a compile-time error as it can’t determine if the return type is non-nullable. I’ve tried adding a type parameter with the “where T : struct, IComponentData” but then the consumer needs to pass in the type which it doesn’t know.
Surely there is some way of making this work? It seems like a fairly major missing feature to not be able to populate an entity dynamically.
@SubPixelPerfect I think you should make a separate thread for that.
That’s my point, I cannot know what the type is at compile time. I’m dynamically generating the components to be added to an entity.
var componentData = compDef.CreateComponentData<Cell>();
EntityManager.SetComponentData(entity, componentData);
I’m enumerating through a collection of “CompDef” instances each of which return a different IComponentData for example Cell or Buildable etc. This means I cannot possibly know what those types are going to be at compile time.
Do you have an example of what you mean by that? If I need to manually cast to a type then that’s not going to result in particularly clean code considering there will be a large number of component types to handle.
You need to read the type of the current json object e.g. “Cell”
If you have this information you can do the following:
string myClassString = // some call from your json lib to retreive the current class
switch(myClassString ){
case "Cell":
Cell cellData = compDef.CreateComponentData<Cell>();
EntityManager.SetComponentData(entity, cellData);
break;
//other cases ...
default:
throw new NotImplementedException();
}
That’s what I thought you may have meant, having a mammoth switch case statement seems like an absolute last resort since there would end up being a huge number of cases. I was hoping for an alternative to hard-coding all of the different component data types.
EDIT:
I’ve another idee… Maybe this apporach is better…
Take use of an Interface
interface IJsonbase {
void AssignToEntity(EntityManager manager, Entity entity);
}
struct Cell : IJsonbase, IComponentData {
public int X;
public int Y;
public override void AssignToEntity(EntityManager manager, Entity entity) {
manager.SetComponentData(entity, this);
}
}
//Now you can do something like this
IJsonbase componentData = compDef.CreateComponentData();
componentData.AssignToEntity(EntityManager, entity);
you can use interface (it will box but you are already boxing). still requires each concrete type to implement the method.
or you can use reflection to generate the ‘SetComponentData’ method at runtime (it may be worse)
I think EntityManager has a ‘SetComponentBoxed(Entity, object)’ or similar… I saw some post in this forum mentioning it
it was my tread mentioning SetComponentBoxed method
i’m trying to do exactly opposite thing to what you looking for - i’m trying to serialize entity with all its components and values
and there is a very similar problem with generics
for now I ended up with generation generic GetComponentData methods using reflection
public void SerializeEntiy(Entity e){
var types = this.EntityManager.GetComponentTypes( e );
for( int j = 0; j != types.Length; j++ ){
ComponentType ct = types[j];
Type t = ct.GetManagedType();
if( t.GetInterfaces().Contains( typeof( IComponentData ) ) ){
MethodInfo methodInfo = typeof(EntityManager).GetMethod("GetComponentData");
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(t);
var parameters = new object[]{e};
object componentData = genericMethodInfo.Invoke( EntityManager, parameters );
Debug.Log(t +" : "+ JsonUtility.ToJson( componentData ) );
}
}
types.Dispose();
}
very ugly solution but works, can’t find a better way so far
the interface approach is what @Spy-Shifty_1 did in their post. (it was edited)
the reflection approach is almost exactly as @SubPixelPerfect has
(1. I thought GetMethod(“name”) didn’t work with generic methods, I usually had to do GetMethods().First(m => m.MethodName == “name”). maybe it’s something that got fixed with the new runtime.
2. I’d cache heavily though, reflection is heavy)