Serializing References to Non-Unity Objects

I have a voxel world split into many Chunk objects (Chunk doesn’t inherit from anything) that I have stored in a SerializableDictionary in a ChunkStore monobehaviour. The Chunk object has the [System.Serializable] attribute, so it can be serialized in the monobehaviour and edited in the inspector.

I would like to have references to some Chunk objects in other monobehaviours; for example, Chunks that have meshes have a GameObject created for them and a ChunkMono monobehaviour added that stores a reference to the Chunk.

Here is some pseudo-code:

public class ChunkStore : MonoBehaviour {

    // I want Chunks to be serialized / saved with this Monobehavior
    public SerializableDictionary<int3, Chunk> chunks;

}

public class ChunkMono : MonoBehaviour {

    // I want this to reference a Chunk stored in the ChunkStore
    [SerializeReference] public Chunk chunk;

}

[System.Serializable]
public class Chunk {

    // some fields

}

In order to have ChunkMonos only store a reference to the Chunk objects, I put the [SerializeReference] attribute on the Chunk field. However, serializing (unloading and reloading the scene) still seems to create a copy of the Chunk object instead of referencing the Chunk in the ChunkStore.

Here is some pseudo-code to test this:

private void TestIfChunksAreTheSame(ChunkMono chunkMono, ChunkStore chunkStore, int3 chunkCoordinates) {
    Chunk chunk1 = chunkMono.chunk;
    Chunk chunk2 = chunkStore.Chunks[chunkCoordinates];
    // they are the same before reloading the scene
    if (chunk1 == chunk2) {
        Debug.Log("ChunkMono's Chunk is the same as the ChunkStore Chunk");
    }
    // they are not the same after reloading the scene
    else {
        Debug.LogError("ChunkMono's Chunk is not the same as the ChunkStore Chunk!");
    }
}

Am I using [SerializeReference] wrong or is there a better way that I could store a serializable Chunk object in ChunkStore and references to Chunks in ChunkMono? I’ve considered having Chunk extend ScriptableObject, but it seems that ScriptableObjects need a separate asset to save and won’t serialize with the ChunkStore monobehaviour, and I do not want to manage a ScriptableObject asset for every single Chunk.

Any reference type that Unity serializes gets new‘ed on deserialization. So this won‘t work. Furthermore SerializableDictionary only works for small datasets. If we‘re talking Minecraft-esque worlds you will find serialization of chunks to perform badly and use a lot of memory too.

Best bet is to use Unity‘s Serialization package and serialize the chunks to binary. This is super fast and can be done in a background job, plus you get full flexibility.

Unity’s serialisation is per-Unity object. You cannot serialize references to non-Unity objects across assets; you’ll end up with multiple different instances. This is just a restriction you have to work with.

1 Like

You could serialize just the managed reference ids of the chunks in the ChunkMono components, and then resolve the reference at runtime using ManagedReferenceUtility.GetManagedReference.

[ExecuteAlways]
public sealed class ChunkMono : MonoBehaviour
{
    [SerializeField] ChunkStore chunkStore;
    [SerializeField] long chunkRefId;
 
    Chunk chunk;

    void Awake() => UpdateChunkReference();

    public void SetChunk(ChunkStore chunkStore, Chunk chunk)
    {
        #if UNITY_EDITOR
        UnityEditor.Undo.RecordObject(this, "Set Chunk");
        chunkRefId = ManagedReferenceUtility.GetManagedReferenceIdForObject(chunkStore, chunk);
        #endif

        this.chunkStore = chunkStore;
        this.chunk = chunk;
    }

    void UpdateChunkReference()
        => chunk = (Chunk)ManagedReferenceUtility.GetManagedReference(chunkStore, chunkRefId);
}