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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Saving reference to C# class in ScriptableObject

Discussion in 'Scripting' started by RIw, Mar 24, 2018.

  1. RIw

    RIw

    Joined:
    Jan 2, 2015
    Posts:
    90
    Hello, I'm making a some kind of 'Database' in ScriptableObject which is being edited in Edit Mode and being used in Play Mode.

    I'm storing in the ScriptableObject an array of Data class. In Data class I have a field of IEffect interface. I have written 3 different implementations of it, and now I want my ScriptableObject to remember them, when they are attached to Data class member. And It works as I want, but when i hit play button or restart the Unity, the IEffect data from Data class is being lost. Can you help me with this ?
     
  2. GroZZleR

    GroZZleR

    Joined:
    Feb 1, 2015
    Posts:
    3,201
  3. RIw

    RIw

    Joined:
    Jan 2, 2015
    Posts:
    90
    @GroZZleR I actually have it done, but the problem is still here. I was using ScriptableObjects multiple times but now I'm having problem with introducing interfaces to this.
     
  4. eses

    eses

    Joined:
    Feb 26, 2013
    Posts:
    2,637
    @Rlw - Is your Data class a normal C# class or SO?

    I'm asking since it might be caused by this.
     
  5. JoshuaMcKenzie

    JoshuaMcKenzie

    Joined:
    Jun 20, 2015
    Posts:
    897
    the problem is that you are storing them as interface references. System.Serializable won't help here. to serialize custom classes in the inspector you must have a concrete reference to them, not an interface|abstracted reference.

    the problem is interfaces are contracts of behaviour, not state. serialization saves and loads state, not behaviour.

    There's a couple workarounds you can try:
    • You can manually serialize your custom classes to a format, attach an id to that data and serialize it again to file. then you're scriptableobjects implement ISerializationCallbackReceiver and a hidden,serialized field to store the ID. Then inside OnBeforeSerialize() and OnAfterDeserialize() calls, you take the reference inside IEffect field and serialize/deserialize the custom class to file and just have the ScriptableObject serialize the ID used instead (this is basically how Unity does it for UnityObjects). its actually more work than it sounds cause you also have to watch out for reference splitting (two fields, possibly from two classes, which were referencing the same instance before serialization, are now each referencing two separate instances after deserialization), and infinite recursion (where objects reference themselves or indirectly reference back to themselves, like Circularly linked lists).
    • Instead of referencing as an Interface, reference as an abstract ScriptableObject (which implements said interface). and have your IEffect classes derived from said abstract ScriptableObject. keeping it as a ScriptableObject reference will allow you the polymorphism that you want with the serialization that you need (as unity will automatically serialize them for you. This option is far less work and bug-prone.
     
  6. RIw

    RIw

    Joined:
    Jan 2, 2015
    Posts:
    90
    @eses They are normal classes without anything special, just a method, and a script component that is being attached at runtime.

    @JoshuaMcKenzie I also thought about storing the Implementation's name prefix in a string such as "Slow" for "SlowEffect" class. Then I could use reflection or something to retrieve the concrete implementation at runtime.
    I could have some singleton component that takes the string, and returns implementation.