C# 在Unity C中序列化类似ObservableCollection的类ObservableList#

C# 在Unity C中序列化类似ObservableCollection的类ObservableList#,c#,unity3d,C#,Unity3d,由于到目前为止还不支持在Unity中序列化C#的observativeCollection,因此我使用一个脚本扩展List,并创建一个名为observativeList的可序列化类,如Unity论坛的回答中所述。以下是相同的代码: [Serializable] public class ObservedList<T> : List<T> { public event Action<int> Changed = delegate { };

由于到目前为止还不支持在Unity中序列化C#的
observativeCollection
,因此我使用一个脚本扩展
List
,并创建一个名为
observativeList
的可序列化类,如Unity论坛的回答中所述。以下是相同的代码:

[Serializable]
 public class ObservedList<T> : List<T>
 {
     public event Action<int> Changed = delegate { };
     public event Action Updated = delegate { };
     public new void Add(T item)
     {
         base.Add(item);
         Updated();
     }
     public new void Remove(T item)
     {
         base.Remove(item);
         Updated();
     }
     public new void AddRange(IEnumerable<T> collection)
     {
         base.AddRange(collection);
         Updated();
     }
     public new void RemoveRange(int index, int count)
     {
         base.RemoveRange(index, count);
         Updated();
     }
     public new void Clear()
     {
         base.Clear();
         Updated();
     }
     public new void Insert(int index, T item)
     {
         base.Insert(index, item);
         Updated();
     }
     public new void InsertRange(int index, IEnumerable<T> collection)
     {
         base.InsertRange(index, collection);
         Updated();
     }
     public new void RemoveAll(Predicate<T> match)
     {
         base.RemoveAll(match);
         Updated();
     }


     public new T this[int index]
     {
         get
         {
             return base[index];
         }
         set
         {
             base[index] = value;
             Changed(index);
         }
     }
 }
[可序列化]
公共类观察列表:列表
{
公共事件操作已更改=委托{};
公共事件操作更新=委托{};
新增公共作废(T项)
{
基础。添加(项目);
更新();
}
公共新作废删除(T项)
{
底座。移除(项目);
更新();
}
public new void AddRange(IEnumerable集合)
{
base.AddRange(集合);
更新();
}
公共新无效删除范围(整数索引、整数计数)
{
基本参数(索引、计数);
更新();
}
公共新空白清除()
{
base.Clear();
更新();
}
公共新空白插入(整数索引,T项)
{
基础。插入(索引,项目);
更新();
}
public new void InsertRange(int索引,IEnumerable集合)
{
base.InsertRange(索引、集合);
更新();
}
public new void RemoveAll(谓词匹配)
{
基础。移除所有(匹配);
更新();
}
公共新T此[int索引]
{
得到
{
返回基数[指数];
}
设置
{
基准[指数]=数值;
变更(索引);
}
}
}
它仍然没有被序列化,我无法在Unity编辑器中看到它。任何帮助都将不胜感激

编辑#1

预期用例:

public class Initialize : MonoBehaviour
{
    public ObservedList<int> percentageSlider = new ObservedList<int>();
    
    void Start()
    {
        percentageSlider.Changed += ValueUpdateHandler;
    }
    
    void Update()
    {
    }
    
    private void ValueUpdateHandler(int index)
    {
        // Some index specific action
        Debug.Log($"{index} was updated");
    }
}
公共类初始化:单行为
{
public ObservedList percentageSlider=新建ObservedList();
void Start()
{
percentageSlider.Changed+=ValueUpdateHandler;
}
无效更新()
{
}
私有void ValueUpdateHandler(int索引)
{
//一些指标具体行动
Log($“{index}已更新”);
}
}
我将这个脚本作为一个组件附加到一个游戏对象上,这样我就可以输入这个列表的
大小
,处理这些值(就像我可以处理
列表
),并执行一些操作,这些操作只有在
可观察列表
中的某个值更新时才会触发

我想看什么

我所看到的

  • 如果您希望序列化操作,那么必须使用UnityEvents(它连接到Unity的序列化事件系统)

  • 确保在所有要序列化的类型之前使用[SerializedField]属性

  • 主要的问题可能来自继承,这与Unity的序列化系统不匹配。一般来说,我永远不会从列表中继承,相反,我会使您的可观察类型成为列表的包装器,并添加一些特性(列表将是可观察中的一个成员)

  • 编辑1 添加了一个测试代码示例。 ObservedList不是继承,而是充当列表的包装器。 您可以订阅已更改和更新的事件,但它们不是序列化的。如果您想访问任何其他列表功能,只需在ObservedList类中添加一个公共方法,作为其包装器。如果您有任何其他问题,请告诉我

    [Serializable]
    public class ObservedList<T>
    {
        public event Action<int> Changed;
        public event Action Updated;
        [SerializeField] private List<T> _value;
        
        public T this[int index]
        {
            get
            {
                return _value[index];
            }
            set
            {
                //you might want to add a check here to only call changed
                //if _value[index] != value
                _value[index] = value;
                Changed?.Invoke(index);
            }
        }
        
        public void Add(T item)
        {
            _value.Add(item);
            Updated?.Invoke();
        }
        public void Remove(T item)
        {
            _value.Remove(item);
            Updated?.Invoke();
        }
        public void AddRange(IEnumerable<T> collection)
        {
            _value.AddRange(collection);
            Updated?.Invoke();
        }
        public void RemoveRange(int index, int count)
        {
            _value.RemoveRange(index, count);
            Updated?.Invoke();
        }
        public void Clear()
        {
            _value.Clear();
            Updated?.Invoke();
        }
        public void Insert(int index, T item)
        {
            _value.Insert(index, item);
            Updated?.Invoke();
        }
        public void InsertRange(int index, IEnumerable<T> collection)
        {
            _value.InsertRange(index, collection);
            Updated?.Invoke();
        }
        public void RemoveAll(Predicate<T> match)
        {
            _value.RemoveAll(match);
            Updated?.Invoke();
        }
    }
    
    [可序列化]
    公共类观察列表
    {
    公共事件行动改变;
    更新公共活动行动;
    [SerializeField]私有列表\u值;
    公共T此[int索引]
    {
    得到
    {
    返回_值[索引];
    }
    设置
    {
    //您可能需要在此处添加一个复选框,以仅更改呼叫
    //如果_值[索引]!=值
    _值[索引]=值;
    已更改?.Invoke(索引);
    }
    }
    公共作废新增(T项)
    {
    _增值(项目);
    已更新?.Invoke();
    }
    公共作废删除(T项)
    {
    _价值。删除(项目);
    已更新?.Invoke();
    }
    公共void AddRange(IEnumerable集合)
    {
    _value.AddRange(集合);
    已更新?.Invoke();
    }
    公共无效删除范围(整数索引、整数计数)
    {
    _移除范围(索引、计数);
    已更新?.Invoke();
    }
    公共空间清除()
    {
    _value.Clear();
    已更新?.Invoke();
    }
    公共空白插入(整数索引,T项)
    {
    _插入(索引,项目);
    已更新?.Invoke();
    }
    公共void InsertRange(int索引,IEnumerable集合)
    {
    _value.InsertRange(索引、集合);
    已更新?.Invoke();
    }
    public void RemoveAll(谓词匹配)
    {
    _value.RemoveAll(匹配);
    已更新?.Invoke();
    }
    }
    
  • 如果您希望序列化操作,那么必须使用UnityEvents(它连接到Unity的序列化事件系统)

  • 确保在所有要序列化的类型之前使用[SerializedField]属性

  • 主要的问题可能来自继承,这与Unity的序列化系统不匹配。一般来说,我永远不会从列表中继承,相反,我会使您的可观察类型成为列表的包装器,并添加一些特性(列表将是可观察中的一个成员)

  • 编辑1 添加了一个测试代码示例。 ObservedList不是继承,而是充当列表的包装器。 您可以订阅已更改和更新的事件,但它们不是序列化的。如果您想访问任何其他列表功能,只需在ObservedList类中添加一个公共方法,作为其包装器。如果您有任何其他问题,请告诉我

    [Serializable]
    public class ObservedList<T>
    {
        public event Action<int> Changed;
        public event Action Updated;
        [SerializeField] private List<T> _value;
        
        public T this[int index]
        {
            get
            {
                return _value[index];
            }
            set
            {
                //you might want to add a check here to only call changed
                //if _value[index] != value
                _value[index] = value;
                Changed?.Invoke(index);
            }
        }
        
        public void Add(T item)
        {
            _value.Add(item);
            Updated?.Invoke();
        }
        public void Remove(T item)
        {
            _value.Remove(item);
            Updated?.Invoke();
        }
        public void AddRange(IEnumerable<T> collection)
        {
            _value.AddRange(collection);
            Updated?.Invoke();
        }
        public void RemoveRange(int index, int count)
        {
            _value.RemoveRange(index, count);
            Updated?.Invoke();
        }
        public void Clear()
        {
            _value.Clear();
            Updated?.Invoke();
        }
        public void Insert(int index, T item)
        {
            _value.Insert(index, item);
            Updated?.Invoke();
        }
        public void InsertRange(int index, IEnumerable<T> collection)
        {
            _value.InsertRange(index, collection);
            Updated?.Invoke();
        }
        public void RemoveAll(Predicate<T> match)
        {
            _value.RemoveAll(match);
            Updated?.Invoke();
        }
    }
    
    [可序列化]
    公共类观察列表
    {
    公共事件行动
    
    public class Example : MonoBehaviour
    {
        public ObservedList<int> percentageSlider = new ObservedList<int>();
    
        private void Start()
        {
            percentageSlider.Changed += ValueUpdateHandler;
    
            // Just as an example
            percentageSlider[3] = 42;
        }
    
        private void ValueUpdateHandler(int index, int oldValue, int newValue)
        {
            // Some index specific action
            Debug.Log($"Element at index {index} was updated from {oldValue} to {newValue}");
        }
    }
    
    public abstract class ObservedList { }
    
    [Serializable]
    public class ObservedList<T> : ObservedList, IList<T>
    {
       ...
    }
    
    [CustomPropertyDrawer(typeof(ObservedList), true)]
    public class ObservedListDrawer : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            // NOTE: Now here is the dirty hack
            // even though the ObservedList itself doesn't have that property
            // we can still look for the field called "_list" which only the 
            // ObservedList<T> has
            var list = property.FindPropertyRelative("_list");
            EditorGUI.PropertyField(position, list, label, true);
        }
    
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            var list = property.FindPropertyRelative("_list");
    
            return EditorGUI.GetPropertyHeight(list, label, true);
        }
    }
    
    [Serializable]
    public class ObservedListInt : ObservedList<int>{ }
    
    public class Example : MonoBehaviour
    {
        public ObservedListInt percentageSlider = new ObservedListInt();
    
        private void Start()
        {
            percentageSlider.Changed += ValueUpdateHandler;
    
            // Just as an example
            percentageSlider[3] = 42;
        }
    
        private void ValueUpdateHandler(int index, int oldValue, int newValue)
        {
            // Some index specific action
            Debug.Log($"Element at index {index} was updated from {oldValue} to {newValue}");
        }
    }