unity3d 如何序列化HashSet以便在Inspector中使用

au9on6nz  于 2023-03-03  发布在  其他
关注(0)|答案(2)|浏览(359)

Unity版本:2017.3.1f1
我正尝试在Unity检查器中使用HashSet。具体来说,我需要一些MonoBehaviour来拥有HashSet字段,这些字段可以通过检查器修改其内容。
为了实现这个目标,我创建了一个具体的类,它将HashSet作为子类,并在内部使用List进行序列化(反序列化),其方式与本指南中的Dictionary非常相似:

但是我遇到了一个问题,列表显示在检查器中,但我不能在其中设置超过1个值。如果我将列表的大小设置为2或更大,它会立即设置回1。
在尝试调试这个问题时,我发现OnBeforeSerialize(而 * 不是 * OnAfterDeserialize)在每一帧都在执行,不断地重置值,但我不知道为什么它会设置为1。
注意,如果我在1个可用槽中输入了一个字符串,它不会被重置。所以这种方法目前对于0或1个字符串的HashSet是"有效的",但不能超过1个。而且,如果我使用HashSet字段而不是从它继承,结果也不会改变(就像上面链接中所做的那样)。
下面是一个最小的例子:

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

public class TEST : MonoBehaviour
{
    public StringHashSet test;
}

[System.Serializable]
public class SerializableHashSet<T> : HashSet<T>, ISerializationCallbackReceiver
{
    public List<T> values = new List<T> ();

    public void OnBeforeSerialize ()
    {
        values.Clear ();

        foreach (T val in this) {
            values.Add (val);
        }
    }

    public void OnAfterDeserialize ()
    {
        this.Clear ();

        foreach (T val in values) {
            this.Add (val);
        }
    }
}

[System.Serializable]
public class StringHashSet : SerializableHashSet<string>
{
}

1.我怎样才能让它按预期工作(任意大小的字符串列表被序列化(反序列化)为HashSet)?
1.另外,为什么每帧都执行OnBeforeSerialize,即使检查器中没有进行任何更改?

    • 更多信息**

我发现这是因为当列表的大小改变时,默认情况下列表中所有新元素的值都与前一个元素的值相同,因此在HashSet中所有新元素的值都被压缩为1。
因此,虽然问题2仍来自上述问题,但问题1已演变为要求解决此问题,同时保持所需的HashSet功能。

gpnt7bae

gpnt7bae1#

下面是我解决这个问题的方法,考虑到问题中的更多信息部分添加的发现:

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

[System.Serializable]
public class SerializableHashSet<T> : HashSet<T>, ISerializationCallbackReceiver
{
    public List<T> values = new List<T> ();

    public void OnBeforeSerialize ()
    {
        var cur = new HashSet<T> (values);

        foreach (var val in this) {
            if (!cur.Contains (val)) {
                values.Add (val);
            }
        }
    }

    public void OnAfterDeserialize ()
    {
        this.Clear ();

        foreach (var val in values) {
            this.Add (val);
        }
    }
}

[System.Serializable]
public class StringHashSet : SerializableHashSet<string>
{
}

OnAfterDeserialize相同。
OnBeforeSerialize不再清除values列表,而是将新值从HashSet添加到List--具体地说,就是存在于HashSet中但不存在于List中的值。
这允许在检查器的列表中添加新元素时继续使用功能:重复的和空白的条目将工作正常,因为列表不会在任何时候被清除,只会被添加到。
关于第二个问题,我找到了有关为什么OnBeforeSerialize被如此频繁地执行的信息:http://answers.unity.com/answers/796853/view.html
相关信息:
如果对象的检查器在编辑器中打开,则在每帧调用

w6lpcovy

w6lpcovy2#

我也需要在检查器中有一个唯一的对象列表,并遇到了这个问题,这是我如何解决它:
申报您的名单:

public List<T> list = new List<T>();

创建OnValidate方法:

private void OnValidate() {
        int count = list.Count;

        // remove duplicates
        HashSet<T> uniqueList = new HashSet<T>(list);

        list= uniqueList.ToList();

        if (count > list.Count) {
            list.Add(null);
        }
    }

希望这能帮上忙。

相关问题