codewriter-packages / Tri-Inspector

Free inspector attributes for Unity [Custom Editor, Custom Inspector, Inspector Attributes, Attribute Extensions]
MIT License
972 stars 47 forks source link

SerializeReference not applying changes to disk #166

Closed somedeveloper00 closed 3 months ago

somedeveloper00 commented 4 months ago

Describe the bug SerializeReference not applying changes to disk. I have some sort of data structure like the following

 [Serializable]
 public struct Item
 {
     public LocalizedString name;
     public LocalizedString description;
     public Sprite icon;
     [SerializeField] private EntityStats baseStats;
     [SerializeReference] private EntityEnhancement[] level2Enhancements;
     [SerializeReference] private EntityEnhancement[] level3Enhancements;
     [SerializeReference] private EntityEnhancement[] level4Enhancements;
...

and neither of the SerializeReference fields get saved to disk after Ctrl+S.

Expected behavior Save to disk.

Code Sample

public sealed partial class TroopsManifest : ManifestScriptableObject<TroopsManifest.Item>
{
  // we'll have some TroopsManifest.Item[] in the base class

   [Serializable]
   public struct Item
   {
     public LocalizedString name; // saves to disk
     public LocalizedString description; // saves to disk
     public Sprite icon; // saves to disk
     [SerializeField] private EntityStats baseStats; // saves to disk
     [SerializeReference] private EntityEnhancement[] level2Enhancements; // DOESNT save to disk
     [SerializeReference] private EntityEnhancement[] level3Enhancements; // DOESNT save to disk
     [SerializeReference] private EntityEnhancement[] level4Enhancements; // DOESNT save to disk
  }
}

Screenshots image

Desktop: Unity version: Tri Inspector version: latest to this date

somedeveloper00 commented 4 months ago

Found the reason! My stupid ass didn't know SerializeReference on private fields aren't enough to serialize the fields. We need SerializeField and SerializeReference together if the field's private. So TriInspector should similarly not display the SerializeReference fields that are private and don't have SerializeField attribute.

I won't change this bug report's title, for archival reasons, but the correct title for this bug should be TriInspector shouldn't display private fields with SerializeReference attribute but without SerializeField attribute

Thank you for reading. I think this should be an easy fix; I'll try to see if I can do it myself and do a PR

somedeveloper00 commented 4 months ago

update: upon further testing, it turns out the problem isn't relevant to SerializeField at all. It's that when we use SerializeReference in a list or an array, the base type must be Serializable, otherwise Unity won't serialize it. It's a strange behavior and even Odin inspector has got it wrong :)

So, here's the deal with SerializeReference serialization check:

if is array or list:
    if base type has Serializable attribute:
        return true
    else:
        return false
else:
        return true

I checked it with the following test

using System;
using System.Collections.Generic;
using UnityEngine;

public sealed class Test : MonoBehaviour
{
    public abstract class NonSerializableBase { }

    [Serializable]
    public sealed class NonSerializableSub : NonSerializableBase
    {
        public int a;
    }

    [Serializable]
    public abstract class SerializableBase { }

    [Serializable]
    public sealed class SerializableSub : SerializableBase
    {
        public int b;
    }

    [SerializeReference] private NonSerializableBase nonSerializableBase;
    [SerializeReference] private SerializableBase serializableBase;
    [SerializeReference] private NonSerializableBase[] nonSerializableBaseArr;
    [SerializeReference] private SerializableBase[] serializableBaseArr;
    [SerializeReference] private List<NonSerializableBase> nonSerializableBaseList;
    [SerializeReference] private List<SerializableBase> serializableBaseList;
    [SerializeReference, SerializeField] private NonSerializableBase[] nonSerializableBaseArrSf;
    [SerializeReference, SerializeField] private SerializableBase[] serializableBaseArrSf;
    [SerializeReference, SerializeField] private List<NonSerializableBase> nonSerializableBaseListSf;
    [SerializeReference, SerializeField] private List<SerializableBase> serializableBaseListSf;
}

where the following fields will be serialized: