Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by SaintA721 · Feb 01, 2021 at 12:18 PM · scriptableobjectloadingsave dataloading filesave-to-file

How to Save and Load a list of Scriptable Objects from a File?

I'd like to be able to load and save a list Scriptable Objects. For example, an players inventory which is a C# List of Scriptable object type Items that needs to be saved to a file and loaded from said file when the player starts the game again.

I'd preferably like to not have to store this data on a server but instead keep it local where the game lives. But at this point if there is an simpler way to load SO, I'd be open to hearing it.

I've looked into addressables and saving to json files, but have ran into dead ends with both (I can't figure out a way to load a SO from addressables without using Addressables.InstantiateAsync("string/to/path"); and json files are unable to save the SO since its id changes every time I reload the game). I won't rule out either of these options, but if someone could provide a simple example of using one of these methods or a new method or even just talk through a possible workaround that would be great!

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

2 Replies

· Add your reply
  • Sort: 
avatar image
1

Answer by AbandonedCrypt · Feb 02, 2021 at 12:19 PM

Why overcomplicate this and not just use AssetDatabase? Using AssetDatabase.CreateAsset() you can save a scriptableobject to an asset file. At editor-time you can load it via AssetDatabase.LoadAssetAtPath() and you can also add it to any form of asset retrieval method (assetbundles, addressables ...) for loading at runtime. As your ScrptableObjects are not supposed to be altered at runtime, this should be the easiest way.

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image SaintA721 · Feb 04, 2021 at 04:16 AM 0
Share

The problem isn't so much that I need to save the data inside of the Scriptable Object (from my understanding the data inside the SO is mean't to stay static since the data will get reset after the game is closed). I'm more in the market to understand how to save the specific SO a player would have acquired over their game play session. It sounds like with the Asset Database approach I could somehow put the SO in the database, grab the path to where that item lives in the database then save it to a file. When the game loads again, I would get the path to that asset from the file, then load/add it in to my inventory (lets say its a list) via the AssetDatabase.LoadAssetAtPath() you mentioned?

avatar image AbandonedCrypt SaintA721 · Feb 09, 2021 at 02:01 PM 0
Share

No SO data is meant to be persistent. Think of it as a data container that keeps its data consistent between editor time and run time. You are not supposed to save data into a scriptableobject during runtime. Scriptableobjects are not supposed to be altered at runtime.
i might misunderstand your use-case but the way i get it you try to keep track of player progress or something and save that into a scriptableobject at runtime? in that case a SO is the wrong thing to use and you are better off using a custom class of values that you serialize the old-fashioned way, into json or bytestream etc.

avatar image
0

Answer by Llama_w_2Ls · Feb 01, 2021 at 01:49 PM

Saving and loading data can be done by writing and reading variables to and from a file. I will get to how you can do that later, but just a quick explanation on files.


Files can store data that contain text. This text could be a string, a number, a bool or byte etc. It's important to note that not all data types can be converted to a string perfectly. Therefore, we need to save a string that represents that data type, and when we read the file, we need to interpret that string correctly.


Let's take the example of saving the position of a game object to a file. You can't save the transform of the object, but you can save 3 floats that contain the x, y and z position of the object. Or use Vector3.ToString().


Reading and Writing from/to a file

You need to be using System.IO to perform operations on files.


Reading a file

You can use the File class to read all lines from the file into a single string, or use the StreamReader to read the file line by line, and perform operations on the line that is currently being read by the stream reader. For example:

     void ReadFile(string directory)
     {
         // Getting raw text
         string text = File.ReadAllText(directory);
 
         // Using a stream reader
         StreamReader reader = new StreamReader(directory);
 
         string line;
         while ((line = reader.ReadLine()) != null)
         {
             if (line == "Line 4: ")
             {
                 // Do stuff
             }
             // etc.
         }
     }



Writing to a file

You can write to a file using the File class or by using the StreamWriter as well:

     void WriteToFile(string directory)
     {
         // Overwrites any text already in the file
         File.WriteAllText(directory, "Hello world");
 
         // Adds on to the end of an existing file
         File.AppendAllText(directory, "Hello world");
 
         // Overwrites any text already in the file
         StreamWriter writer = new StreamWriter(directory);
         writer.Write("Hello world");
     }

If the file does not exist already, it will be created. Nice.


Saving your data

To save the scriptable objects, it would be good practice to go through every object, save the index of that object in the list, then underneath, save its properties, then leave a space for the next object. Here's an example:


Scriptable object called Item:

 [CreateAssetMenu(fileName = "New Inventory")]
 public class Item : ScriptableObject
 {
     public string Name;
     public int ID;
     public float Price;
     public Vector3 Position;
 }



Class called 'Backpack' with a list of scriptable objects called inventory:

 public class Backpack : MonoBehaviour
 {
     public List<Item> Inventory;
 }

The Save() method would look something like this:

     void SaveInventory(string directory)
     {
         for (int i = 0; i < Inventory.Count; i++)
         {
             Item item = Inventory[i];
 
             File.AppendAllText
             (
                 // Saves object index
                 directory, "Object " + i.ToString() + "\n" +
 
                 // Saves object properties
                 item.name + "\n" +
                 item.ID.ToString() + "\n" +
                 item.Price.ToString() + "\n" +
                 item.Position.ToString() + "\n\n"
             );
         }
     }

And the Load() method would look something like this:

     void LoadInventory(string directory)
     {
         StreamReader reader = new StreamReader(directory);
         string text = reader.ReadToEnd();
 
         // Remember to close the stream IMPORTANT!
         reader.Close();
 
         string[] lines = text.Split('\n');
 
         for (int i = 0; i < lines.Length; i++)
         {
             // Is a new object
             if (lines[i].Contains("Object"))
             {
                 // Get properties
                 string name = lines[i + 1];
                 int id = int.Parse(lines[i + 2]);
                 float price = float.Parse(lines[i + 3]);
                 Vector3 position = StringToVector3(lines[i + 4]);
 
                 // Create new scriptable object and add to inventory
                 Item item = ScriptableObject.CreateInstance<Item>();
 
                 item.Name = name;
                 item.ID = id;
                 item.Price = price;
                 item.Position = position;
 
                 Inventory.Add(item);
             }
         }
     }
 
     Vector3 StringToVector3(string sVector)
     {
         // Remove the parentheses
         if (sVector.StartsWith("(") && sVector.EndsWith(")"))
         {
             sVector = sVector.Substring(1, sVector.Length - 2);
         }
 
         // split the items
         string[] sArray = sVector.Split(',');
 
         // store as a Vector3
         Vector3 result = new Vector3(
             float.Parse(sArray[0]),
             float.Parse(sArray[1]),
             float.Parse(sArray[2]));
 
         return result;
     }



Let me know if this works or not, and I'll try to help. @SaintA721

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image SaintA721 · Feb 02, 2021 at 05:46 AM 0
Share

Wow this is great thanks! I have a couple of edge cases such as when the Scriptable Object has other fields such as Prefabs, enums and references to other Scriptables Objects. But it sounds like the solution to that would be to increase the complexity of the Load and Save to include functionality to break apart the info inside the prefabs and SO's so they can be instantiated and passed into the original Scriptable Object later?

avatar image Llama_w_2Ls SaintA721 · Feb 02, 2021 at 07:42 AM 0
Share

Exactly. For more complex objects, you need to interpret saved data. For example, you could already have a list of prefabs available to choose from, and when you load your saved data, you read the string, and choose the correct prefab from the list that matches the name of the string.

avatar image Bunny83 · Feb 02, 2021 at 12:04 PM 1
Share

I would strongly recommend to not roll your own text based format. If you really want to do it anyways you have to pay much more attention to potential issues. One is versioning. If you roll out an update of your game and something has changed in your save format, you have to take that into account. This generally requires you to know the format the old save files was stored in. So including a version number / string is a must have. Next thing is the usage of float.Parse without an explicit culture will fail depending on your local culture setting. For example here in germany (but also in france and spain if I'$$anonymous$$ not mistaken) we use the comma as decimal point and not the dot. So your vector parsing code would completely break down. The same care has to be taken when saving the data as ToString also by default uses the local culture. You should always use the InvariantCulture for any kind of serialization. Next is your text based parsing is error prone. You search for a line that contains the string Object. However what happens when an object has the name "Object"? since you parse over every single line you would start parsing a new object at the name. You also really, really shouldn't use the static File.AppendAllText in a loop on the same file. Each call will open a file handle, seek to the end of the file, writes your line and closes the file again. This is not only horrible for performance reasons but also bad from a file updating point of view (thinking of SSD and their write cycles)


I would highly recommend to use a framework that does all the serialization for you in a consistent way. Commonly we use json as it's very compact and at the same time can represent arbitrarily complex data structures. I've written a very compact json library. There's also an extension file for some Unity types which allows direct writing of Vector, Quaternion or Color values.

avatar image Llama_w_2Ls Bunny83 · Feb 02, 2021 at 12:39 PM 0
Share

$$anonymous$$y bad. I wasn't aware of the better approaches to saving data.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

117 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

how to have save and load system. With this programing,How to do a save and load system on mobile. With this programing 1 Answer

Save and (later) read scriptable objects 1 Answer

Problem when saving and loading 1 Answer

Saving Player Data for Multiple Scenes/Levels 2 Answers

Save and load an inventory 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges