Skip to content

Lookup Table

Luke Mirman edited this page Sep 14, 2022 · 2 revisions

Introduction

The LookupTable class is a generic class that handles a string based lookup table for any asset in the project. This is particularly useful for referencing items, abilities, or character data stored in your project.

For the sake of this wiki page we will use the example of an arbitrary class Item, but keep in mind anything that extends ILookupCollectionEntry can be utilized.

Design Overview

The design of this system is around the following:

  • LookupCollectionAsset<T> - An abstract class which must be extended to store a collection of objects you wish to load into the LookupTable<T>, where <T> is the example class Item.
  • LookupTable<T> - Handles loading and and runtime management of the LookupCollectionAsset<T>, where <T> is the example class Item.
  • ILookupCollectionEntry - An interface which adds the Key property to your class. The key property is used to find objects in the LookupTable<T>.

Example - Item System

Representing the Items

To start we need to create the object that is actually stored in the LookupTable. For this example we are going to create a very rudimentary item that we want to reference the name and sprites of. Our item should extend ScriptableObject so we can create create instances in our project window and it should also extend ILookupCollectionEntry so we can add it to a collection asset later.

Sample Item Code

[CreateAssetMenu(fileName = "New Item", menuName = "Item")]
public class Item : ScriptableObject, ILookupCollectionEntry
{
	// The actual serialized key
	[SerializeField] private string key;

	// Tells the LookupTable to utilize the `key` variable for the table.
	public string Key => key;

	// Example data for the item. This could be anything depending on what you are storing.
	public Sprite icon;
	public string title;
}

Now that we have the structure for Items lets create a few instances to keep track of by right clicking in the project window. If you do not see the option to create Item make sure you included the CreateAssetMenu attribute in your script.

Storing the List of All Items

Next up we will script the collection of items. This is done by extending LookupCollectionAsset<T> in our own script. For this script we are going to use a very basic List<T> to store our asset references. In most cases this is more than sufficient but if you need a more complicated structure you can do your own implementation, as long as it can use IEnumerable<T>.

Sample Item Collection Code

[CreateAssetMenu(fileName = "New Item Collection", menuName = "Item Collection")]
public class ItemCollection : LookupCollectionAsset<Item>
{
	// The actual serialized list
	[SerializeField] private List<Item> items = new List<Item>();

	// Tells the LookupCollection to utilize the serialized list as an IEnumerable.
	public override IEnumerable<Item> Entries => items;
	
	// The purpose of this method is mostly for custom editor scripts to add elements.
	public override void InsertEntry(Item entry)
	{
		items.Add(entry);
	}
}

Now let's create our Item Collection in our project. In the project go to the Assets/Resources/ folder and right click to create our Item Collection. If you do not already have the Resources folder create one. Add the item instances we created earlier to the collection's items list.

Keep note of the exact name you end up giving your collection for later.

Loading the List of All Items

With our Item Collection now created and located somewhere within the Resources folder the next order of business is to load the item collection into a LookupTable. This can technically be done anywhere but it is recommended you do it in a single static class, which will be the implementation in this example.

First we create an instance of LookupTable<Item> and give it the name of the collection stored in the resources folder.

Sample Items Code

public static class Items
{
	// Create an instance of the LookupTable. Use the `ReloadResource` to set the Dictionary and List.
	// In this example the Item Collection from the previous step is located at `Assets/Resources/Collections/` and is named `Items`. Depending on how you saved your collection you should adjust the parameter as necessary.
	private static readonly LookupTable<Item> ItemLookup = new LookupTable<Item>("Collections/Items");

	// Access the item collection as a dictionary
	public static Dictionary<string, Item> Lookup => ItemLookup.Lookup;
	// Access the item collection as a complete list of items.
	public static List<Item> ItemList => ItemLookup.List;

	// Loads the ItemCollection on the first time this static class is referenced.
	static Items()
	{
		ItemLookup.ReloadResource();
	}
}

Strictly speaking we are completely setup now! All that is necessary now is to add any future new Item that you create to the ItemCollection. In the next step we will look at how to actually use the example Items class in practice.

Utilizing the List of All Items

In your any script of your project you can now access the item collection as either a Dictionary or a List. Both are useful depending on the purpose of your implementation.

You may access it as a Dictionary if you wanted to access it's sprite for ui using image.sprite = Items.Lookup["item_key_here"].icon or preferably if (Items.Lookup.TryGetValue("item_key_here", out Item item)) image.sprite = item.icon;.

You may access it as a List if you wanted to access every item to show in a gallery such as:

// `galleryDisplay` would theoretically be a custom component that is responsible for the ui elements.
// This function would clear everything currently being displayed.
galleryDisplay.ClearDisplay();
foreach (Item item in Items.ItemList)
{
	// This function would set the next unused slot to the item in the list.
	galleryDisplay.AddItemDisplay(item);
}

Recommendations

  • Strictly speaking the List and Dictionary in Items are mutable, which means they could be modified. This should usually be avoided but you may need this functionality for specific implementations. If this is too dangerous you can make the access of the List and Dictionary private and create your own functions in Items to have more restricted access to them.
  • To make the creation of new Item instances easier it is recommended you create an attribute for the ILookupCollectionEntry that utilizes the InsertEntry method on the LookupCollectionAsset. For this attribute you are would show a button when the Entry is not found in the Items asset and when the button is pressed, it is then added to the collection.
  • It is also recommended you make an attribute that validates the key of your lookup table entries.

Key Formatting Best Practices

It is encouraged that your keys follow the following rules, although most are not necessarily required for the lookup table to function.

  • The key should not be null or white space. (Required)
  • The key should not be shared by any other object within the lookup table. (Required)
  • The key should not contain any spaces. (Strongly Recommended)
  • The key should only use lowercase alphanumeric characters. (0-9, a-z) (Strongly Recommended)
  • The key should not include any accented or exotic ascii characters (Ò﹏Ó). (Strongly Recommended)

Limitations

  • By design requires use of a Scriptable Object to store the collection.
  • Collections must be loaded from the Resources folder.
  • Utilizes a string based lookup which can potentially be inefficient at large scope
    • String lookups can sometimes be inefficient especially when compared some other keys.
    • If the asset key is to change everything that used the former key must also be updated manually.