Singleton ScriptableObjects
Published a year ago
How to set them up
Sometimes you need data that can be accessed from anywhere without an instance to a class, this is where the singleton comes into play. A singleton is a class that can only have one instance of itself running at a time and has a static reference to that instance. This way you can access that instance like this MySingletonClass.Instance. Look at the example below (an item database):
public class ItemsDB { private static ItemsDB instance; public static ItemsDB Instance { get {return instance; } } //We use a property so no one from outside can change the value of instance //We can make this field static so it's easier to access it public static List<Item> items = new List<Item>(); //Set instance variable upon creation public ItemsDB() { instance = this; } }
Ok, this is a singleton but we can't access it from the Editor, only from code, and that renders this particular class for this particular purpose useless. So we need a ScriptableObject.
public class ItemsDB : ScriptableObject { ...
Ok, so now we can create the it in the editor and it might work the way it is, setting instance in the constructor. But the best way to do initialize the class is through a static Init method with the [RuntimeInitializeOnLoadMethod] attribute that will load our scriptable object asset, like this:
[RuntimeInitializeOnLoadMethod] private static void Init() { instance = Resources.LoadAll<ItemsDB>()[0]; }
Add that to the class and when the game starts either in the editor or in a build, no matter what scene, all methods with the [RuntimeInitializeOnLoadMethod] attribute will be called, including our Init. In this method we will load the first asset of type ItemsDB Unity finds inside any Resources folder, so you need to place the scriptable object inside a Resources folder. If you have more than one ItemsDB in the Resources folder you should use Resources.Load<ItemsDB>("MyAssetPath"); instead.
That's it, we reached the end of this very short "tutorial" on singleton scriptable objects. I hope someone has learned something new with this article and if there are any alternatives to this method or if someone encounters any issues please let me know.
Self taught hobbyist - Programmer
6 months ago
Game Studio
I didn't know about the "[RuntimeInitializeOnLoadMethod]" attribute and I will start using it. Thanks! What I did before was to have a MonoBehaviour that initializes the Scriptable Objects in the Awake, but if you forget to add the references, you will get a lot of null exceptions. I suggest you to not use the singleton pattern at all. I have a Scriptable Object that shares memory for all AI agents (AISharedMemory). It's similar to your ItemDB singleton. I could have used a singleton because only the enemies had references to it, but I decided to use a reference to AISharedMemory in all AI related Components. When I needed to create a AI companion to help players, it was super simple. I made another instance of the AISharedMemory and add that instance to the AI companion Components in their prefabs. And it was simple as that. If I had used the singleton pattern, I would have to fix a lot of stuff in the code. If you think that setting up references for the most common case can be tedious, there's a simple way that Unity can help you with that. You need just to select the script that makes references and set the instance of the Scriptable Object in the inspector. Every time you create a new instance of the Component, it will make reference to that specific Scriptable Object.