How to create a Database system to handle Unity prefabs

Posted by : on

Category : Unity

The problem with prefab references in Unity

Prefabs are a very useful tool in unity as they allow us to create and store premade GameObjects along with their components and child GameObjects as reusable assets. Instead of copying and pasting a GameObject from the scene, we can make a prefab of it and use it many times in the same or different scenes while keeping all the copies synchronized. By changing a prefab value, this change is applied to all our scene GameObjects that are created by this particular prefab. Prefabs are also very useful for instantiating GameObjects at runtime.

Although prefabs are very useful, many times they present us with a problem, when we need to replace them, with another prefab. Let’s say that we have a prefab that represents a hit vfx and gets instantiated or starts whenever a character in our game gets hit. This prefab is referenced by many scripts and if we ever want to replace it with another prefab we have to find all the references of this particular prefab in all our scripts and change them. The same is true for all kinds of prefabs, for example when we instantiate a new enemy in different places in our game, or playing a level up alert every time when a unit in our game levels up.

This is not only true for individual prefabs, but also for groups of prefabs. We may have a group of prefabs that uses a certain color palette, but we are also considering using a different group with another color. Being able to change all the references of a single prefab, or even change a group of prefabs with another is actually pretty simple by creating a Scriptable Object to hold our prefab references and two small scripts that allow us to reference our Scriptable Objects easily in our code.

A simple solution

Instead of referencing our prefabs directly in code, let’s write a script that creates a Scriptable Object that holds our references:

using UnityEngine;

[CreateAssetMenu(menuName = nameof(PrefabsDatabase), fileName = nameof(PrefabsDatabase))]
public sealed class PrefabsDatabase : ScriptableObject
{
   public SpriteRenderer IsometricDiamond;
   public ParticleSystem Particles;
}

This is a simple script that creates ScriptableObjects, that can be populated with our prefabs. In this particular example, each Scriptable Object we create, can hold two prefabs: A spriteRenderer prefab and a Particle system prefab. In Unity, it will look something like this:

Prefab Database without references

Here i have added references to the prefab:

Prefab Database with references

and obviously we can have different kinds of prefab databases by creating multiple Scriptable Objects, for example:

Prefab Databases

This takes care of the ‘one place for reference’ problem, but how we can reference those databases?

Let’s create a new script:

using UnityEngine;

// Create once with the name CurrentPrefabDatabase and add it to the Resources folder
[CreateAssetMenu(menuName = nameof(CurrentPrefabDatabase), fileName = nameof(CurrentPrefabDatabase))]
public sealed class CurrentPrefabDatabase : ScriptableObject
{
    public static CurrentPrefabDatabase Instance => 
        _instance ??= Resources.Load<CurrentPrefabDatabase>(nameof(CurrentPrefabDatabase));

    public  PrefabsDatabase CurrentDataBase;

    private static CurrentPrefabDatabase _instance;
}

We only need one creation of a Scriptable Object from this script, after that, we can delete the CreateAssetMenu attribute if we want, so that it won’t pollute our menu. The important thing here, is to have this ScriptableObject in our Resources folder. Now we can add to the CurrentDataBase field, the Scriptable Object PrefabsDatabase we want:

Current Prefab Database

This script lazy loads itself whenever we need it. We can access it, by its static instance property, so now the only think that we need, is a script that will make that access easier in our code. A one line static class:

public static class PrefabsFacade
{
   public static PrefabsDatabase Prefabs => CurrentPrefabDatabase.Instance.CurrentDataBase;
}

and thats it. Now instead of having to write something like that:

using UnityEngine;

public class Capsule : MonoBehaviour
{
   [SerializeField] private SpriteRenderer isometricDiamond;
   [SerializeField] private ParticleSystem particles;
    
   private SpriteRenderer _go;
   private ParticleSystem _go2;


   private void Start()
    {
       _go = Instantiate(isometricDiamond, transform);
       _go2 = Instantiate(particles, transform);
    }
}

where we have direct references to our prefabs in each script, we can do this:

using UnityEngine;
using static PrefabsFacade;

public class Capsule : MonoBehaviour
{
   private SpriteRenderer _go;
   private ParticleSystem _go2;


   private void Start()
    {
       _go = Instantiate(Prefabs.IsometricDiamond, transform);
       _go2 = Instantiate(Prefabs.Particles, transform);    

       _go2.Play();
    }
}

by writing the using static PrefabsFacade statement in our code, we can have a reference to our prefabs database just by writing Prefabs.(+the name we have for our prefab). Now whenever we need to change one of our prefabs, we only have to change it in one place, the field in our PrefabsDatabase Scriptable Object in the Unity editor and the changes will happen in all places of our code that we use that prefab.

Even if we need to change a group of prefabs, it is a single drag and drop of a new PrefabsDatabase Scriptable Object, in our CurrentPrefabDatabase Scriptable Object in the Resources folder.

For more complicated scenarios, the Unity Addressables is a better solution, but for fast prototyping and small games, using this technique with the Resources folder, can save us a lot of time from searching and replacing prefabs references all over our code.

Here are the three scripts needed together for fast reference:

using UnityEngine;

[CreateAssetMenu(menuName = nameof(PrefabsDatabase), fileName = nameof(PrefabsDatabase))]
public sealed class PrefabsDatabase : ScriptableObject
{
   // add your prefab references here, for example:
   // public ParticleSystem Particles;
}
using UnityEngine;

// Create once with the name CurrentPrefabDatabase and add it to the Resources folder
[CreateAssetMenu(menuName = nameof(CurrentPrefabDatabase), fileName = nameof(CurrentPrefabDatabase))]
public sealed class CurrentPrefabDatabase : ScriptableObject
{
    public static CurrentPrefabDatabase Instance => 
        _instance ??= Resources.Load<CurrentPrefabDatabase>(nameof(CurrentPrefabDatabase));

    public  PrefabsDatabase CurrentDataBase;

    private static CurrentPrefabDatabase _instance;
}
public static class PrefabsFacade
{
   public static PrefabsDatabase Prefabs => CurrentPrefabDatabase.Instance.CurrentDataBase;
}

I hope you found this article useful and as always if you have questions, use the comments section, or contact me directly via the contact form or email, also if you don’t want to miss any of the new articles, you can always subscribe to my newsletter or the RSS feed.


Follow me: