Better way to save default values, readable at runtime?

Good Morning. I am creating a small asset and I have doubts about whether my implementation is correct. I have a class with a series of values like:

public class MyClass: MonoBehaviour {
    [SerializeField] List<string> _values;

    void Start() {
        foreach(var value in _values)
            Debug.Log(value);
    }
}

However, I expect the instance of MyClass to return both the values in the _values list and a series of default values that are common to all instances of MyClass:

public class MyClass : MonoBehaviour {
    [SerializeField] List<string> _values;

    void Start()
    {
        DefaultValues.WriteDefaultValues();
        foreach(var value in _values)
            Debug.Log(value);
    }
}

public static class DefaultValues{
    static List<string> _defaultValues;

    public static void WriteDefaultValues(){
        foreach(var defaultValue in _defaultValues)
            Debug.Log(defaultValue);
    }
}

Now, the value of _defaultValues must be both configurable from the editor and available in the final build. The approach I am using stores the value of _defaultValues in a ScriptableObject asset that is loaded using Resources at the beginning of the application.

public class MyClass : MonoBehaviour
{     
    [SerializeField] List<string> _values;

    void Start()
    {
        DefaultValues.Instance.WriteDefaultValues();
        foreach (var value in _values)
            Debug.Log(value);
    }
}

public class DefaultValues : ScriptableObject
{
    static DefaultValues _instance;

    public static DefaultValues Instance => _instance ??= Resources.Load<DefaultValues>("MyDefaultValues");

    [SerializeField] List<string> _defaultValues;

    public void WriteDefaultValues()
    {
        foreach (var defaultValue in _defaultValues)
            Debug.Log(defaultValue);
    }
}

My asset needs to either have a resources folder with an instance of DefaultValues called “MyDefaultValues” which is a singleton pattern or else it will throw an error. Something like ScriptableSingleton would have been perfect, however it needs to be accessible from the final assemblies and not just from the editor. Is my approach the correct one for these cases? Isn’t there a way to access this data without using Resources and without having to create additional assets in the project? What is the design pattern that you use or that unity recommends for these situations?

PS: Avoid solutions that use Addresables or any package that is not included in Unity by default, because I want it to work in any editor without further requirements

What prevents you from statically embedding the default list inside the container?
Maintenance-wise, you can make a code generator that lets you easily convert a ScriptableObject to a script that is pretty much available in the assembly when you ship the class.

However the problem with SerializedFields is that you cannot tell whether the underlying series are ever initialized. From the C# perspective, it cannot tell. The values are simply injected by Unity and the list is created as a whole. I’m not sure if you can do this at all.

So basically, let me get this from the beginning:
DV = default values
OC = original class

You want an OC object with data serialization, that is somehow filled with DV on initialization, but never again.

I’m not sure why are you doing any of it like this?

You instead need a factory that will make an instance of the OC, fill it up and leave it alone.
This factory simply has access to DV, and uses them to construct the OC.
The OC simply serializes its list.

The two have nothing to do with each other.

1 Like

Fun fact, Awake/OnEnable for scriptable objects are called when you put them in your pre-loaded assets. It’s a pretty simple way of initialising scriptable object singleton references, and they will stay in memory during the entire applications runtime. I use it a lot for low-level configuration values.

And if you need values to reset to ‘default’ values, you can just reset them in OnEnable which gets called for SO’s during domain reloads.

1 Like

I don’t see the code generator feasible, firstly because it is a half solution, I replace a resources folder with an scriptable object by a monoscript file but I still have additional files and secondly because DV contains serialized UnityEvents and it seems to me an overwhelming task, if it is even possible, to create a code generator that interprets the persistent listeners of a UnityEvent.

It’s just a minimal example, maybe I shouldn’t have added the SerializeField attribute in the OC to avoid doubts. I have full guarantees that _values is initialized and it’s not null in any case. Although OC is serializable in the original project, OC is not even a MonoBehaviour and it is intended to be instantiated with a regular constructor like any class not derived from UnityEngine.Object.

OC is never populated with DV data, it just has access to it and uses it at some point. It doesn’t need an initialization with the DV data, it just requires the reference to DV which in this case is DefaultValues.instance.

I understand if you’re not sure why I’m doing this but it’s a long project and this is just a silly example so as not to overwhelm anyone with unnecessary code. Of course it doesn’t make any apparent sense here, but I’m sure I want and need to access a single DV instance from any OC instance.

I invite you not to wonder if what I am doing is the right thing, I am completely sure that it is the right THING and in fact it works as I expect with the Resources/SingletonScriptable pattern. What I’m asking is, am I doing it in the right WAY?

Thanks for the comment. I use the Instance property to access Resources and load the Scriptable instead of Awake/OnEnable because I am not sure if the user will need to use the Scriptable at some point and thus I save a call to Resources.Load, but to be honest I don’t know if it really is more efficient.

Anyway, I insist once again: the question is not if there is a more optimal way to load the Singleton with Resources than the one I am using. The question is if there is an approach without the use of Resources and singleton scriptables.

If you need ‘one of’ something then you always need
 one of something. Whether that be a plain static class, a one-off asset that you reference everywhere, or a singleton instance (which is fundamentally the same as the prior point).

At the end of the day, you need an instance somewhere that you get a reference to in some way shape or form, potentially via a singleton pattern or other means. Even all of Unity’s configuration values are using it, I believe. There’s no real way around it.

I use Json for this.