Empty ScriptableObject when loading AssetBundle

Hello everyone,

I have this Scriptableobject dictionary script that containing basic key-value pairs (here my DictionaryEntry) for localization purpose.

I have successfully edit my dictionaries assets with the editor, and now I would like to load them (but they are not available at the start of the app, so they cannot be inside the Resources folder). When I load them with Resources.Load(path/in/Resources) all works perflectly. However, when I use an assetbundle with the following code, my scriptableobject Dictionary is empty.

....
           // Assetbundle is in persistentDataPath
            AssetBundle assetBundle = AssetBundle.LoadFromFile(path/to/assetbundle);
            Dictionary dict = assetBundle.LoadAsset<Dictionary>(path/to/asset/in/my/bundle)

            Debug.Log("Count : " + dict.Entries.Count);
            // Output : Count : 0

          // I have directly try this one too (considere I have only one asset per bundle)
          Dictionary dict = assetBundle.LoadAsset<Dictionary>(assetBundle.GetAllAssetNames()[0]);
          // But the result was the same :/

Here a simplify code of my Dictionary :

note : I tried to replace private field by public (with and without [SerializedField] attribute), but no success.

    [Serializable]
    public class DictionaryEntry
    {
        [SerializeField]
        private string key;

        [SerializeField]
        private string baseValue;

        public string Key { get { return key; } set { key = value; } }

        public string BaseValue { get { return baseValue; } set { baseValue = value; } }

[CreateAssetMenu, Serializable]
    public class Dictionary : ScriptableObject
    {
        [SerializeField]
        private List<DictionaryEntry> entries = new List<DictionaryEntry>();

        public List<DictionaryEntry> Entries { get { return entries; } }
    }

I am using Unity 5.6 beta / Android AssetBundles / and I have try None option and Uncompressed Assetbundle Option

Can anybody has any idea about why my scriptableobject is empty ?

Regards,

Valtyr

Ping !

The only thing I can think is that unity’s Object classes aren’t serializable. You may try serializing the entries to file by themselves (since you are using dataPath) then on load do:

Dictionary obj = ScriptableObject.CreateInstance();
obj. entries = (Deserialize entries somehow);

if you need to know more about serialization google C# Binary formatters and Streams.

Thanks for your response,

However I don’t understand the thing. From Unity doc, ScriptableObject are objects created from asset in order to store data and to be easily loadable. For me, I have miss something in the assetbundle packaging/loading because I can fully retrieve my Dictionary content when I am loading from Resources.

Firstly. Are you trying to create a game save system?

Secondly
If you are using resources.load it can load and deserialize because unity has it’s own handlers for it’s Object classes. However if you are loading from dataPath you are relying solely on the system’s ability to deserialize so the handlers don’t get used in that event. This is why I suggested you create your own. If you give me a few minutes I may be able to dummy up a small serialization engine to assist you with this. But I need to know what you are trying to accomplish exactly.

Also
I just thought… if you are placing things in the dataPath folder before you build they usually wont be there in the build because the path changes. And unity doesn’t compile from that folder during build. This would cause an empty dictionary.

I am not creating a save system (No write action is needed). I want to load an asset (as scriptableobject) inside a assetbundle that I have download (That is why it is in the persistentDataPath folder and not in the Resources nor dataPath).

I have sucessfully load other kind of asset like prefabs/scenes/sprites… only my Dictionary class seems to meet problems.

This kind of data can be easily serialized into XML file but our team would like to favorise Unity features. And scriptableobjects match with our needs (concerning dictionary).

So you are just trying to create serializable dictionaries?

        //The forech return item
        public struct DictionaryItem<TKey, TValue>{
            public readonly TKey Key;
            public readonly TValue Value;
            public DictionaryItem(TKey Key, TValue Value){
                this.Key = Key;
                this.Value = Value;
            }
        }

        //The Base Class
        [System.Serializable]
        public class MyDictionary<TKey, TValue> : System.Collections.Generic.IEnumerable<DictionaryItem<TKey, TValue>> {
            [SerializeField]
            protected List<TKey> _keys = new List<TKey> ();
            [SerializeField]
            protected List<TValue> _values = new List<TValue> ();

            public int Count{get{return _keys.Count;}}

            public System.Collections.Generic.IEnumerator<DictionaryItem<TKey,TValue>> GetEnumerator(){
                for (int i = 0; i < Count; i++) {
                    yield return new DictionaryItem<TKey, TValue> (_keys [i], _values [i]);
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator(){
                return GetEnumerator ();
            }

            public bool ContainsKey(TKey key){
                return _keys.Contains (key);
            }

            public bool ContainsValue(TValue value){
                return _values.Contains (value);
            }

            public void Add(TKey key, TValue value){
                if (!ContainsKey (key)) {
                    _keys.Add (key);
                    _values.Add (value);
                    return;
                }
                throw new System.Exception ("The Key Already Exists In Our Dictionary.");
            }

            public bool Remove(TKey key){
                if (ContainsKey (key)) {
                    int i = _keys.IndexOf (key);
                    _keys.RemoveAt (i);
                    _values.RemoveAt (i);
                    return true;
                }
                return false;
            }

            public void Clear(){
                _keys = new List<TKey> ();
                _values = new List<TValue> ();
            }

            public TValue this[TKey key]{
                get{
                    if (ContainsKey (key)) {
                        return _values [_keys.IndexOf (key)];
                    }
                    throw new System.Exception ("The Key Does Not Exist In Our Dictionary.");
                }
                set{
                    if (ContainsKey (key)) {
                        _values [_keys.IndexOf (key)] = value;
                        return;
                    }
                    Add (key, value);
                }
            }

            public TKey[] Keys{ get { return _keys.ToArray (); } }
            public TValue[] Values{ get { return _values.ToArray (); } }
        }

        //Inherit my dictionary<Some Key type, Some Value Type>
        [System.Serializable]
        public class OurDictionaryStringString : MyDictionary<string, string>{}

        //Declair a pre defined dictionary (this will serialize)
        public OurDictionaryStringString myDict = new OurDictionaryStringString ();

This will operate much like a regular Dictionary

Sorry to resurrect an older thread, came across this one while googling, but the code is not good.

The above is very bad example of serializeable dictionary, the Dictionary has a constant O(1) lookup and add time, while the above has a linear O(n), which might be a world of a difference for bigger data sets. While this semantically looks the same as Dictionary it’s entirely different collection type.