Unity 序列化字典“索引超出范围”12 项后

Agg*_*sor 5 c# serialization dictionary unity-game-engine deserialization

更新:这是 Unity 确认的错误,并且已提交。

我创建了一个通用的可序列化字典和一个用于 int:Object 的特定字典。

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

/// <summary>
/// Is what it sounds like
/// </summary>
[System.Serializable]
public class SerializableDictionary<TKey,TValue> : Dictionary<TKey,TValue>, ISerializationCallbackReceiver 
{
  [SerializeField]
  protected List<TKey> keys = new List<TKey>();

  [SerializeField]
  protected List<TValue> values = new List<TValue>();

  //Save the dictionary to lists
  public void OnBeforeSerialize()
  {
    keys.Clear();
    values.Clear();
    foreach(KeyValuePair<TKey,TValue> pair in this)
    {
        keys.Add(pair.Key);
        values.Add(pair.Value);
    }
  }

  //Load the dictionary from lists
  public void OnAfterDeserialize()
  {
    this.Clear();

    if(keys.Count != values.Count)
    {
      throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable."));
    }

    for(int i = 0; i<keys.Count; i++)
    {
      this.Add(keys[i], values[i]);
    }
  }
}

[System.Serializable]
public class DictionaryOfStringAndInt : SerializableDictionary<string, int> { }

[System.Serializable]
public class DictionaryOfIntAndSerializableObject : SerializableDictionary<int, ScriptableObject> 
{
}
Run Code Online (Sandbox Code Playgroud)

然而编译器因错误而变得疯狂

IndexOutOfRangeException:数组索引超出范围。System.Collections.Generic.Dictionary`2+Enumerator[System.Int32,UnityEngine.ScriptableObject].MoveNext ()

由于某种原因这条线

foreach(KeyValuePair<TKey,TValue> pair in this)

抛出该错误,但仅在我DictionaryOfIntAndSerializableObject超过 12 个对象之后。这让我完全困惑。

您能想到为什么DictionaryOfIntAndSerializableObject在 12 个项目之后抛出超出范围异常的原因吗?

更新这篇文章有同样的问题:http://forum.unity3d.com/threads/serializeable-dictionary-exceptions-after-code-change.319285/

进一步说明

我有一个数据库 GameObject,其中保存了我的所有模型(类型)数据。我正在做的是将 CSV 文件解析为ScriptableObjects存储在数据库 GameObject 中的文件。这样我的数据库在运行时编译一次,我可以在运行时和编辑器中自由访问它。该数据库对象就是拥有DictionaryOfIntAndSerializableObject对象的对象。我每张桌子都有一个(所以我有一个用于武器、盔甲等)。

这是我所有可编写脚本的对象的基类(作为字典中的值存储):

public class M_Card : ScriptableObject

{

  public E_CardTypes cardTypeID;

  public int rankID;

  public int rarityID;

  public int qualityID;

  public int editionID;

  public Sprite sprite;

 public M_Card()
 {
    cardTypeID                  = 0;
    rankID                      = 0;
    qualityID                   = 0;
    sprite = null;
 }

}
Run Code Online (Sandbox Code Playgroud)

下面是代码(在解析 CSV 中的一行之后)我将 M_Card 的实例放入字典中:

 private void LoadActionCards()
 {
    List<Dictionary<string, object>> listData = CSVReader.Read("Actions");

    for (var i = 0; i < listData.Count; i++)
    {
      Dictionary<string, object> data = listData[i];

      string name = (string)data["Name"];
      M_ActionCard card = ScriptableObjectCreator.createActionCard(name);

      int cardTypeID  = (int)data["CardTypeID"];
      card.cardTypeID = (E_CardTypes)cardTypeID;

      AssignActionCardValues(card, data);

      Database.ACTION_CARD_LIST.Add(card);

      // THIS IS WHERE I MAP THE TYPEID TO THE M_CARD OBJECT
      // THIS IS WHERE THE OBJECT IS ADDED TO THE DICTIONARY
      Database.ACTION_CARD_MAP[card.ID] = card;
    }
  }
Run Code Online (Sandbox Code Playgroud)

现在我有 4 个数据表(武器、头盔、盔甲、职业)。但是,如果这些表中的任何一个包含超过 12 个项目,我会在第 13 个项目上得到 100% 的错误。我仍在调查,但这是如何将键添加到字典中的。

请注意,M_HelmetCard 与 M_ActionCard 相同,它们都只是 M_Card 的空子类。

这里有一些图片来添加更多证据。第一个我有全部 16 个头盔。出现错误 在此输入图像描述

然后我在这里做了,所以只有 12 个并且错误消失了。 在此输入图像描述

任何错误超过 12 个的列表。实际的项目并不重要(所以它不像特定的第 13 个项目已损坏,13 个相同的项目仍然会导致此错误)。

此刻非常困惑。

(我想问题可能是我有一个Sprite类正在被序列化并且数据太多,但是当我摆脱它时问题仍然存在)。

更新这里是一个存在此问题的示例项目的链接(删除了所有不相关的内容)。https://www.mediafire.com/?axxn3u823cm7c59

要产生错误:单击database层次结构中的对象,然后单击Update Database按钮。

然后点击播放,您就会看到错误。

然后,如果您想查看第 13 项,请打开 Helmets CSV 文件 ( \Assets\Resources\ExcelData),然后删除第 13 项。然后再次更新数据库。然后点击播放。错误就会消失!

这是一个重大的WTF:?

med*_*ity 3

我偶然发现了同样的问题。Unity 的序列化有时会破坏序列化字典的内部结构,导致无法添加其他值。

IDictionary就我而言,我通过实现接口并将SerializableDictionary其功能委托给Dictionary不受 Unity 序列化影响的私有对象来消除这些错误。

这是我在 Visual Studio 的帮助下生成的解决方案

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

[System.Serializable]
public class SerializableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ISerializationCallbackReceiver
{
    [SerializeField]
    private List<TKey> keys = new List<TKey>();

    [SerializeField]
    private List<TValue> values = new List<TValue>();

    private Dictionary<TKey, TValue> dictionary = new Dictionary<TKey, TValue>();

    public ICollection<TKey> Keys
    {
        get
        {
            return ((IDictionary<TKey, TValue>)dictionary).Keys;
        }
    }

    public ICollection<TValue> Values
    {
        get
        {
            return ((IDictionary<TKey, TValue>)dictionary).Values;
        }
    }

    public int Count
    {
        get
        {
            return ((IDictionary<TKey, TValue>)dictionary).Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return ((IDictionary<TKey, TValue>)dictionary).IsReadOnly;
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            return ((IDictionary<TKey, TValue>)dictionary)[key];
        }

        set
        {
            ((IDictionary<TKey, TValue>)dictionary)[key] = value;
        }
    }

    public void OnBeforeSerialize()
    {
        keys.Clear();
        values.Clear();
        foreach (KeyValuePair<TKey, TValue> pair in this)
        {
            keys.Add(pair.Key);
            values.Add(pair.Value);
        }
    }

    public void OnAfterDeserialize()
    {
        dictionary = new Dictionary<TKey, TValue>();

        if (keys.Count != values.Count)
        {
            throw new System.Exception(string.Format("there are {0} keys and {1} values after deserialization. Make sure that both key and value types are serializable.", keys.Count, values.Count));
        }

        for (int i = 0; i < keys.Count; i++)
        {
            Add(keys[i], values[i]);
        }
    }

    public void Add(TKey key, TValue value)
    {
        ((IDictionary<TKey, TValue>)dictionary).Add(key, value);
    }

    public bool ContainsKey(TKey key)
    {
        return ((IDictionary<TKey, TValue>)dictionary).ContainsKey(key);
    }

    public bool Remove(TKey key)
    {
        return ((IDictionary<TKey, TValue>)dictionary).Remove(key);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return ((IDictionary<TKey, TValue>)dictionary).TryGetValue(key, out value);
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        ((IDictionary<TKey, TValue>)dictionary).Add(item);
    }

    public void Clear()
    {
        ((IDictionary<TKey, TValue>)dictionary).Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return ((IDictionary<TKey, TValue>)dictionary).Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        ((IDictionary<TKey, TValue>)dictionary).CopyTo(array, arrayIndex);
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return ((IDictionary<TKey, TValue>)dictionary).Remove(item);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return ((IDictionary<TKey, TValue>)dictionary).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IDictionary<TKey, TValue>)dictionary).GetEnumerator();
    }
}
Run Code Online (Sandbox Code Playgroud)