I’m so excited that I finally started to understand ECS and how Bakers work, only to realize that I can’t even do something as simple as saving baked entities in a Dictionary to access them via a key.
Here’s what I’m trying to do:
I have a list of EntityDefinition, which are scriptable objects that reference models of my entities. They have fields like id (which I’ll use as a key) and a GameObject as a prefab (the part for the Baker).
I create a MonoBehaviour in a Sub Scene to store the list of all my entities. I process it in two ways:
- The classic way, where I transform the list in Awake into a Dictionary<int, EntityDefinition> to retrieve a definition by its id.
- A Baker, which creates an Entity containing an IComponentData with a NativeHashMap<int, Entity>, similar to the classic way.
Here is my code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Unity.Entities;
using Unity.Collections;
public class EntityDictionaryAuthoring : MonoBehaviour
{
public EntityDefinition[] entityList = new EntityDefinition[0];
// -------------------------------
// Processed indexed Definitions (not Baked, keep as Dictionary)
// -------------------------------
// Be able to get access to the Dictionary with definition.id without looping the array
public Dictionary<int, EntityDefinition> entities = new Dictionary<int, EntityDefinition>();
// Tools
// ----------------
public int getMaxId()
{
int id = -1;
foreach (EntityDefinition definition in entityList)
if (definition.id > id)
id = definition.id;
return id;
}
// Process
// ----------------
// Process array of Definition as indexed Dictionary
private void ProcessDictionary()
{
foreach (EntityDefinition definition in this.entityList)
entities.Add(definition.id, definition);
}
void Awake()
{
ProcessDictionary();
}
// Baker
// ----------------
// Transform all entity Prefabs into ECS Entity
private class Baker : Baker<EntityDictionaryAuthoring>
{
public override void Bake(EntityDictionaryAuthoring authoring)
{
Entity entity = GetEntity(TransformUsageFlags.None);
// Create an Native HashMap for Baked Entity
NativeHashMap<int, Entity> entities = new NativeHashMap<int, Entity>(authoring.getMaxId() + 1, Allocator.Persistent);
foreach (EntityDefinition definition in authoring.entityList)
entities.Add(definition.id, GetEntity(definition.prefabAuthoring, TransformUsageFlags.Dynamic));
// Create a EntityDictionary IComponentData
AddComponent(entity, new EntityDictionary
{
entities = entities,
});
}
}
}
// IComponentData
// ----------------
public struct EntityDictionary : IComponentData
{
public NativeHashMap<int, Entity> entities;
}
// Scriptable EntityDefinition
// ----------------
[System.Serializable]
[CreateAssetMenu(fileName = "Entity", menuName = "Infinitory/Entity/Entity", order = 50)]
public class EntityDefinition : ScriptableObject
{
public int id;
public GameObject prefabAuthoring;
}
But now, I have a lot of errors:
ArgumentException: Blittable component type ‘EntityDictionary’ on GameObject ‘DefinitionDictionary’ 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.
Unity.Entities.EntityDiffer.ThrowNonSerializationExceptions (Unity.Collections.NativeList`1[T] debugInfo) (at ./Library/PackageCache/com.unity.entities/Unity.Entities/Diff/EntityDifferComponentChanges.cs:1714)
InvalidOperationException: The previously scheduled job EntityDiffer:BuildEntityGuidToEntity reads from the Unity.Collections.NativeArray1[Unity.Entities.EntityDiffer+EntityInChunkWithGuid] BuildEntityGuidToEntity.SortedEntitiesWithGuid. You must call JobHandle.Complete() on the job EntityDiffer:BuildEntityGuidToEntity, before you can deallocate the Unity.Collections.NativeArray1[Unity.Entities.EntityDiffer+EntityInChunkWithGuid] safely.
Question 1: From what I understand, NativeHashMap is not “blittable” and won’t keep the data once baked. Is that correct?
Solutions?
A. Bake it at runtime when the game starts using Job.OnStartRunning?
B. Another better solution I haven’t thought of?
Question 2 (bonus): I haven’t tested this yet, but I read that MonoBehaviour on Sub Scenes is not available at runtime. So, my method of storing a Dictionary<int, EntityDefinition> won’t work either.
Solutions:
A. Store my entityList on another MonoBehaviour on the scene directly and access it from the Baker (using a Singleton, for example, or a reference stored on the Sub-scene authoring MonoBehaviour)?
B. Have two lists, one on the scene and another (the same) on the Sub-scene?
Can anyone help me out with these issues?