How to save Scriptable Object as value, not by reference?

Discussion and help for Easy Save 3
cjygame
Posts: 3
Joined: Fri Dec 03, 2021 2:01 am

How to save Scriptable Object as value, not by reference?

Post by cjygame »

Hi, I was having an issue with saving and loading Scriptable Objects of my projects.

My scriptable objects have something like:

Code: Select all

public class MyScriptableObject : ScriptableObject
{
string itemName;
Sprite itemImage;
GameObject prefab;
float damage;
float cost;
}
of these, string and float appear just fine when I call ES3.Load<MyScriptableObject>, but sprite and GameObject are lost. (itemName and prefab, in the above example). But the scriptable object asset in the project folder itself did not lose any of its data during saving/loading. Considering that the said loaded scriptable objects has no name at all, it appears to be a copy of incomplete asset.

Looking through old posts on the forum, I believe the issue that I am saving the scriptable object by reference, not by its value. I've been saving like:

Code: Select all

ES3.Save<MyScriptableObject>("SAVE", transform.GetComponentInChildren<Item>().MyScriptableObject);
The above code is causing the issue, so I tried making a new array of MyScriptableObject, or creating a new MyScriptableObject and then assigning them. I also tried putting [ES3Serializable] on every possible variables on the SO. But none of the ways in which I attempted to find MyScriptableObject's value worked, and only found its reference.

How do you properly save scriptable object as an asset, and not save an instance of the scriptable object that's created during runtime? Or, was I getting all of those things wrong? Please let me know, thanks.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: How to save Scriptable Object as value, not by reference?

Post by Joel »

Hi there,

Just to clarify, is the issue that the prefab and itemImage fields are not being saved? If this is the case, this isn't an issue with ScriptableObjects. The issue is instead that there isn't a reference to that GameObject and Sprite at runtime.

This might be because the GameObject and Sprite are generated at runtime, or that there is not a reference to these objects in your scene prior to runtime, making it impossible to generate a persistent reference to them. Be aware that fields of UnityEngine.Object types will be stored by reference.

Please could you tell me more about the GameObject and Sprite you are trying to store a reference to. Are they generated at runtime? Is the Sprite modified at runtime? Do they exist in your scene prior to runtime or are they loaded in dynamically, or originally belong in a separate scene?

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
cjygame
Posts: 3
Joined: Fri Dec 03, 2021 2:01 am

Re: How to save Scriptable Object as value, not by reference?

Post by cjygame »

Oh, I see. Neither the GameObject nor the Sprite are generated at runtime. They all contain asset data, and I made sure of it using Odin Inspector's [AssetOnly] attribute. Also, looks like my method does not have a reference to these objects in my scenes prior to runtime.

Okay, so they are: Not generated at runtime. Sprite is not modifed at runtime. The scriptable objects themselves exist as a list in [ItemDatabase.cs], but the prefabs and sprites does not exist in any scene prior to them being spawned into a game object pool in dynamically (I'm using a pooling system).

The prefabs themselves have a monoBehaviour which has a variable of scriptable object.

Since the scriptable objects are in no way modified during runtime, only read, I am thinking of saving a string of asset path to scriptable object, and then loading them accordingly instead of saving the scriptable object itself. Or, have a unique int ID assigned to each scriptable object and then load them based on their ID number (from the ItemDatabase that I hold their data of). Would that be a good way of doing it, or do you recommend other ways to load the scriptable object some other proper way?
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: How to save Scriptable Object as value, not by reference?

Post by Joel »

Also, looks like my method does not have a reference to these objects in my scenes prior to runtime.
Hi there,

If this is the case then you can add a reference to them by right-clicking them and pressing Easy Save 3 > Add Reference(s) to Scene.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
cjygame
Posts: 3
Joined: Fri Dec 03, 2021 2:01 am

Re: How to save Scriptable Object as value, not by reference?

Post by cjygame »

Yeay! It worked! I searched for t:MyScriptableObject in the project and right clicked->Add References(s) to the ES3 Reference Mgr (Script), which is attached as a persistent gameobject in the bootstrap scene.. Seems so obvious now, but I didn't know where to look. Thank you so much for your help :D

One last question, though. I already have 4162 Reference Count (0 Prefab Count), and that number will quadruple to 20000 ish when I finish with the project. Does that sound okay... performance or memory wise? I'm not quite certain about the impact this will have on the game.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: How to save Scriptable Object as value, not by reference?

Post by Joel »

Glad that resolves the issue :)

All the reference manager does is store references to objects already in your scene (plus the ones you've added by right-clicking and selecting 'Add References to Manager), so it won't have any measurable performance or file size impact. We have projects which have over 100,000 which show no spike when profiling.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
alchemist_wurzel
Posts: 5
Joined: Fri Nov 26, 2021 11:35 am

Re: How to save Scriptable Object as value, not by reference?

Post by alchemist_wurzel »

Hi Joel,

I am attempting to do exactly as the title of this thread says.

I have a ScriptableObject instance (generated at runtime) that I want to serialize by value. It does contain Unity.Object references but we have marked them all as ES3NonSerializable which works and has already been tested. The other fields are structs only and intended to be serialized.

If I try to serialize it like so

Code: Select all

byte[] arBytes = ES3.Serialize(_scriptableObjectInstance);
ES3.SaveRaw(arBytes, _filePath);
it wants me to use the ES3 Reference Mgr which I am trying to avoid. In the ES3 Runtime Settings I have set Serialise Unity Object fields to "By Value".
Is this possible?

Cheers!
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: How to save Scriptable Object as value, not by reference?

Post by Joel »

Hi there,

This would not be possible without an Easy Save 3 Manager because ScriptableObjects are themselves reference types, so go through the reference type resolution process.

Is there any particular reason you do not want an Easy Save 3 Manager? Note that you can stop any updating for it occurring by unchecking 'Auto Update References' and 'Use Global References' in Tools > Easy Save 3 > Settings.
In the ES3 Runtime Settings I have set Serialise Unity Object fields to "By Value".
This only regards fields, not the object itself.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
alchemist_wurzel
Posts: 5
Joined: Fri Nov 26, 2021 11:35 am

Re: How to save Scriptable Object as value, not by reference?

Post by alchemist_wurzel »

I see, I will use the ES3 Manager then. It seems to work fine with it. I am just a little wary what's happening under the hood with the Object Reference stuff as I am trying not to save or load any actual references, just values.

I have run into another issue which is probably easy to fix though. I am trying to load all files in a folder and store them in a list for later use with this code:

Code: Select all

        public List<T> LoadFilesAtPath<T>(string _strPath)
        {
            List<T> liFiles = new List<T>();
            string[] arFileNames = ES3.GetFiles(_strPath);
            ES3Settings settings = new ES3Settings(_strPath);
            settings.location = ES3.Location.File;
            for (int i = 0; i < arFileNames.Length; i++)
            {
                byte[] arFileAsBytes = ES3.LoadRawBytes(Path.Combine(_strPath, arFileNames[i]), settings);
                T file = ES3.Deserialize<T>(arFileAsBytes, settings);
                if (file != null) liFiles.Add(file);
            }
            return liFiles;
        }
What happens when I load e.g. 3 files is that only the last file that is deserialized will be in the list 3 times. I am probably doing something wrong with the ES3Settings here or something similar but I can't figure out what I'm doing wrong.
User avatar
Joel
Moodkie Staff
Posts: 4826
Joined: Wed Nov 07, 2012 10:32 pm

Re: How to save Scriptable Object as value, not by reference?

Post by Joel »

Hi there,

You shouldn’t provide an ES3Settings object at all as this will override the path you give as a parameter, and File is already the default location.

All the best,
Joel
Joel @ Moodkie Interactive
Purchase Easy Save | Contact | Guides | Docs | Getting started
Post Reply