Storing and Accessing Data

Traditionally it is commonplace to lookup data points in a database by string, or ID. These lookups can be fairly costly operations on the CPU in most cases unless the database system is heavily optimized to make those lookups efficient. Lots of approaches and options are available to the public, but let's just focus on how Vault handles this.

With Vault the goal is to never lookup anything by string or another parameter in the data - because we design everything by reference.

Vault stores data as Assets

When you create data in Vault it actually creates a new .asset file in your project under the /Storage/ folder. This asset is a DataEntity : ScriptableObject and that means you can just define variables that are direct references to it just like you would create a prefab and put it in a field. The [AssetDropdown(typeof(T))] allows you to find those assets anywhere in your project with a simple searchable dropdown box.

You can probably see that the way we communicate with this data is different than traditional databases. Since your data is now a ScriptableObject, you can run functions in it, take advantage of inheritance, abstraction, store more references within it, and reference it directly in the editor! Your data just went Super Saiyan.

Access: Ideal case

Access your data by direct reference. This is done by using the [AssetDropdown(typeof(T))] attribute on variables in your systems. When you pick that item, it just stores the reference in that variable. It's simple and it's basically free on the CPU at runtime. You don't need to go look for the file, drag anything into the field, remember or lock in any string names, write down id's... None of that noise is required.

Consider this example:

[AssetDropdown(typeof(WeaponMelee))]
public WeaponMelee DeepWeaponType; // Define your content by reference in the editor
private void Update()
{
    // This is basically free because we have set the reference in the editor. No lookup required.
    if (UnityEngine.Input.GetMouseButtonDown(0))
    {
        DeepWeaponType.Fire(this);
    }
}

Note: If you must do a lookup (for instance, when passing data over a network), design so that you can reference any items directly and then pass around the DB Key as needed. It is readily available on any item via anyDataEntity.GetDbKey().

Access: When a string lookup is required

In all cases you should design away from patterns that require you to do string lookups on your database. However, in some cases you may actually need to reference data by string. While this is not ideal, it may be a necessity at some point.

That being said, you'll want to cache whatever you can and keep a reference to the item so can minimize the number of string lookups you need to do.

Consider this example:

public WeaponMelee DeepWeaponType;
public string WeaponName = "Pointy Stick";

public void Initialize()
{
    DataEntity weapon = Vault.Db.Get(WeaponName); // [Obsolete] warning.
    DeepWeaponType = (WeaponMelee) weapon;
}
private void Update()
{
    if (UnityEngine.Input.GetMouseButtonDown(0))
    {
        DeepWeaponType.Fire(this);
    }
}

Access: The wrong way

Since the goal is to avoid lookups based on data parameters such as string names we definitely want to avoid doing it often. Notice below that you can get your content with a string lookup every frame, but doing this is objectively and measurably stupid.

Consider this extremely bad example:

private void Update()
{
    if (UnityEngine.Input.GetMouseButtonDown(0))
    {
        ((WeaponMelee)Vault.Get("My Cool Pokey Thing")).Fire(this);
    }
}

Conclusion

  • You should design your systems to leverage the [AssetDropdown(typeof(T))] attribute in any case you need to find something in the database.

  • You should avoid lookups using strings.

  • You should take full advantage of the fact that your data is now ScriptableObject types and use all of the fancy features that it provides.

Last updated