Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

copy / reset Scriptable Objects

Discussion in 'Scripting' started by Tom163, Nov 6, 2020.

  1. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,263
    I've got a bunch of scriptable objects, some of which need to reset to default values when a new game starts.

    My thinking was to have a copy of them with the default values, that makes it easy to edit it in the editor and ensure that whatever happens, the default values don't change.

    Now to my surprise I found that "currentSettings = defaultSettings" doesn't do what I expected, namely copy over the values.


    Is there a way to tell a Scriptable Object "set all your value to these values from this other SO that's also your type" ?
     
  2. diego-giacomelli

    diego-giacomelli

    Joined:
    Oct 27, 2012
    Posts:
    26
    This happens because you need to set each individual field or property of the target ScritableObject, but the line:

    currentSettings = defaultSetting

    Is just changing what object the variable currentSettings is referencing.

    A simple way to do this is to use the JsonUtility, first, you get the json from the defaultSetting with JsonUtility.ToJson, then for each ScriptableObject of the same type you call the JsonUtility.FromJsonOverwrite.

    Is good to remember that you must consider the limitation and performance of JsonUtility: https://docs.unity3d.com/Manual/JSONSerialization.html
    So, use it with parsimony.


    Here a sample code with a hypothetic ScriptableObject:

    SO.cs
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [CreateAssetMenu(menuName = "SO")]
    4. public class SO : ScriptableObject
    5. {
    6.     [SerializeField]
    7.     string _privateField;
    8.  
    9.     public int publicField;
    10. }
    SOReseter.cs
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class SOReseter : MonoBehaviour
    4. {
    5.     [SerializeField]
    6.     SO _default;
    7.  
    8.     [SerializeField]
    9.     SO[] _targets;
    10.  
    11.     private void Awake()
    12.     {
    13.         if (_default != null)
    14.         {
    15.             var json = JsonUtility.ToJson(_default);
    16.  
    17.             foreach (var t in _targets)
    18.                 JsonUtility.FromJsonOverwrite(json, t);
    19.         }
    20.     }
    21. }
     
  3. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,290
    If you need to have a copy of ScriptableObject - just use Instantiate(*your_so*).
    That will create and return a copy of it. Then, just modify / use copy instead of default one.


    Keep in mind though, ScriptableObjects are designed to be used as an immutable read-only data structures.
    You're misusing them somewhat, if you're modifying them in runtime.
    Although, there's always exception to the rules.
     
    Last edited: Nov 6, 2020
  4. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,263
    They are? None of the tutorials and texts said that so clearly. Yes, I am extensively modifying them at runtime to store game state information, especially about entities not in the current scene (which, for example, recover with global time passing, even if they're not in the current scene).
     
  5. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,290
    You can use different approaches to achieve that.
    Like a static class storage, no need to create / alter SO's for that (less overhead and confusion when working in a team).

    Its not a strict rule, but a best practice recommendation.
     
  6. Tom163

    Tom163

    Joined:
    Nov 30, 2007
    Posts:
    1,263
    I don't think static classes serve me. Here's one use case that repeats in similar form throughout my game:

    I have an SO for settlements (villages, towns, etc.) that stores things like a description, currently active quests, shops to be found there, etc.

    Having that editable in the editor, especially when I reference other SOs, is important. It also allows me to reference these objects outside scenes, e.g. when I iterate over all settlements in a daily tick to update stuff.

    And that's also where I want to reset things to when the game restarts.