0
\$\begingroup\$

I have a ScriptableObject that holds the data that I will put into a Dictionary:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using Unity.VisualScripting;

[CreateAssetMenu(fileName = "New PronunciationData", menuName = "Pronunciation Data", order = 51)]

public class PronunciationData : ScriptableObject
{
    [SerializeField]
    public BlendKey thisBlendKey;

    [IncludeInSettings(true)]
    [System.Serializable]
    public class BlendKey{

        [SerializeField]
        public string soundName;

        [SerializeField]
        public List<NeemData> neemList;
    
    }

    [SerializeField]
    public AudioClip Pronunciation;

}

BlendKey will be the key for the Dictionary. It's a custom class which contains 2 items:

  • A string (e.g. the letters used to make a particular sound)
  • A list of items in another custom class called NeemData (e.g. a list of the sounds themselves).

NeemData is defined in another Scriptable Object. For the purposes of comparing 2 NeemDatas, I only really need to confirm that neemName is the same for both:

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

[CreateAssetMenu(fileName = "New NeemData", menuName = "Neem Data", order = 51)]

public class NeemData : ScriptableObject
{
    [SerializeField]
    public string neemName;

    [SerializeField]
    public Vector3 neemPosition;

    [SerializeField]
    public Sprite singleNeemSprite;

    [SerializeField]
    public Color baseColor;

    [SerializeField]
    public AudioClip Pronunciation;

    [SerializeField]
    public AudioClip placementAudio;

    [SerializeField]
    public List<PronunciationData> blendPronunciation;

    [SerializeField]
    public string feemText;

    [SerializeField]
    public List<FeemData> coreFeems;

    [SerializeField]
    public Color feemColor;

}

Pronunciation is just an Audioclip, and will be the value for the Dictionary.

The problem is that no matter what BlendKey I provide, the Dictionary returns a value of Null. In other words, it's unable to find anything using the keys I provide - even though I've done extensive checking to make sure that the key is exactly the same.

I think there are 2 possible causes:

  1. I'm using a custom class as a Key. Some sources mention that when you do so, you "need to override GetHashCode() (and preferably also Equals())". https://stackoverflow.com/questions/6999191/use-custom-object-as-dictionary-key

  2. One of the items in my Custom Class is a List. I've encountered problems comparing Lists before for a match, so imagine it would be even more tricky to do so here... where you have to match not only 2 lists, but also the 2 other strings (i.e. the soundNames).

I've looked into other possible data structures, but the challenge is that I really do need both the SoundName and the neemList to be a unique combination. So the Dictionary Key really does need to use both of those variables.

Is it possible to edit the Scriptable Object to use overrides, such that Unity can successfully look up values in the dictionary? Please let me know - thank you!

\$\endgroup\$
4
  • \$\begingroup\$ BlendKey is a normal class so it could overwrite HashCode and equals. And I really do need both the BlendKey and the SoundName it looks to me that SoundName is already part of Blendkey. \$\endgroup\$
    – Zibelas
    Oct 10, 2022 at 7:23
  • \$\begingroup\$ << And I really do need both the BlendKey and the SoundName >> @Zibelas how do you mean? \$\endgroup\$
    – kanamekun
    Oct 10, 2022 at 8:09
  • 1
    \$\begingroup\$ Your second last paragraph basically, I just quoted it. \$\endgroup\$
    – Zibelas
    Oct 10, 2022 at 8:12
  • \$\begingroup\$ << I really do need both the SoundName and the neemList to be a unique combination. >> Ah sorry, just corrected the sentence! \$\endgroup\$
    – kanamekun
    Oct 10, 2022 at 8:18

1 Answer 1

2
\$\begingroup\$

This can be easily solved by your first bullet point. Just implement the override. You will need to come up with a way to identify NeemData List on your own since I do not know the structure of it. It might be as easy as just taking the length of the array (might not be 100% unique but that would mean you first need to have the same soundname with the same amount of sound clips).

[Serializable]
public class BlendKey {
    [SerializeField]
    public string soundName;
    [SerializeField]
    public List<NeemData> neemList;

    public override bool Equals(object obj) {
        if (obj == null) return false;
        BlendKey c = obj as BlendKey ;
        if (c == null) return false;
        if (!soundName.Equals(c.soundName)) return false;
        if (neemList.Count != c.neemList.Count) return false;
        for (int index = 0; index < neemList.Count; index++) {
            if (!neemList[index].neemName.Equals(c.neemList[index].neemName)) return false;
        }

        return true;
    }

    public override int GetHashCode() {
        int listHash = 0;
        for (int index = 0; index < neemList.Count; index++) {
            listHash += neemList[index].neemName.GetHashCode();
        }
        return soundName.GetHashCode() + listHash;
    }
}


public class TestDic: MonoBehaviour {
    
    public Dictionary<BlendKey, string> testDic = new Dictionary<BlendKey, string>();        
    public NeemData[] NeemDatas;

    private void Start() {
        BlendKey testKey = new BlendKey();
        testKey.soundName = "ABC";
        testKey.neemList = new List<NeemData>{NeemDatas[0], NeemDatas[1]};
        testDic.Add(testKey, "Sound 1");
    
        BlendKey testKey2 = new BlendKey();
        testKey2.soundName = "XYZ";
        testKey2.neemList = new List<NeemData>{NeemDatas[1], NeemDatas[2]};
        testDic.Add(testKey2, "Sound 2");
    
        BlendKey testKey3 = new BlendKey();
        testKey3.soundName = "ABC";
        testKey3.neemList = new List<NeemData>{NeemDatas[1], NeemDatas[0]};

        Debug.Log(testDic[testKey]);
        Debug.Log(testDic[testKey2]);
        Debug.Log(testDic[testKey3]);
    }
}

The third testDic[testKey3]); will give you a key not found error since it is not the same data as from testKey (it has the same NeemData, but the order is different). You could solve this by further adding everything for a sort interface but if that is really needed will depends on if the NeemData is generated dynamically or by hand.

\$\endgroup\$
4
  • \$\begingroup\$ Thank you for this!! I added the definition of NeemData to the question - to check if two NeemDatas are equal, I would need the NeemName string to be the same. Is it possible to adapt this code to check for that? \$\endgroup\$
    – kanamekun
    Oct 10, 2022 at 9:01
  • \$\begingroup\$ @kanamekun no problem, it truncated the NeemData to just the name for test purpose. If you take care that the neemList in BlendKey has a fixed order, this should be all. Else it would need another interface to first sort the List. It depends if the testkey3 can happen or if you prevent it \$\endgroup\$
    – Zibelas
    Oct 10, 2022 at 9:29
  • \$\begingroup\$ I added it and am doing testing now and it seems to work perfectly. I can't thank you enough! Will keep testing, and also I will study the code so I can better understand it. THANK YOU! \$\endgroup\$
    – kanamekun
    Oct 10, 2022 at 9:55
  • 1
    \$\begingroup\$ The equals is just a lazy compare if the all parts are equal. If the size of the list has different amount of entries, no need to check entry by entry. And it just compares the names at the same location. \$\endgroup\$
    – Zibelas
    Oct 10, 2022 at 9:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .