Skip to content

Scriptable Dictionary

ScriptableDictionary<TKey, TValue> is a type of scriptable object that holds a dictionary of values. It acts just like a regular Dictionary<TKey, TValue>, but it is a scriptable object that can be used in the inspector and be passed around to other objects.

ScriptableDictionary<TKey, TValue> implements IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, and IDictionary. However, it does not always expose those implemented methods. To access them you need to explicitly cast the dictionary to the interface you want to use.

Scriptable Dictionary

Using a Scriptable Dictionary in your code is really straight forward. You use the Add, Remove, and Clear methods to manage the dictionary.

Add will add an object to the dictionary. The key must be unique, otherwise it will throw an exception.

Add Example
using UnityEngine;
using Hertzole.ScriptableValues;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableDictionary<string, Item> items;
public void AddItem(string key, Item item)
{
items.Add(key, item);
}
}

Remove will remove an object from the dictionary.

Remove Example
using UnityEngine;
using Hertzole.ScriptableValues;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableDictionary<string, Item> items;
public void RemoveItem(string key)
{
items.Remove(key);
}
}

Clear will remove all objects from the dictionary. This is useful for cleaning up the dictionary when it is no longer needed, like when quitting or changing scenes.

Clear Example
using UnityEngine;
using Hertzole.ScriptableValues;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableDictionary<string, Item> items;
public void Clear()
{
items.Clear();
}
}

You can also access the dictionary directly using the indexer. This is useful for getting or setting values in the dictionary.

Indexer Example
using UnityEngine;
using Hertzole.ScriptableValues;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableDictionary<string, Item> items;
public void SetItem(string key, Item item)
{
items[key] = item;
}
public Item GetItem(string key)
{
return items[key];
}
}

You can listen to changes in the dictionary using the OnCollectionChanged event.

Listening Example
using UnityEngine;
using Hertzole.ScriptableValues;
using System.Collections.Specialized;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableDictionary<string, Item> items;
private void OnEnable()
{
items.OnCollectionChanged += OnDictionaryChanged;
}
private void OnDisable()
{
items.OnCollectionChanged -= OnDictionaryChanged;
}
// Note that the item type is KeyValuePair<TKey, TValue>
private void OnDictionaryChanged(CollectionChangedArgs<KeyValuePair<string, Item>> args)
{
// Action is a NotifyCollectionChangedAction
Debug.Log("Action: " + args.Action);
// New Index is the index of the item(s) that was added
Debug.Log("New Index: " + args.NewIndex);
// Old Index is the index of the item(s) that was removed
Debug.Log("Old Index: " + args.OldIndex);
// New Items is the item(s) that was added as a Memory<T>
Debug.Log("New Items: " + args.NewItems);
// Old Items is the item(s) that was removed as a Memory<T>
Debug.Log("Old Items: " + args.OldItems);
}
}

Remember to unsubscribe from events when you no longer need them. See the events section for more information.

There are many more methods available in the dictionary, such as ContainsKey, TryGetValue, and Count. See the reference for a full list of all availale methods and properties.

Listening to collection changes includes a lot of parameters to keep track of. This table will help you understand when to use which parameter and when each action is invoked. If a specific parameter is not mentioned in its respective action, it is not used and can be ignored.

ActionWhen InvokedParameters
AddWhen one or multiple items are added to the collection.NewIndex is the index of the first item added. NewItems are the item(s) that were added. If it’s a single item, NewItems will have a length of 1.
RemoveWhen one or multiple items were removed from the collection.OldIndex is the index of the first item removed. OldItems are the item(s) that were removed. If it’s a single item, OldItems will have a length of 1.
ReplaceWhen one or multiple items were replaced in the collection. They could be replaced using the indexer, using Reverse or Sort.NewIndex and OldIndex are the index of the first item replaced. NewItems are the item(s) that were set. OldItems are the item(s) that were replaced. If it’s a single item, NewItems and OldItems will have a length of 1.
ResetWhen the collection is cleared.NewIndex and OldIndex are both 0. OldItems are the item(s) that were removed from the collection.

Troubleshooting

Empty or Incorrect Items

When accessing NewItems or OldItems, the items may be empty or not the ones in the collection.
You have probably accessed these items outside the scope of the collection event. NewItems and OldItems are pooled Memory<T> objects and are returned to the pool when the event is done. This means that you should copy the items to a new array or list if you want to use them outside the event.

Copying Items
using System;
using UnityEngine;
using Hertzole.ScriptableValues;
public class Inventory : MonoBehaviour
{
[SerializeField]
private ScriptableList<Item> items;
private void OnEnable()
{
items.OnCollectionChanged += OnListChanged;
}
private void OnDisable()
{
items.OnCollectionChanged -= OnListChanged;
}
private void OnListChanged(CollectionChangedArgs<Item> args)
{
Item[] newItems = args.NewItems.ToArray();
Item[] oldItems = args.OldItems.ToArray();
// You may now use newItems and oldItems whenever you want.
// There are other ways to copy data, but this is the simplest (but not the most performant).
}
}

The following properties are available on all Scriptable List:

Disabled by default

Marks the scriptable dictionary as read-only. This means that the dictionary cannot be changed at runtime and will always have the same value. This is useful for dictionary that are set in the editor and should not be changed at runtime.

Enabled by default

When this is enabled OnCollectionChanged will not be triggered when a dictionary item is set to the same value as it already has. This is useful for dictionaries that are set frequently and should not trigger the events when the value is the same.

Enabled by default

Clears the dictionary when the game starts. This is useful for dictionaries that should be cleared when the game starts, such as a dictionary of inventory items. Disabling this will keep the dictionary from the last play session.

The comparer used to compare the keys in the dictionary. This is useful for custom types that need a custom comparer. The default is EqualityComparer<TKey>.Default, which uses the default equality comparer for the type.

The actual number of items in the list.

A collection of the current keys in the dictionary.

A collection of the current values in the dictionary.

You need to create a dictionary before you can use it in the editor. Fortunately, this is really easy to do! All you need to do is inherit from ScriptableDictionary<TKey, TValue>.

Creating a Dictionary
using UnityEngine;
using Hertzole.ScriptableValues;
[CreateAssetMenu(fileName = "New Item Dictionary", menuName = "Scriptable Values/Dictionary")]
public class ItemDictionary : ScriptableDictionary<string, Item>
{
// You can add any custom methods or properties here.
// There are no methods to override, but you can add your own methods.
}