Thanks to the hints by @snacktime I think I got it working.
I think I’ll never figured out how subscenes work lol, like what are those sub scene sections, blob retain frames, etc it’s just too complex, but at least from reading the code I found the section where the serialization process is actually happening.
So in my new system every gameobject prefab that I want to convert will be given this component.
public class ConvertToEntityPrefab : MonoBehaviour
{
public SerializedEntityPrefab SerializedEntityPrefab;
}
And that component got one button to do the conversion in the editor and save the converted entity in a generated SerializedEntityPrefab
asset which is a ScriptableObject
asset. I wanted this to be automatic but I don’t know how to do something on asset save other than OnValidate.
[CustomEditor(typeof(ConvertToEntityPrefab))]
public class ConvertToEntityPrefabInspector : Editor
{
public unsafe override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Serialize To Entity"))
{
using (var serializationWorld = new World("Serialization World"))
{
byte[] serializedEntityData;
ReferencedUnityObjects referencedUnityObjects;
ConvertToEntityPrefab ConvertToEntityPrefab = target as ConvertToEntityPrefab;
GameObjectConversionUtility.ConvertGameObjectHierarchy(ConvertToEntityPrefab.gameObject, new GameObjectConversionSettings
(
serializationWorld,
GameObjectConversionUtility.ConversionFlags.AddEntityGUID | GameObjectConversionUtility.ConversionFlags.AssignName
));
using (var entityRemapping = serializationWorld.EntityManager.CreateEntityRemapArray(Unity.Collections.Allocator.Persistent))
{
using (var memoryWriter = new MemoryBinaryWriter())
{
SerializeUtilityHybrid.Serialize(serializationWorld.EntityManager, memoryWriter, out referencedUnityObjects, entityRemapping); // entity remapping ????
serializedEntityData = new byte[memoryWriter.Length];
using (var memoryReader = new MemoryBinaryReader(memoryWriter.Data))
{
//TODO: compress the bytes, 16kb overhead every prefab is just yikes
for (int i = 0; i < memoryWriter.Length; i++)
{
serializedEntityData[i] = memoryReader.ReadByte();
}
}
}
}
if (ConvertToEntityPrefab.SerializedEntityPrefab == null)
{
var asset = CreateInstance<SerializedEntityPrefab>();
AssetDatabase.CreateAsset(asset, AssetDatabase.GetAssetPath(ConvertToEntityPrefab).Replace(".prefab", "Serialized.asset")); //TODO: less hard-coding maybe?
AssetDatabase.SaveAssets();
ConvertToEntityPrefab.SerializedEntityPrefab = asset;
}
var SerializedEntityPrefab = ConvertToEntityPrefab.SerializedEntityPrefab;
SerializedEntityPrefab.Array = referencedUnityObjects.Array;
SerializedEntityPrefab.EntityData = serializedEntityData;
EditorUtility.SetDirty(SerializedEntityPrefab);
}
}
}
}
public class SerializedEntityPrefab : ReferencedUnityObjects
{
[HideInInspector]
public byte[] EntityData;
public void LoadEntity(World dstWorld, World loadingWorld)
{
unsafe
{
fixed (byte* bytePtr = EntityData)
{
using (var entityDataReader = new MemoryBinaryReader(bytePtr))
{
SerializeUtilityHybrid.Deserialize(loadingWorld.EntityManager, entityDataReader, this);
}
}
}
dstWorld.EntityManager.MoveEntitiesFrom(loadingWorld.EntityManager); //TODO: this method has an out param, we should tag the moved entities with SCD or something, so that we can unload it easier later on
}
}
And now I only need to refer to the generated SerializedEntityPrefab
without ever need to touch the original authoring GameObject on runtime. Also only the generated SerializedEntityPrefab
will be included in the build as addressables. This prefab authoring process overall is a lot less janky than my previous SubScene approach.
I’m not sure if this will work for all cases though, I only tried this in several prefabs with mesh renderers and other components and it works perfectly for those, they even got the prefab and linked entity group components automatically! Actually I’m quite surprised that ECS automagically exported my SharedComponentData as ReferencedUnityObjects
and importing it back correctly. I’m not sure what will happen if I use blob asset store in the conversion process. Also I don’t know what is entity mapping when serializing the world, the process works the same without it.