Singleton ScriptableObjects
Published 2 years ago
4.3 K
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
Garrett Bates
3 months ago
Is there a good way to abstract away some of the boiler plate of creating the singleton class and setting up the instance reference? Using a MonoBehaviour based approach I was able to have a type-generic Singleton base class that would do all the setup for the inheriting class. But applying a similar approach here, the method marked with [RuntimeInitializeOnLoad] never runs.
Ole Jürgensen
4 months ago
FunbitesI 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.
I did not know that you could assign default references to scripts. That is very cool, thanks! When trying to access ScriptableObjects from pure c# classes you cannot work with such references, though. By the way, you do not need to initialize ScriptableObject in Awake. Just referencing it is enough to enable it.
Ole Jürgensen
4 months ago
old_pilgrimScriptableObjects have OnEnable() callback, so no need for RuntimeInitializeOnLoadMethod attribute.
OnEnable is only called when the ScriptableObject is referenced somewhere and, well, enabled. You can achieve this by selecting the asset in the project view or by adding a reference to it in some scene object.
5 months ago
ScriptableObjects have OnEnable() callback, so no need for RuntimeInitializeOnLoadMethod attribute.
Thanks !!! It's very useful.