Search Unity

Question The question of useless fields in Unity serialization

Discussion in 'Scripting' started by Yuze_75, Feb 28, 2024.

  1. Yuze_75

    Yuze_75

    Joined:
    Mar 14, 2022
    Posts:
    12
    In my game development, for the convenience of debugging, I implemented some special classes to serialize data structures (such as dictionaries, object, etc.) and display them on the Inspector panel But this display is only for the convenience of viewing data on the Inspector panel, not for serializing data. These data structures will only be generated during game runtime For example:
    Code (CSharp):
    1. #nullable enable
    2. using System;
    3. using UnityEngine;
    4.  
    5. /// <summary>
    6. /// Used to display interface types inherited from UnityObject
    7. /// </summary>
    8. [Serializable]
    9. public struct UnityObjectInterface<T> where T : class
    10. {
    11.     public UnityObjectInterface(T value) : this() => Value = value;
    12.  
    13.     [SerializeField] private UnityEngine.Object? value;
    14.  
    15.     public T? Value
    16.     {
    17.         get
    18.         {
    19.             if (value != null && value is T t) return t;
    20.  
    21.             value = null;
    22.             return null;
    23.         }
    24.         set
    25.         {
    26.             if (value is UnityEngine.Object o) this.value = o;
    27.         }
    28.     }
    29. }
    30.  
    31. public interface ITestInterface
    32. {
    33.     void Tick();
    34. }
    35.  
    36. public class TestMono : MonoBehaviour
    37. {
    38. #if UNITY_EDITOR
    39.     [SerializeField] private UnityObjectInterface<ITestInterface> testInterface;
    40.  
    41.     public ITestInterface? TestInterface
    42.     {
    43.         get => testInterface.Value;
    44.         set => testInterface.Value = value;
    45.     }
    46.  
    47. #else
    48.     public ITestInterface? TestInterface { get; set; }
    49. #endif
    50.  
    51.     private void Awake()
    52.     {
    53.         TestInterface = GetComponent<ITestInterface>();
    54.     }
    55.  
    56.     private void Update()
    57.     {
    58.         TestInterface?.Tick();
    59.     }
    60. }
    61.  
    But there is an question here. In editor mode, Unity will serialize many additional data structures according to the script in the editor model, but these data structures are meaningless at runtime, resulting in some useless fields in the. asset file, such as:
    upload_2024-2-28_18-35-7.png

    upload_2024-2-28_18-35-48.png
    (the first is the. asset file in editor mode; the second is the. asset file that should appear in runtime mode)


    I know keeping these meaningless fields in the. asset file is to prevent a script change from causing all objects to be modified But I have two questions about this useless field


    1. Because I extensively use this type of serialized display object in my game (of course, they are all empty), will these useless fields affect performance during serialization and deserialization?


    2. If such useless fields will have a certain impact on performance, I would also like to ask, will all files be checked during Unity packaging, and will useless fields be removed before packaging?
     
    xucian likes this.
  2. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,911
    I mean if you don't need this data at runtime why serialise it into the asset in the first place? If you want to view data in a certain way for convenience, use a custom inspector for that instead.
     
    Spy-Master likes this.
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,335
    Yes. More stuff to serialize/deserialize => more work => less performance. How much less is completely dependent on how many of these that's around.

    What do you mean by "packaging" here? Builds?
    Anyway, it depends. For more recent versions of Unity (at least 2021 or later), you can have serialized fields behind
    #if UNITY_EDITOR
    , and the data from those fields will not be included in builds. In earlier versions that caused bad errors due to a different set of fields existing when building and running.

    So in your example, the serialized data will not exist, so the size of your TestMono will be lower in builds than in editor, and accessing your TestInterface will be faster in builds than in editor due to one less indirection.



    That being said, this level on genericism and abstraction is often not a very good idea, and will lead to very simple things being very convoluted and hard to understand. So consider if just referencing the things you need without a
    InterfaceWrapper?
    is a lot better for everyone involved - yourself, the reader, and the computer. It probably is.
     
    Sluggy likes this.
  4. Yuze_75

    Yuze_75

    Joined:
    Mar 14, 2022
    Posts:
    12
    I know you can customize the Inspector panel, but for example, in the example in my code above, I would like to see which UnityObject my interface references. This cannot be done by the default mechanism of Unity I cannot accomplish this by simply rewriting a PropertyDrawer Rewriting the UnityObject Editor to display objects in these interfaces would be too cumbersome (as it requires handling display issues in nested classes)

    Although using plugins such as Odin can easily solve these problems, these plugins are all paid for
     
  5. Yuze_75

    Yuze_75

    Joined:
    Mar 14, 2022
    Posts:
    12
    My previous expression did have some issues, and my second question is about after building

    And thank you very much for your answer. I will consider simplifying or avoiding using such wrapper class in the future
     
  6. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,911
    Worth the money, mind you.
     
    CodeRonnie likes this.
  7. Lekret

    Lekret

    Joined:
    Sep 10, 2020
    Posts:
    358
    There's completely free and open-source TriInspector which has essentially the same functionality as Odin when it comes to attributes and validation. upload_2024-2-29_10-31-41.png
     
    Nad_B likes this.
  8. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,998
    You should not serialize data to runtime asset files which you would like to exclude in a build. This can cause many other issues as well as the data layout of the serialized data suddenly changes.

    So instead of serializing data you don't actually want to be serialized, you may just want to implement a property drawer which you can simply add to any actual serialized field to add arbitrary unrelated gui to your inspector. You can use custom PropertyAttributes to provide context (like the name of a field) and use reflection in the drawer to do whatever you want. That way you don't add anything to the serialized data.

    On the other hand, when you're working with interfaces, you might want to check out my SerializableInterface struct. It actually allows you to drag and drop objects (Monobehaviours or ScriptableObjects) which implement the given interface and have that reference serialized. That way you don't need to use any GetComponent call but you do have direct access to the interface. The getter does do the casting on the first access for you.

    Note that usually a DecoratorDrawer would be better, however Unity unfortunately doesn't provide any context (SerializedProperty) to a decorator which makes decorators almost useless as they have no way to access the serialized class or property. However PropertyDrawers could simply be chained. So a PropertyDrawer can actually use EditorGUI.PropertyField itself to draw the next drawer so the actual field isn't affected. You should need to manage your property height accordingly to include your "decoration". Of course another approach would be a generic custom inspector which does react to your own attributes or directly handles the display of certain things that aren't serialized.

    As others have suggested, things like Odin do exactly those things. Of course you can always roll your own approach, but getting it right takes quite some time, knowledge and skill.

    Messing with serialized data that shouldn't be serialized in the first place is never a good approach, though.
     
  9. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,911
    This was true in the past, but not any more. It's been supported for a while now.

    In fact in the docs for script serialisation, it notes it's both supported, and the data is not included in builds: https://docs.unity3d.com/Manual/script-Serialization.html

    So the answer to this thread is... just surrounded it in
    #if UNITY_EDITOR
    processors and bob's your uncle.
     
    Bunny83 likes this.
  10. xucian

    xucian

    Joined:
    Mar 7, 2016
    Posts:
    846
    may I present you Runtime Inspector
    it's a free asset that I heavily used for our game editor, which is really complex. you should be able to configure it to only show what you need, I don't know if new versions have more granular display filtering options, as I have an older one, but that's where I'd start.

    it's not mine, just kudoing the author
     
    Bunny83 likes this.