Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 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
0
Question by aseemkashyap · Dec 13, 2018 at 02:22 PM · serializationscriptableobjectcustom editorsaveload

"Unbroken Reference" problem when using a custom Editor to Save/Load a ScriptableObject Asset

Hi,

I'm relatively new to Unity and C#, and very new to ScriptableObjects and custom Editors, so please forgive me if my description of the problem is unnecessarily lengthy or confusing.

I've used relevant code from my project and made a small demo project, that tries to highlight only the problem I'm facing, and I've included the scripts below.

Classes:

MyScriptableObject (MyScriptableObject.cs) is a Class derived from ScriptableObject. It has two string fields, and a field of type OtherScriptableObject.

OtherScriptableObject (OtherScriptableObject.cs) is a Class derived from ScriptableObject. It has two string fields.

ObjectContainer (ObjectContainer.cs) is a plain Class. It has a string field, and a List of MyScriptableObjects.

ObjectContainerList (ObjectContainerList.cs) is a Class derived from SO. It has a List of ObjectContainers.

ListEditor (ListEditor.cs) is a Class derived from EditorWindow, that allows me to create/edit an ObjectContainerList, and Save/Load it as a ScriptableObject Asset file.

Description:

The ListEditor works as expected while starting from scratch:

I'm able to create empty ObjectContainer 'windows', in which I can Add/Remove rows of MyScriptableObject fields, and assign SO files to those fields.

Saving works fine, the ListEditor lets me save a list of these 'containers' as a ScriptableObject Asset file (of type ObjectContainerList) while keeping all references to the MyScriptableObjects (and also keeps references to the OtherScriptableObject within them).

Loading the Asset file back into the ListEditor also works, it shows the containers and their Lists exactly as they were saved.

Problem:

The problem arises after loading, where any change I now make to containers in the ListEditor is immediately reflected in the Asset file, even when the code responsible for saving the Asset file has not been executed.

For example, when I add a new row to one of the containers, immediately in the Asset file a new element gets added within that container's List.

My best guess is that there is somehow an "unbroken reference" that gets created (between the ObjectContainerList Asset file, and the List of ObjectContainers in the ListEditor) while initially saving (or loading) the Asset file.

Is there a way to prevent this?

I had some luck with a third-party 'ObjectCopier' script earlier, but that was before I needed nested ScriptableObjects.

Also, since I'm new to ScriptableObjects and the concept of Serialization in general, could the problem simply be a result of my using the wrong modifiers ([SerializeField] and [System.Serializeable]) for Classes and their fields.

If I could provide extra information or screenshots to describe the problem better, please let me know.

I apologize in advance if this question has already been asked and answered. Thanks.

Scripts :

MyScriptableObject.cs

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using System;
 
 [CreateAssetMenu(menuName = "MyScriptableObject")]
 [System.Serializable]
 public class MyScriptableObject : ScriptableObject {
 
     [SerializeField] public string myScriptableObjectName;
     [SerializeField] public string myData;
     [SerializeField] public OtherScriptableObject otherScriptableObject;
 }


OtherScriptableObject.cs

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 [CreateAssetMenu(menuName = "OtherScriptableObject")]
 public class OtherScriptableObject : ScriptableObject {
 
     [SerializeField] public string otherSoName;
     [SerializeField] public string otherData;
 }


ObjectContainer.cs

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEditor;
 using System;
 
 [System.Serializable]
 public class ObjectContainer{
 
     public string containerId;
     public List<MyScriptableObject> myScriptableObjectList;
 
     [HideInInspector] public Rect containerRect;
     float defaultHeight = 50f;
     public ObjectContainer(float posX)
     {
         containerId = Guid.NewGuid().ToString();    
         containerRect = new Rect(posX, 20f, 200f, 100f);
         myScriptableObjectList = new List<MyScriptableObject>();
     }
     public void DrawContainer()
     {
         float extraHeight = 0f;
         extraHeight += (myScriptableObjectList.Count * 20f);
         containerRect.height = defaultHeight + extraHeight;
         
         GUILayout.BeginVertical();
         if(GUILayout.Button("+",GUILayout.Width(190f))){AddObject();}
         for(int i = 0; i < myScriptableObjectList.Count; i++)
         {
             GUILayout.BeginHorizontal();
                 if(GUILayout.Button("X")){ DeleteObject(i); break; }
                 myScriptableObjectList[i] = (MyScriptableObject) EditorGUILayout.ObjectField(myScriptableObjectList[i],typeof(MyScriptableObject),false);
             GUILayout.EndHorizontal();
         }
         GUILayout.EndVertical();
     }
     void AddObject()
     {
         myScriptableObjectList.Add(null);
     }
     void DeleteObject(int index)
     {
         myScriptableObjectList.RemoveAt(index);
     }
 }


ObjectContainerList.cs

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 
 public class ObjectContainerList : ScriptableObject{
     
     [SerializeField] public List<ObjectContainer> containerList;
 }


ListEditor.cs

 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEditor;
 
 public class ListEditor : EditorWindow {
 
     private static List<ObjectContainer> containerList = new List<ObjectContainer>();
 
     string fileName = "ContainerListFile";
     string filePath = "Assets/Resources";
 
     [MenuItem("Window/Container List Editor")]
     static void ShowEditor()
     {
         ListEditor editor = EditorWindow.GetWindow<ListEditor>(false,"Container List Editor");
         editor.wantsMouseMove = true;
         editor.Show();
         EditorWindow.FocusWindowIfItsOpen<ListEditor>();
     }
     void OnGUI()
     {
         DrawContainers();
         DrawMenu();
         if (GUI.changed) Repaint();        
     }
     private void DrawMenu()
     {
         Rect menuBar = new Rect(0, 0, position.width, 20f);
         GUILayout.BeginArea(menuBar, EditorStyles.toolbar);
         GUILayout.BeginHorizontal();
         if (GUILayout.Button(new GUIContent("Save"), EditorStyles.toolbarButton)) Save();          
         if (GUILayout.Button(new GUIContent("Load"), EditorStyles.toolbarButton)) Load();
         if (GUILayout.Button(new GUIContent("Add Container"), EditorStyles.toolbarButton)) AddContainer();
         if (GUILayout.Button(new GUIContent("Delete Last Container"), EditorStyles.toolbarButton)) DeleteContainer();
         GUILayout.EndHorizontal();
         GUILayout.EndArea();
     }
     void DrawContainers()
     {
         BeginWindows();
         for(int i = 0; i < containerList.Count; i++)
         {    
             containerList[i].containerRect = GUI.Window(i, containerList[i].containerRect, Draw, "container "+i);
         }
         EndWindows();
     }
     void Draw(int index)
     {
         containerList[index].DrawContainer();
     }
     void AddContainer()
     {
         ObjectContainer newContainer = new ObjectContainer(containerList.Count * 200f);
         containerList.Add(newContainer);
     }
     void DeleteContainer()
     {
         if(containerList.Count!=0)containerList.RemoveAt(containerList.Count-1);
     }
 
     // the problem is probably an "unbroken reference" between 'containerList' (list that represents containers in the Editor),
     // and 'objectContainerListSO.containerList' (list of containers within the SO Asset)
     private void Save()
     {
         ObjectContainerList objectContainerListSO = ScriptableObject.CreateInstance<ObjectContainerList>();
         objectContainerListSO.containerList = new List<ObjectContainer>();
         
         foreach (var container in containerList)
         {
             objectContainerListSO.containerList.Add(container); //...this is probably where the unbroken-reference gets created
         }
         AssetDatabase.CreateAsset(objectContainerListSO, filePath + "/" + fileName + ".asset"); 
         AssetDatabase.SaveAssets();
     }
 
     private void Load()
     {
         ObjectContainerList objectContainerListSO = ScriptableObject.CreateInstance<ObjectContainerList>();
         objectContainerListSO = AssetDatabase.LoadAssetAtPath(filePath + "/" + fileName + ".asset", typeof(ObjectContainerList)) as ObjectContainerList;
         
         if(objectContainerListSO == null)Debug.LogError("File Not Found!");
         else
         {
             containerList = new List<ObjectContainer>();
 
             foreach (var container in objectContainerListSO.containerList)
             {
                 containerList.Add(container);//...or maybe here?
             }
         }
     }
 
 }
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

0 Replies

· Add your reply
  • Sort: 

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

103 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

Related Questions

Serializing a ScriptableObject without creating an asset for it? 2 Answers

ScriptableObject with Custom Editor resetting data in inspector 1 Answer

Storing and Retrieving Data from a ScriptableObject 1 Answer

CustomEditor for an ScriptableObject asset only works after recompile. 1 Answer

[Solved]How to serialize Dictionary with Unity Serialization System 6 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