proper way to make Scriptable Object global references available

So I’ve moved to using Scriptable Objects as my data containers and I’m happy with that.

Just one thing bothers me: I need to put references to them everywhere by hand. That strikes me as dangerous. One day, two things in the scene that should reference the same SO will not and that’ll be tricky to debug.

The advantage of static classes is, of course, that they’re guaranteed to be unique. Can I achieve something similar with my SOs and if so, how?

For example, I have one player and one gamestate SO. Is there a pattern for anything in my scene to get those without me having to manually set up references everywhere? (please don’t answer “Singleton”. I’m trying to get as far away from Singletons as possible)

My best idea so far is to write a small wrapper MonoBehaviour that stores my global references, give it a Tag like MainController, and then search for that and find my references via GetComponent etc. - that’s a couple lines of code to copy&paste into everything that wants to use my global references. It would probably work, but maybe there’s a better way?

1 Like

You could always use Resources.Load or Addressables.LoadAssetAsync to find your ScriptableObject by name or address without manually attaching it to anything. You’re a little prone to the address or name of the thing changing (no compile-time checking), but if you’re smart about it you make the name/address a const variable somewhere and then you only have one place to change it if you need to.

Something like this:

public class MySoSingleton {
  static MySOType instance;
  const string theName = "NameOfMyScriptableObject";

  public static MySOType Instance {
    get {
      if (instance == null) {
         instance = Resources.Load<MySOType>(theName);
      }

      return instance;
    }
  }
}

EDIT: updated this post to contain my latest singleton-ish blurb:

ULTRA-simple static solution to a GameManager:

OR for a more-complex “lives as a MonoBehaviour or ScriptableObject” solution…

Simple Singleton (UnitySingleton):

Some super-simple Singleton examples to take and modify:

Simple Unity3D Singleton (no predefined data):

Unity3D Singleton with a Prefab (or a ScriptableObject) used for predefined data:

These are pure-code solutions, DO NOT put anything into any scene, just access it via .Instance

Alternately you could start one up with a RuntimeInitializeOnLoad attribute.

The above solutions can be modified to additively load a scene instead, BUT scenes do not load until end of frame, which means your static factory cannot return the instance that will be in the to-be-loaded scene. This is a minor limitation that is simple to work around.

If it is a GameManager, when the game is over, make a function in that singleton that Destroys itself so the next time you access it you get a fresh one, something like:

public void DestroyThyself()
{
   Destroy(gameObject);
   Instance = null;    // because destroy doesn't happen until end of frame
}

There are also lots of Youtube tutorials on the concepts involved in making a suitable GameManager, which obviously depends a lot on what your game might need.

OR just make a custom ScriptableObject that has the shared fields you want for the duration of many scenes, and drag references to that one ScriptableObject instance into everything that needs it. It scales up to a certain point.

And finally there’s always just a simple “static locator” pattern you can use on MonoBehaviour-derived classes, just to give global access to them during their lifecycle.

WARNING: this does NOT control their uniqueness.

WARNING: this does NOT control their lifecycle.

public static MyClass Instance { get; private set; }

void OnEnable()
{
  Instance = this;
}
void OnDisable()
{
  Instance = null;     // keep everybody honest when we're not around
}

Anyone can get at it via MyClass.Instance., but only while it exists.

Thanks for the answers. I’d so much like a solution without singletons. I consider singletons an anti-pattern. I don’t get why a simple static script can’t do this.

I consider them a solution.

Good luck in your pursuit!

1 Like

I’d argue that static data and “global” accessible ScriptableObjects are just another form of Singleton. ¯_(ツ)_/¯

2 Likes

Not exactly, but yes. I’ve since come around to using Singletons in some very narrow cases. There’s a Master Singleton pattern that I find a good approach to avoiding Singletons showing up everywhere and causing a complete mess.

A good alternative to using singletons is dependency injection. It’s a bit tricky to achieve in Unity - outside of just using serialized fields - but once the pipelines for it are in place, it can be used to avoid basically all the usual pitfalls that come with singletons.

For example, in my DI solution Init(args) globally shared services can be defined like this:

[Service(AddressableKey = "GameState")]
class GameState : ScriptableObject
{
[Service(FindFromScene = true)]
class Player : MonoBehaviour
{

And then clients can declare they need those services like this:

class Enemy : MonoBehaviour<GameState, Player>
{
    protected override void Init(GameState gameState, Player player)
    {

And then there’s no longer any need to manually drag-and-drop references to all clients, they get delivered automatically to everyone.


The services can be located in other scenes and prefabs as well, just like with singletons, which isn’t possible with serialized fields.

Since the dependencies of components are statically declared, it’s also possible to display information about those dependencies on the Inspector, which isn’t possible to do with singletons that are being used inside method bodies.

1 Like