Search Unity

ScriptableObject with classes?

Discussion in 'Scripting' started by Dudledok, Apr 23, 2015.

  1. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    I haven't used Scriptable Objects before so maybe this is possible, and hopefully it is.

    A normal object can have the following (it will look familiar if you have used the standard controller assets):

    Code (CSharp):
    1. public class Controller: MonoBehaviour
    2. {
    3.     [Serializable]
    4.     public class MovementSettings
    5.     {
    6.         public float ForwardSpeed = 8.0f;   // Speed when walking forward
    7.         public float BackwardSpeed = 4.0f;  // Speed when walking backwards
    8.     }
    What's great about this is it makes the properties of this class really clear as to what they are without needing to name each one with long names, but also the inspector kindly collapses each sub class.

    Unfortunately doing this in a Scriptable Object doesn't seem to be possible because the properties don't show in the inspector. For the following you can only set the name:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Name : ScriptableObject
    6. {
    7.     public string Name;
    8.  
    9.     public class Sounds
    10.     {
    11.         public AudioClip[] Ambient;
    12.         public AudioClip[] OneShots;
    13.     }
    14. }
     
  2. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    ScriptableObject is for storing data only and not something that you can attach to a GameObject. That means you will get no variable throughput in the inspector.

    http://docs.unity3d.com/ScriptReference/ScriptableObject.html

    So, unfortunately I'm going to have to say it isn't possible. If you want to attach this script to a GameObject it has to inherit from MonoBehaviour

    Hope that clears it up!
     
  3. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    Can you give me more details on exactly what you are trying to achieve. I have a lot of experience with custom editors and may be able to point you in the right direction.
     
  4. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    So I don't want to attach it to an object. Basically I want to create an Environment class or something similar which holds properties such as which objects, textures, sounds etc to use. Then in my environment generator I will have an array of possible environments to choose from. I create instances of the scriptable object for each biome. It works great other than it looks messy.
     
  5. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    So you're wanting to clean up the inspector then? I can definitely help you out with a custom editor script. I'm gonna need more information though.
     
  6. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    You know what? This does the trick:

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using System.Collections;
    4. public class Name : ScriptableObject
    5. {
    6.     public string Name;
    7.     public SoundEffects Sounds;
    8. }
    9. [Serializable]
    10. public class SoundEffects
    11. {
    12.     public AudioClip[] Ambient;
    13.     public AudioClip[] OneShots;
    14. }
    15.  
    By creating the classes outside the scriptable object
     
  7. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    Perfect. Glad you figured it out! If you need any help in the future feel free to get at me via my website or my Twitter account: Links in my signature. Best of luck in all your future endeavours!
     
    Dudledok likes this.
  8. blizzy

    blizzy

    Joined:
    Apr 27, 2014
    Posts:
    775
    Not quite. The "trick" was to declare your "Sounds" member.
     
    hamsterbytedev likes this.
  9. hamsterbytedev

    hamsterbytedev

    Joined:
    Dec 9, 2014
    Posts:
    353
    Exactly that.
     
  10. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,706
    And to make it Serializable. You could have kept SoundEffects inside the ScriptableObject definition:
    Code (csharp):
    1. using System;
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Name : ScriptableObject
    6. {
    7.     public string Name;
    8.  
    9.     //[Serializable] //<-- Need this line to make it show in the inspector.
    10.     // ^ edit: sorry, wasn't paying attention. This is a ScriptableObject. Don't need this.
    11.     public class SoundEffects
    12.     {
    13.         public AudioClip[] Ambient;
    14.         public AudioClip[] OneShots;
    15.     }
    16.  
    17.     public SoundEffects Sounds; //<-- And this line to declare the variable.
    18. }
     
    Last edited: Apr 28, 2015
  11. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,537
    I think his goal was to get it outside of the script so he can keep the main class tidy and just reference the other classes when he needs them.

    I use this idea myself.. it makes access to the variables a little bit longer but it works fine. If you have *lots* of data this is much better than stuffing it all in the main class.
     
  12. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,706
    I do this, too. I was replying to his original issue, which was:
    The issue was that the class wasn't marked Serializable, and it will missing a variable declaration.
     
  13. LaneFox

    LaneFox

    Joined:
    Jun 29, 2011
    Posts:
    7,537
    Ah i missed that. Too many donuts and not enough coffee.
     
  14. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,706
    You bring up a great point, though. Nesting classes inside classes can get really messy. It's usually better to keep them separate like you say.
     
  15. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    Sorry but this is wrong. The way I am doing it feels like a trick because I have to create the class outside and declare a variable of that type inside.

    I posted the first block of code for a reason: This shows you can create a subclass MovementSettings so that if you have an object of type Controller and want to access the forward movement speed you do "ControllerObject.MovementSettings.ForwardSpeed"

    See above.

    With a ScriptableObject it serializes anyway so didn't think it would be necessary to specify it but I did try with and without marking it serializable anyway.
     
  16. blizzy

    blizzy

    Joined:
    Apr 27, 2014
    Posts:
    775
    You wrote "this did the trick" and stated that you just put the class outside of its enclosing class. But this actually doesn't do anything to your ScriptableObject subclass. What actually "did the trick" was to put a member "public SoundEffects Sounds" into your ScriptableObject subclass - at all. I was just pointing that out, and I don't think I am wrong here :)
     
    TonyLi likes this.
  17. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    My point is that you don't need a member "public SoundEffects Sounds" when NOT using a ScriptableObject, you just declare it at serializible and then you immediately can start setting those values in the inspector. In the first block of code I posted that works perfectly (it's from the FPSController script) WITHOUT declaring a member. I wanted to be able to set the values in the inspector in a tidy way, and that's 'the trick' that can be achieved with this alternative method when using a ScriptableObject.

    Is there a way to achieve this in the same way that it can be achieved when not using a ScriptableObject?
     
  18. TonyLi

    TonyLi

    Joined:
    Apr 10, 2012
    Posts:
    12,706
    If you don't declare a public member variable, there's nothing to be serialized; you can't just define a type without declaring a variable. Is the member variable of type MovementSettings declared somewhere else in Controller perhaps?
     
    Dudledok likes this.
  19. Dudledok

    Dudledok

    Joined:
    Oct 24, 2013
    Posts:
    110
    This is what I thought and realise now to be true. Yes it was an oversight on my behalf that further down the script it is declared. No magic trick here then, my bad!