Yes, I found this nice script a while ago, I use it for all the scripts which would otherwise have to be static but now can be actual MonoBehavior scripts so I can include Update etc. functions in them if I want.
For my project, I had a gameObject in the scene called Manager (not to be confused with the static class Manager which is below!) This GameObject carries misc scripts that need to be accessed as if they were static ones without having them to be.
A function in, say, the Helper script on the Manager gameObject would be accessed like this:
Manager.helper.FunctionName();
using UnityEngine;
using System.Collections;
static class Manager {
public static Helper helper;
public static World world;
public static GameObject playerCamera;
public static GameObject managerObject;
public static GameObject player;
// when the program launches, UtilityManager will check that all the needed elements are in place
// that's exactly what you do in the static constructor here:
static Manager() {
managerObject = safeFind("Manager"); // (some persistent object)
helper = (Helper)safeComponent( managerObject, "Helper" );
world = (World)safeComponent( managerObject, "World" );
playerCamera = safeFind("Player Camera"); // (some persistent object)
player = safeFind("Player"); // (some persistent object)
// etc ..
// (you could use just the one persistent game object, or many - irrelevant)
// PS. annoying arcane technical note - remember that really, in c# static constructors do not run
// until the first time you use them. almost certainly in any large project like this, Manager
// would be called zillions of times by all the Awake (etc etc) code everywhere, so it is
// a non-issue. but if you're just testing or something, it may be confusing that (for example)
// the wake-up alert only appears just before you happen to use Grid, rather than "when you hit play"
}
// this has no purpose other than for developers wondering HTF you use UtilityManager
// just type UtilityManager.SayHello() anywhere in the project.
// it is useful to add a similar routine to (example) PurchaseManager.cs
// then from anywhere in the project, you can type UtilityManager.purchaseManager.SayHello()
// to check everything is hooked-up properly.
public static void SayHello() {
Debug.Log("Confirming to developer that the UtilityManager is working fine.");
}
// just some convenience routines to save people copy pasting
// when GameManager wakes up, it checks everything is in place...
private static GameObject safeFind(string s) {
GameObject g = GameObject.Find(s);
if ( g == null ) bigProblem("The " +s+ " game object is not in this scene. You're stuffed.");
// next .... see Vexe to check that there is strictly ONE of these fuckers. you never know.
return g;
}
private static Component safeComponent(GameObject g, string s) {
Component c = g.GetComponent(s);
if ( c == null ) bigProblem("The " +s+ " component is not there. You're stuffed.");
return c;
}
private static void bigProblem(string error) {
for (int i=10;i>0;--i) Debug.LogError(" >>> Cannot proceed... " +error);
for (int i=10;i>0;--i) Debug.LogError(" !!!!! Is it possible you just forgot to launch from scene zero.");
Debug.Break();
}
}
using UnityEngine;
using System.Collections;
public class GameController : MonoBehaviour {
public static GameController instance { get; private set; }
// Use this for initialization
void Awake () {
if (instance == null) {
instance = this;
} else {
Destroy (gameObject);
}
DontDestroyOnLoad (gameObject);
}
// Update is called once per frame
void Update () {
}
}
You can get the GameController by calling GameController.instance anywhere.
You just need to create one of these at the start.
Just be aware that that code @stuart_coyle posted (and that I use myself) isn’t a true singleton. Since it is possible for two of these objects to exist at the same time (however briefly) means that it isn’t really a singleton and can lead to issues. This is due to the fact that the Awake function continues to run and finish execution even after Destroy is called on it. Therefore, as above, DontDestroyOnLoad will still be run even on an object that is about to be culled. Instead, the better practice for singletons is to have a loader class that looks to see if the singleton already exists, and if it doesn’t, creates one. That way there is always only a single instance ever.
thank you all for taking your time and writing an answer.
i think i’m going with the singleton/toolbox, although its usage is very controversial on the internet as wrong implementation can lead to big problems, like you said Mike!
For all of you who are interessted in improving their c# skills:
Could someone point to a singleton implementation that accomplishes what @Mike-Geig was describing above, please?
I had some awful bugs (with other engines) due to ghost-objects on termination and this is critical for avoiding the same to be happening with unity too. Thanks!!
Well, the other issue with the implementation as described above is the object is created the first time you try to access it. Since Unity objects aren’t truly initialized until Awake() is reached, this is not really possible. So it’s possible to get a null reference, depending on when/where you try to get a reference to a singleton instance.
What I’ve done instead is to have a Startup scene that loads up all my “singletons” before loading up the real main scene. The downside to this approach is hitting “Play” needs these instances to exist, so I’ve used a scene auto loader for that.
Mike Geig also alluded to a loader class, and here’s how I’d imagine that would work: The loader class itself is a true singleton. When you query it for an object (say the Audio Manager), it first checks to see if a cached Audio Manager instance exists. If not, then try to find one using FindObjectOfType(), and if that returns null, create a new GameObject and attach an AudioManager component to it.
Of course, that approach also comes with its own subtle set of bugs to watch out for. For example: What if you try to get stuff during Awake() functions? Would you end up creating stuff that were meant to be created by a scene when it loads? Or does a scene contain an instance it’s not supposed to have? And so on…
@BlackPete
These setup scenes is exactly what I also do! They just host an empty transform with all the required singletons.
I understand that there are a lot of potential issues (racing conditions) with singletons which cannot easily spotted during development but may occur on device. So I also consider other solutions for inter-objects communication. I’m currently investigating simple referencing with a central refs registry class .