0
\$\begingroup\$

I have a custom class that I serialize/deserialize to/from file(s), and is not guaranteed to be identical each time the game runs (in this example, a language pack. it's possible to fix a typo manually). As a result, I do not use UnityEngine.Object on it, and deserialize it each load. However, some classes that implement UnityEngine.Object (Monobehavior), have references to it. these references must persist (so that the language stays the same each load)
As per the Unity Scripting page, https://docs.unity3d.com/Manual/script-Serialization.html, this can cause undesired behavior: "If you store a reference to an instance of a custom class in several different fields, they become separate objects when serialized. Then, when Unity deserializes the fields, they contain different distinct objects with identical data."

Naturally, I'd prefer this not be the case. again from Unity documentation linked above: When you need to serialize a complex object graph with references, do not let Unity automatically serialize the objects. Instead, use ISerializationCallbackReceiver [interface] to serialize them manually.

I know how to implement the interface. How do I preserve the reference? (Disclaimer: not an expert in serialization, so if it's a simple fix, I'm sorry. Unity documentation doesn't provide an example for references).

\$\endgroup\$
0
\$\begingroup\$

I'd be tempted to create a ScriptableObject-derived class that serves as a "file handle" to this separately-serialized file. All your scripts that need to reference this file will reference the ScriptableObject, so references are preserved correctly (and you keep the conveniences of Unity's inspector for populating them), and the ScriptableObject can be responsible for tracking the deserialization state of this file (and possibly loading it on demand)

For example:

public abstract class ExternalResource<T> : ScriptableObject {

    [SerializeField]
    protected string filePath;

    T reference;

    public T GetResource() {
        if(reference == null)
            reference = Load();
        return reference;
    }

    protected abstract T Load();
}

[CreateAssetMenu(menuname = "External Resource/Bytes", filename = "new bytes.asset")]
public class ExternalBytes : ExternalResource<byte[]> {

    protected override byte[] Load() {
        return System.IO.File.ReadAllBytes(filePath);
    }
}

public class MyComponentRequiringExternalData : MonoBehaviour {

    public ExternalBytes dataSource;
}
\$\endgroup\$
  • \$\begingroup\$ I'll accept this - I was using a string to hold the reference - the "name," so to speak. it worked, but required making sure it was unique, and have it initialized elsewhere. the filepath will always be unique, and while perhaps more than I need, the fact it is a template allows it to be reused (OOP has been drilled into my head, so anything with reuse is a bonus). the data itself isn't serialized, either. well done \$\endgroup\$ – Andrew Baumher Aug 10 '18 at 18:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.