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
4
Question by jeffsim · Apr 02, 2016 at 05:26 AM · inspectorscriptableobject

Can a ScriptableObject contain a List of ScriptableObjects?

I have a ScriptableObject ("monster defn list") which contains a list of objects ("monster defns") - when those contained objects are [Serializable], everything works fine; but if I instead make them ScriptableObjects, then the Inspector complains with "mismatched type" when viewing them. Should this work? Note that I'm on the 5.4 beta at the moment.

To see the issue; the following code creates a new Editor Window that can be opened with Control+Shift+m - click the "create list" button and then click the "create defn" button a few times. Select the created asset (in /Assets/MonsterDefnList) in the inspector, and notice that the defns look fine (as they are currently [serializable]). now delete that asset, and uncomment the first line of the code below and repeat; now the inspector will instead tell you "Type Mismatch" for those defns.

 // #define UseScriptableObject
 using UnityEditor;
 using UnityEngine;
 using System.Collections.Generic;
 
 #if UseScriptableObject
 public class MonsterDefn : ScriptableObject
 #else
 [System.Serializable]
 public class MonsterDefn
 #endif
 {
     public int MaxHP;
 }
 
 public class MonsterDefnList : ScriptableObject
 {
     public List<MonsterDefn> MonsterDefns;
 }
 
 public class MonsterDefnEditor : EditorWindow
 {
     public MonsterDefnList monsterDefnList;
 
     [MenuItem("Window/Monster Defn Editor  %#m")]
     static void Init()
     {
         EditorWindow.GetWindow(typeof(MonsterDefnEditor));
     }
 
     void OnEnable()
     {
         monsterDefnList = AssetDatabase.LoadAssetAtPath("Assets/MonsterDefnList.asset",
                             typeof(MonsterDefnList)) as MonsterDefnList;
     }
 
     void OnGUI()
     {
         if (GUILayout.Button("Create New MonsterDefn List")) {
             monsterDefnList = ScriptableObject.CreateInstance<MonsterDefnList>();
             monsterDefnList.MonsterDefns = new List<MonsterDefn>();
             AssetDatabase.CreateAsset(monsterDefnList, "Assets/MonsterDefnList.asset");
             AssetDatabase.SaveAssets();
         }
 
         if (GUILayout.Button ("Add MonsterDefn")) {
 #if UseScriptableObject
             MonsterDefn newMonsterDefn = ScriptableObject.CreateInstance<MonsterDefn>();
 #else
             MonsterDefn newMonsterDefn = new MonsterDefn ();
 #endif
             monsterDefnList.MonsterDefns.Add (newMonsterDefn);
         }
 
         if (GUI.changed) EditorUtility.SetDirty(monsterDefnList);
     }
 }





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
6
Best Answer

Answer by Bunny83 · Apr 02, 2016 at 05:42 AM

Yes, it is supported, however it won't work the way you use it. ScriptableObjects need to be explicitly saved as asset. They can't be serialized along with another ScriptableObject like "normal" serializable classes can. References to ScriptableObjects are actually serialized as asset references. That's why each ScriptableObject need to be an asset in your project.

Keep in mind that you can use AssetDatabase.AddObjectToAsset to add multiple assets into the same asset file. However due to the way Unity stores multiple assets in the same file it can happen that one of your "child" assets become the "main asset".

Comment
Add comment · Show 5 · 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 jeffsim · Apr 02, 2016 at 01:14 PM 0
Share

Thank you for your reply! I changed the code to use AddObjectToAsset, and sure enough ran into the child asset beco$$anonymous$$g the main asset; i.e. create the monsterdefnlist and add a few monsterdefns to it; then in the Project window expand the monsterdefnlist asset and (1) all of the child assets are of type monsterdefnlist (ins$$anonymous$$d of monsterdefn), and (2) the actual monsterdefnlist is the last child.

Am I misusing AddObjectToAsset? Here is the new approach:

 using UnityEditor;
 using UnityEngine;
 using System.Collections.Generic;
 
 public class $$anonymous$$onsterDefn : ScriptableObject {
     public int $$anonymous$$axHP;
 }
 
 public class $$anonymous$$onsterDefnList : ScriptableObject {
     public List<$$anonymous$$onsterDefn> $$anonymous$$onsterDefns;
 }
 
 public class $$anonymous$$onsterDefnEditor : EditorWindow {
     $$anonymous$$onsterDefnList monsterDefnList;
 
     [$$anonymous$$enuItem("Window/$$anonymous$$onster Defn Editor %#m")]
     static void Init() {
         EditorWindow.GetWindow(typeof($$anonymous$$onsterDefnEditor));
     }
 
     void OnEnable() {
         monsterDefnList = AssetDatabase.LoadAssetAtPath("Assets/$$anonymous$$onsterDefnList.asset",
                             typeof($$anonymous$$onsterDefnList)) as $$anonymous$$onsterDefnList;
     }
 
     void OnGUI() {
         if (GUILayout.Button("Create New $$anonymous$$onsterDefn List")) {
             monsterDefnList = ScriptableObject.CreateInstance<$$anonymous$$onsterDefnList>();
             monsterDefnList.$$anonymous$$onsterDefns = new List<$$anonymous$$onsterDefn> ();
             AssetDatabase.CreateAsset(monsterDefnList, "Assets/$$anonymous$$onsterDefnList.asset");
             AssetDatabase.SaveAssets();
         }
          
         if (GUILayout.Button ("Add $$anonymous$$onsterDefn")) {
             $$anonymous$$onsterDefn new$$anonymous$$onsterDefn = ScriptableObject.CreateInstance<$$anonymous$$onsterDefn>();
             monsterDefnList.$$anonymous$$onsterDefns.Add (new$$anonymous$$onsterDefn);
             AssetDatabase.AddObjectToAsset (new$$anonymous$$onsterDefn, monsterDefnList);
             AssetDatabase.SaveAssets();
         }
 
         if (GUI.changed) EditorUtility.SetDirty(monsterDefnList);
     } 
 }

nb: If anyone else tries does the above and gets stuck on the inspector giving a "The associated script cannot be loaded..." error when you view the asset after exiting/restarting; it looks like you need to break the above classes out into their own cs files.

avatar image Bunny83 jeffsim · Apr 02, 2016 at 01:33 PM 0
Share

$$anonymous$$eep in $$anonymous$$d that ScriptableObjects have the same limitation as $$anonymous$$onoBehaviours. They need to be defined in their own file with the filename matching the class name. Otherwise Unity can't associate the class with a script asset.

In your code it seems you have all classes in the same file?!

Since the Editor and EditorWindow class are also derived from ScriptableObject it's the same for those.

avatar image jeffsim · Apr 02, 2016 at 01:40 PM 0
Share

@bunny83 yeah, figured that out and was editing the Q as you responded; I had just included them in one file for the purposes of this question and didn't know about the limitation :). Thanks for pointing it out though! Do you have any insight into how to avoid the child/main asset issue (or why it's happening)? Thanks again!

avatar image Bunny83 jeffsim · Apr 03, 2016 at 03:58 AM 0
Share

As far as i know there's no way to avoid it. The problem is that there is no parent / child relationship between multiple assets inside a single assetfile. If you switch the asset serialization mode to "force text" the asset file will be serialized as yaml text. That means you can open the asset in a normal text editor. If you do you will see that all assets you've added are simply listed one after the other. Unity simply treats the first asset in the file as main asset.

I'm not sure why Unity reorders the assets in the file, but it seems to depend on certain hardcoded sorting rules. It's a bit annoying. It would be nice if there was a way to specify the order of the assets or at least if it would preserve the initial order. I haven't really looked into the problem and if the order can be changed. You could try using different classnames and see if that affects the order.

avatar image ifdef_ben jeffsim · Jun 02, 2017 at 03:57 PM 0
Share

Edit: Don't $$anonymous$$d this comment, I did not realized you already cracked the case on your answer bellow.

Original Comment: I don't know if it's still an issue for you. But I've found the parent/child reordering problem to be directly linked to the alphabetical order of the name of the assets. If your child's name comes before your parent's then the order will be bugged.

As a simple workaround the thing I do is adding the letter 'z' in front of all my children's name so that they always come last in the alphabetical order.

avatar image
2

Answer by jeffsim · Apr 03, 2016 at 05:08 PM

On a related note, I believe I figured out what's causing the parent/child relationship issue as well as a workaround. It actually has to do with the naming of the asset file vs. the name of the subassets (!). I've logged a bug with Unity, but here's a simple test case that (a) repros the issue and (b) shows how to work around it:

 using UnityEditor;
 using UnityEngine;
 using System.Collections.Generic;
 
 // note to viewer: Unity needs this to be in a separate file, otherwise it'll
 // fail to load; included in this file just for repro purposes.
 public class MyItemDefn : ScriptableObject 
 {
 }
 
 public class MyItemDefnList : ScriptableObject 
 {
     public List<MyItemDefn> Defns;
 }
 
 public class MyItemDefnEditor : EditorWindow
 {
     MyItemDefnList itemDefnList1;
     MyItemDefnList itemDefnList2;
 
     [MenuItem("Window/MyItemDefn Editor %#m")]
     static void Init()
     {
         EditorWindow.GetWindow<MyItemDefnEditor>();
     } 
 
     void OnGUI()
     {
         if (GUILayout.Button ("Run Test")) {            

             // The bug: When adding a ScriptableObject as a child of a ScriptableObject,
             //   the inspector can get confused about which is the parent and which
             //   is the child, and display the containing object as one of the children
             //
             // Repro'ed in: Unity 5.4.0b12 (beta), but it sounds like this has been
             //   around for a while.
             //
             // The reason this sometimes happens: If the filename of the main asset is
             //   alphabetically after the name of a child asset, then the inspector
             //   misidentifies which is which and the child asset appears as the main
             //   asset (+vice versa).
             //
             // To workaround: Name your main/containing asset's file something
             //   alphabetically before all child assets.  e.g.: append with "aaa"
             //
 
             // This one will fail; the main asset appears as the last asset in the container
             itemDefnList1 = ScriptableObject.CreateInstance<MyItemDefnList>();
             itemDefnList1.Defns = new List<MyItemDefn> ();
             createTestAsset ("zzzTestAsset", itemDefnList1);
 
             // This one will succeed; the main asset will appear as the parent asset
             itemDefnList2 = ScriptableObject.CreateInstance<MyItemDefnList>();
             itemDefnList2.Defns = new List<MyItemDefn> ();
             createTestAsset("aaaTestAsset", itemDefnList2);
 
             AssetDatabase.SaveAssets();
         }
 
         if (GUI.changed) {
             EditorUtility.SetDirty (itemDefnList1);
             EditorUtility.SetDirty (itemDefnList2);
         }
     }
 
     void createTestAsset(string assetName, MyItemDefnList itemDefnList)
     {
         // Save the container asset with the specified name
         string path = AssetDatabase.GenerateUniqueAssetPath ("Assets/"+assetName+".asset");
         AssetDatabase.CreateAsset(itemDefnList, path);
 
         // Create some test items and add them to the container asset.
         for (int i = 0; i < 5; i++)
         {
             MyItemDefn newMyItemDefn = ScriptableObject.CreateInstance<MyItemDefn> ();
             newMyItemDefn.name = "jjj " + i;
             itemDefnList.Defns.Add (newMyItemDefn);
             AssetDatabase.AddObjectToAsset (newMyItemDefn, itemDefnList);
         }
     }
 }

Pretty crazy, and a fun one to track down =). Thanks again to @bunny83!

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 ALKubo · Mar 03, 2017 at 01:11 AM 0
Share

I saved several hours of hard work. Thanks to you guys.

avatar image Edvard-D · Jul 19, 2017 at 09:52 AM 0
Share

Some people might find this useful: a StackOverflow question about characters that would sort last alphabetically and look cleaner than adding "z" at the end.

I've opted for using the character "一" at the front.

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

8 People are following this question.

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

Related Questions

Unity inspector field overlap with eachother 1 Answer

Why doesn't my ScriptableObject save using a custom EditorWindow? 3 Answers

Custom Inspector for ScriptableObject 1 Answer

Show multiple attached scripts variables in one script in the inspector 0 Answers

Referencing / linking a .asset / .prefab file in another .asset / .prefab file programmatically. 2 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