I want to create many types of entities from a game object list, but I found there is no way to create a List in IComponentData, how can I handle creating a list of entities from a game object list?
public class MapRespawnAuthoring : MonoBehaviour
{
public List<GameObject> MapPrefabList;
}
Is there a way to create an entity list like this?
public struct MapEntities : IComponentData
{
public List<Entity> mapChunk1;//can't do this in IComponentData
}
But there are some restrictions to store Entities this way instead of using a DynamicBuffer. I am only aware of Netcode for Entities where they not get remapped between Server and Client on ghost entities. But there are maybe more restrictions.
Hi Arnold:
Thanks for your suggestions, I tried to use:
public struct MapEntities : IComponentData
{
public NativeArray<Entity> mapChunks;
}
The error is:
ArgumentException: Blittable component type âMapEntitiesâ on GameObject âMapManagerâ contains a (potentially nested) pointer field. Serializing bare pointers will likely lead to runtime errors. Remove this field and consider serializing the data it points to another way such as by using a BlobAssetReference or a [Serializable] ISharedComponent. If for whatever reason the pointer field should in fact be serialized, add the [ChunkSerializable] attribute to your type to bypass this error.
Types from System.Collection namespace cannot be used within unmanaged components however they can be used in managed components. In the docs it is stated:
A managed component uses âclassâ instead of âstructâ in its declaration. But as stated in the docs that managed components cannot be used in C# Job System and are not Burst compatible, also their usage is generally less performant because not part of the ECS archetype chunks and subject to Garbage Collection. It is recommended using unmanaged components as much as possible. Still managed components have their use cases, especially when using ECS Entities and GameObjects/MonoBehaviors together. But in your use case it sounds like being possible to achieve using DynamicBuffer or FixedListXyzBytes.
Thanks let me know the difference between managed and unmanaged data types. I tried
Hi FaithlessOne:
Thanks let me know the reference type and value type, I tried
public struct MapEntities : IComponentData
{
public DynamicBuffer<Entity> mapDynamicBuffer;//NullReferenceException: Object reference not set to an instance of an object when creating entity
public FixedList512Bytes<Entity> mapFixedList512Bytes; //no error, but no entity created
}
I bake them like this in Baker:
mapEntities.mapDynamicBuffer = new DynamicBuffer<Entity>();
mapEntities.mapDynamicBuffer.Add(GetEntity(authoring.MapPrefabList[0], TransformUsageFlags.Dynamic));
mapEntities.mapFixedList512Bytes = new FixedList512Bytes<Entity>();
mapEntities.mapFixedList512Bytes.Add(GetEntity(authoring.MapPrefabList[0], TransformUsageFlags.Dynamic));
I initiate them like this ISystem:
var instance = state.EntityManager.Instantiate(mapEntities.mapDynamicBuffer[0]);
var instance = state.EntityManager.Instantiate(mapEntities.mapFixedList512Bytes[0]);
Thanks for these articles, I watched all videos from them, but they only provided the example of Baking entity like:
public struct MapEntities : IComponentData
{
public Entity mapChunk1;
}
If I want to bake a list of entities, I have to write like this:
public struct MapEntities : IComponentData
{
public Entity mapChunk1;
public Entity mapChunk2;
public Entity mapChunk3;
}
I am not sure if there has a smart way to bake all entities into a list, I tried:
public struct MapEntities : IComponentData
{
public DynamicBuffer<Entity> mapDynamicBuffer;//Null reference error
public FixedList512Bytes<Entity> mapFixedList512Bytes; //can't see entity but no error
public NativeArray<Entity> nativeArray; //compile error
}
I bake them like this in Baker:
public class MapRespawnAuthoring : MonoBehaviour
{
[SerializeField] List<GameObject> MapPrefabList;
class Baker : Baker<MapRespawnAuthoring>
{
public override void Bake(MapRespawnAuthoring authoring)
{
MapEntities mapEntities = new MapEntities();
mapEntities.mapDynamicBuffer = new DynamicBuffer<Entity>();
mapEntities.mapDynamicBuffer.Add(GetEntity(authoring.MapPrefabList[0], TransformUsageFlags.Dynamic));
mapEntities.mapDynamicBuffer.Add(GetEntity(authoring.MapPrefabList[1], TransformUsageFlags.Dynamic));
mapEntities.mapDynamicBuffer.Add(GetEntity(authoring.MapPrefabList[2], TransformUsageFlags.Dynamic));
mapEntities.mapFixedList512Bytes = new FixedList512Bytes<Entity>();
mapEntities.mapFixedList512Bytes.Add(GetEntity(authoring.MapPrefabList[0], TransformUsageFlags.Dynamic));
mapEntities.mapFixedList512Bytes.Add(GetEntity(authoring.MapPrefabList[1], TransformUsageFlags.Dynamic));
mapEntities.mapFixedList512Bytes.Add(GetEntity(authoring.MapPrefabList[2], TransformUsageFlags.Dynamic));
mapEntities.nativeArray = new NativeArray<Entity>(3, Allocator.Persistent);
mapEntities.nativeArray[0] = GetEntity(authoring.MapPrefabList[0], TransformUsageFlags.Dynamic);
mapEntities.nativeArray[1] = GetEntity(authoring.MapPrefabList[1], TransformUsageFlags.Dynamic);
mapEntities.nativeArray[2] = GetEntity(authoring.MapPrefabList[2], TransformUsageFlags.Dynamic);
AddComponent(GetEntity(TransformUsageFlags.None), mapEntities);
}
}
}
Initial them like this in ISystem:
public partial struct MapRespawnSystem : ISystem
{
[BurstCompile]
public void OnCreate(ref SystemState state)
{
state.RequireForUpdate<MapEntities>();
}
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
//**run once when code start
state.Enabled = false;
var mapEntities = SystemAPI.GetSingleton<MapEntities>();
state.EntityManager.Instantiate(mapEntities.nativeArray[0]);
state.EntityManager.Instantiate(mapEntities.nativeArray[1]);
state.EntityManager.Instantiate(mapEntities.nativeArray[2]);
state.EntityManager.Instantiate(mapEntities.mapDynamicBuffer[0]);
state.EntityManager.Instantiate(mapEntities.mapDynamicBuffer[1]);
state.EntityManager.Instantiate(mapEntities.mapDynamicBuffer[2]);
}
}
A dynamicbuffer is a type of component, but it acts as a buffer/list of elements. Itâs use was odd to me when I was starting out but here is a general implementation of what I think you are after.
public class MapAuthoring : MonoBehaviour
{
public List<GameObject> MapPrefabs;
public class MapAuthoringBaker : Baker<MapAuthoring>
{
public override void Bake(MapAuthoring authoring)
{
var entity = GetEntity(TransformUsageFlags.Dynamic);
var buffer = AddBuffer<MapElement>(entity);
for (int i = 0; i < authoring.MapPrefabs.Count; i++)
{
buffer.Add(new MapElement
{
MapEntity = GetEntity(authoring.MapPrefabs[i], TransformUsageFlags.Dynamic)
});
}
AddComponent<MapEntities>(entity);
}
}
}
// Regular component
public struct MapEntities : IComponentData
{
}
// The Dynamic buffer component
public struct MapElement : IBufferElementData
{
public Entity MapEntity;
}
public partial struct MapRespawnSystem : ISystem
{
[BurstCompile]
public void OnUpdate(ref SystemState state)
{
foreach (var (mapBuffer, entity) in SystemAPI.Query<DynamicBuffer<MapElement>>().WithAll<MapEntities>().WithEntityAccess())
{
for (int i = 0; i < mapBuffer.Length; i++)
{
state.EntityManager.Instantiate(mapBuffer[i].MapEntity);
}
}
state.Enabled = false;
}
}
@qinyupeng
I tested FixedListXyzBytes in my project and unfortunately it does NOT work. This clearly indicates that there is more handling to the Entity type while baking. So then you have to stick to the other approaches in this case.
Entities inside of fixed lists wont be remapped, only stored as plain entities inside components or dynamic buffers. Fine to use at runtime, but any serialization is up to you to figure out. I think dynamic buffer is the preferable fit for this use case(and most others).
While I do not know the intentions of the OP, Entity references in a component can be very useful for finding components of related entities directly without iterating through all entities of an archetype. For example this is used by the ECS packages itself for Parent/Child relations. So it definitely has its use case.