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. Dismiss Notice

Serialization callback for null references

Discussion in 'Scripting' started by Lost-in-the-Garden, Mar 17, 2022.

  1. Lost-in-the-Garden

    Lost-in-the-Garden

    Joined:
    Nov 18, 2015
    Posts:
    176
    Hello!

    I have a ScriptableObject:
    Code (CSharp):
    1.     public class MySO : ScriptableObject
    2.     {
    3.         ...
    4.     }
    What I am looking for is a way to trigger a callback whenever a reference of that type is serialized even and especially when the reference is null:
    Code (CSharp):
    1.     public class MyBehaviour : MonoBehaviour
    2.     {
    3.         public MySO mySO; //callback then this is serialized
    4.     }
    So whenever mySO is serialized i want that callback fired. Especially when the reference is empty.

    That should work wherever I add an reference to MySO. So I do not want to implement the callback on MyBehaviour. because that would mean i have to do that on all classes with a reference to MySO which defeats the purpose.

    It would be ok if it is an Attribute on either MySO or if not possible in any other way on the field in MyBehaviour. Or an Interface i add to MySO.

    Has anyone ever tried that?

    Thanks!
    Matthias
     
  2. _met44

    _met44

    Joined:
    Jun 1, 2013
    Posts:
    633
  3. Lost-in-the-Garden

    Lost-in-the-Garden

    Joined:
    Nov 18, 2015
    Posts:
    176
    Hello!

    Well I would not see how to use it in this context. I could put the interface on either the MonoBehaviour or the ScriptableObject.

    When I put it on the ScriptableObject, it's not called when the reference is null, so that doesn't help. And putting it on the MonoBehaviour does not fulfill the goal. I would have to implement the callback receiver on every class that has a reference to the Scriptable object. Which is exactly what I want to avoid.

    Matthias
     
  4. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,524
    You can't as what you have in mind has absolutely nothing to do with the "MySO" class itself as the field inside the MonoBehaviour is just a serialized asset reference which allows null values. It is serialized inside the MonoBehaviour class, so it has absolutely nothing to do with your scriptable object.

    However you can use a custom serializable class wrapper instead of a direct reference to your MySO class. That way you can implement the ISerializationCallbackReceiver on that wrapper.

    Code (CSharp):
    1. [System.Serializable]
    2. public class MySORef, ISerializationCallbackReceiver
    3. {
    4.     public MySO value;
    5.     // implement the callbacks here.
    6. }
    Note that you probably want to use a property drawer for your MySORef class so it is drawn inline just like an ordinary reference

    Code (CSharp):
    1. [CustomPropertyDrawer(typeof(MySORef))]
    2. public class MySORefPropertyDrawer : PropertyDrawer
    3. {
    4.     public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    5.     {
    6.         return EditorGUI.GetPropertyHeight(property.FindPropertyRelative("value"), false);
    7.     }
    8.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    9.     {
    10.         EditorGUI.PropertyField(position, property.FindPropertyRelative("value"), label, false);
    11.     }
    12. }
    13.  
    This would draw the reference just the same way a normal MySO reference would have been drawn.

    Of course whenever you want to store a reference to a MySO object you would use the MySORef class instead. When you want to use the reference you have to access the "value" of that class. You could implement an implicit conversion operator, however that wouldn't really cover all cases and may be misleading, so I would not recommend it.

    edit

    Note that you could also use just a PropertyDrawer with an attribute. However there are many edge cases you have to take care of, especially when you want to actively edit / manupulate the reference. Attributes could also be attached to other fields which would probably break the code in your propertydrawer.

    Be warned that "whenever the field is serialized" does happen way more often than you may think. Inside the Unity editor, Unity would serialize the field everytime the inspector is redrawn. So at play more that usually means at least once every frame. So be careful what you do inside the callback. Note that the ISerializationCallbackReceiver callbacks are executed on another thread. So you are limited what you can do in the callback. Since you haven't told us even slightest hint why you need this, you have to judge yourself.
     
    Last edited: Mar 18, 2022
    Lost-in-the-Garden likes this.