Search Unity

  1. Engage, network and learn at Unite Austin 2017, Oct 3 - 5. Get your ticket today!
    Dismiss Notice
  2. Introducing the Unity Essentials Packs! Find out more.
    Dismiss Notice
  3. Check out all the fixes for 5.6 on the patch releases page.
    Dismiss Notice
  4. Unity 2017.1 is now released.
    Dismiss Notice
  5. Unity 2017.2 beta is now available for download.
    Dismiss Notice

Saving instances of Scriptable Objects to prefabs

Discussion in 'Scripting' started by Extrakun, Aug 10, 2010.

  1. Extrakun

    Extrakun

    Joined:
    Apr 2, 2009
    Posts:
    69
    I am using scriptable objects to hold configurations and settings, something like this:

    Code (csharp):
    1. class GUIConfig extends ScriptableObject
    2. {
    3.   ...
    4. }
    I am storing them inside a component attached to a game-object, using ScriptableObject.CreateInstance() from an editor script.
    Code (csharp):
    1.  
    2. public var guiconfig:GUIConfig;
    3.  
    4. public function Start()
    5. {
    6.    // access guiconfig to set skin
    7. }
    However, when I try to make the game-object a prefab, the scriptable object (GUIConfig) is not saved.

    What am I doing wrong?
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,596
    to save anything on a prefab it must be done from an editor script, where you assign it to the array and then use the SetDirty function to tell the editor that the data has changed and requires saving
     
  3. Extrakun

    Extrakun

    Joined:
    Apr 2, 2009
    Posts:
    69
    Unfortunately,that doesn't seem to work.

    I am only using the Editor Scripts to create instances of the scriptable objects. To create the prefab, I do the usual - drag the game object into a new prefab.

    But when I do so, all the scriptable objects in the prefab are null. The game object itself has all the scriptable object instances in bold, and I couldn't apply to it the prefab.

    I've add a screenshot to illustrate:
     

    Attached Files:

  4. Alex-Chouls

    Alex-Chouls

    Joined:
    Mar 24, 2009
    Posts:
    1,495
    I'm running into this same problem with the Unity3 beta.

    I have a MonoBehaviour which contains ScriptableObjects in an array. It all works great (saving/loading etc.) until I try to make the game object a prefab - then the scriptable objects are gone (set to None)

    If I try doing it the other way around, and add the scriptable objects to the prefab, then they are gone when the prefab is instantiated (set to Missing).

    I'll try building a simpler test case, but in the meantime, was wondering if other people are seeing this, and if there is a way to preserve scriptable object references in a prefab?

    [EDIT] Forgot to mention that I instantiate the scriptable objects in an editor using ScriptableObject.CreateInstance()
     
  5. Alex-Chouls

    Alex-Chouls

    Joined:
    Mar 24, 2009
    Posts:
    1,495
    Okay, I made a super simple test case to illustrate the problem.

    MyBehaviour.cs:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class MyBehaviour : MonoBehaviour
    5. {
    6.     public TestData data;
    7. }
    TestData.cs:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. [Serializable]
    5. public class TestData : ScriptableObject
    6. {
    7.     public float health;
    8. }
    Editor/MyBehaviourEditor.cs:
    Code (csharp):
    1. using UnityEditor;
    2. using UnityEngine;
    3.  
    4. [CustomEditor(typeof(MyBehaviour))]
    5. class MyBehaviourEditor : Editor
    6. {
    7.     public override void OnInspectorGUI()
    8.     {
    9.         MyBehaviour myBehaviour = target as MyBehaviour;
    10.  
    11.         if (GUILayout.Button("Instantiate"))
    12.         {
    13.             myBehaviour.data = (TestData)ScriptableObject.CreateInstance(typeof(TestData));
    14.         }
    15.  
    16.         if (myBehaviour.data != null)
    17.         {
    18.             GUILayout.Label(myBehaviour.data.ToString());
    19.         }
    20.     }
    21. }
    Now some testing:

    1. Drag MyBehaviour onto a GameObject and instantiate TestData. It works and saves/load correctly (as expected).

    2. Drag the GameObject to a prefab and TestData is lost.

    3. Re-instantiate TestData on the prefab, and drag the prefab into the scene. TestData is intact, however, it doesn't survive save/load.
     
  6. Alex-Chouls

    Alex-Chouls

    Joined:
    Mar 24, 2009
    Posts:
    1,495
    Found this thread that sheds some light on the problem.

    Basically, it seems that nested ScriptableObjects are not automatically serialized. I tried saving them manually (AssetDatabase.CreateAsset(), EditorUtility.SetDirty(), AssetDatabase.SaveAssets()) and that does seem to work... Unity seems to take care of the details. It means a lot of extra asset files, but seems like a workable solution right now. Need to do some more testing to make sure it really works.

    Are there any plans to automatically serialize nested ScriptableObjects? Or document some of the details of Unity serialization in one place.

    For example, I also beat my head against this problem for a bit: "...This makes it impossible to use inheritance in a serialized embedded class. If you must use inheritance you have to inherit from ScriptableObject." Which led me down the road to this problem with embedded ScriptableObjects!

    Figuring out these kinds of special rules can eat up a lot of development time...

    But I don't mean to sound negative, these are details, overall Unity is really amazing and a lot of fun to work with! Keep up the great work!
     
  7. nafonso

    nafonso

    Joined:
    Aug 10, 2006
    Posts:
    376
    I also had this problem.

    Whenever I applied the changes to the prefab, the prefab would end up with arrays populated with null references.

    Basically I gave up on the ScriptableObject, I just do my classes like this and everything works fine:

    Code (csharp):
    1.  
    2. [System.Serializable]
    3. public class MyClass
    4. {
    5.     public int m_id;
    6.     ...
    7. }
    8.  
    Although this does seem to beat the purpose of the ScriptableObject class IMO...

    Regards,
    Nuno Afonso
     
  8. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    938
  9. gilley033

    gilley033

    Joined:
    Jul 10, 2012
    Posts:
    532
    Never mind, I guess you guys are looking for something other than what I offered . . .
     
    Last edited: Mar 13, 2013
  10. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    506
  11. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,204
  12. Medding3000

    Medding3000

    Joined:
    Dec 11, 2013
    Posts:
    45
    Did anybody find a solution for this?
    Is it possible to get nested scriptableobjects serialized when creating a prefab?

    Im bumping into this problem:
    Im editing a component (script) through a customeditor, then i want to make it a prefab so i drag it into the project view. Now all my references say 'None'.

    Furthermore, when i just copy a GO it is NOT a deep copy but the references remain the same. WHY is this?! So to say, i keep a reference of the GO in the scriptableobject. Then i copy it. the reference will still point to the old GO. :( this makes me sad.
     
  13. Qiu-Jian

    Qiu-Jian

    Joined:
    May 22, 2013
    Posts:
    3
    @Medding3000

    LigihtStriker is right. You need to create an "asset" to hold the scriptableObject with AssetDatabase.CreateAsset(asset, assetPath); This should be done in "EditorMode". If you modify the scriptableObject, don't forget to call "EditorUtility.SetDirty(asset)".

    Once you have the scriptableObject saved in an asset, you can put the reference in a Monobehavior of a prefab.

    BTW. On Android, I met some problem on loading prefab referring some deep nested scriptableObjects using Resources.Load. The player crashes if I put a large number of deep nested gameObject in that prefab.

    There is no such problem if the scriptableObject is located in an scene and loaded by Application.LoadLevelAddictive.

    I have been struggling with this problem for 2 days without finding and clue.
     
  14. aresfe

    aresfe

    Joined:
    Feb 26, 2015
    Posts:
    1
    now it's 2015, and this problem still haven't been solved!
    we do need more extensible serialization in Unity for polymorphism
     
  15. nack

    nack

    Joined:
    Dec 14, 2012
    Posts:
    6
    Okay old thread that I stumbled across. But, I think you need to set the hideFlags on the scriptableObject to HideFlags.HideAndDontSave. This is needed if the root of an object is NOT in the scene, like a prefab. Here is a link to a mega thread by a unity Dev about scriptableObjects best practices.
     
  16. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    938
    If you want to save a ScriptableObject asset then you will need to save it as a standalone asset using AssetDatabase.CreateAsset. Hiding the asset only makes sense when using AssetDatabase.AddObjectToAsset.

    If you want to keep the object instance (like if it is an asset) then you absolutely do not want to use the "DontSave" flag otherwise it'll be lost when Unity next loads.

    If it is just a temporary instance, then obviously the "DontSave" flag makes sense, but still don't forget to manually destroy the instance where applicable to avoid leakage.
     
    nack likes this.
  17. nack

    nack

    Joined:
    Dec 14, 2012
    Posts:
    6
    You're probably right, but that guides does say the following:

    HideFlags
    In the examples using ScriptableObjects you will notice that we are setting the ‘hideFlags’ on the object to HideFlags.HideAndDontSave. This is a special setup that is required when writing custom data structures that have no root in the scene. This is to get around how the Garbage Collector works in Unity.

    When the garbage collector is run it (for the most part) uses the scene as ‘the root’ and traverses the hierarchy to see what can get GC’d. Setting the HideAndDontSave flag on a ScriptableObject tells Unity to consider that object as a root object. Because of this it will not just disappear because of a GC / assembly reload. The object can still be destroyed by calling Destroy().
     
  18. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    938
    Correct; the "DontSave" part indicates that the object will not be saved when you exit from the Unity editor. This is useful when creating temporary objects that have no root; like procedurally generated meshes for instance.

    Typically you would destroy the object inside a "OnDestroy" or "OnDisable" method in the associated MonoBehaviour class.

    In editor scripting you do not necessarily want to explicitly destroy the ScriptableObject instance; setting the "DontSave" flag is useful here since it avoids "leaked object" warnings from being logged to the Unity console. Unity will just release the native resource since this was clearly intended since the "DontSave" flag was specified.

    If you are saving a ScriptableObject to an asset file with AssetDatabase.CreateAsset or AssetDatabase.AddObjectToAsset, then you obviously DO want to save it... so do not use the "DontSave" flag since this is ambiguous and may behave oddly.

    If you have a ScriptableObject instance, and you haven't saved it to an asset file using either AssetDatabase.CreateAsset or AssetDatabase.AddObjectToAsset, then it WILL NOT be saved as a part of the prefab file. It will simply be lost when you next reload Unity.
     
    nack likes this.
  19. nack

    nack

    Joined:
    Dec 14, 2012
    Posts:
    6
    Okay I think I got'cha.

    If I want the scriptableObject to be serialized outside of the scene, I MUST add it to an asset using either AssetDatabase.CreateAsset or add it to an existing asset with AssetDatabase.AddObjectToAsset, without the hideFlags, or at least without DontSave. With AddObjectsToAsset I can use a single asset to store multiple instances of said scriptableObject or it's derivatives (not abstracts though, according to that unity dev post, it's not wise).

    Thanks for clearing this up btw, I don't think I'm the only one slightly confused :)
     
  20. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    938
    You can save multiple ScriptableObject instances of varying class types to the same .asset file. But avoid adding ScriptableObject instances to .prefab files because that is probably asking for trouble; especially when (and if) they finally decide to add nested prefab support.

    You can't save abstract `ScriptableObject` instances to an asset file for the simple reason that you can't actually instantiate one... :p
     
    nack likes this.
  21. nack

    nack

    Joined:
    Dec 14, 2012
    Posts:
    6
    Thanks, I'll stick to .assets for this then. Heh, I of course meant the derived classes ;)
     
  22. numberkruncher

    numberkruncher

    Joined:
    Feb 18, 2012
    Posts:
    938
    You can save derived ScriptableObject class instances to .asset files.

    Unity cannot serialize derived instances of non-ScriptableObject classes that are annotated with the `System.SerializableAttribute` attribute. For these you cannot serialize by abstract or base references.
     
  23. ALKubo

    ALKubo

    Joined:
    Mar 3, 2017
    Posts:
    1