Hello,
So I have a singleton implementation on a class, but its sub-classes trigger its “Destroy” condition:
public class Game : MonoBehaviour
{
public static Game Instance;
public static Session Session;
public static Level Level;
public static Score Score;
/// <summary> Make this a singleton </summary>
protected virtual void Awake()
{
// Singleton logic:
if (Instance == null)
{
Instance = this;
// Don't destroy the loading screen while switching scenes:
DontDestroyOnLoad(gameObject);
print(Instance +" dont destroy");
}
else
{
print(Instance +" do destroy");
Destroy(gameObject);
return;
}
}
}
}
So for example, the class:
public class Session : Game
Triggers the destroy condition. How can I do this better? Thanks!
that’s not a singleton, that’s just a static accessor to a non-singleton MonoBehaviour.
in general, to call something a singleton, is to make sure by design that it can only ever have a single instance at any point in time. here nothing stops you from having this MonoBehaviour attached to another object. In fact, the very belief that it’s a singleton is a fallacy that could lead you to a disaster.
that said, you don’t make Monobehaviours into singletons ever. they aren’t supposed to be used like this by design.
the most you can do is to make your own custom Game class, that is a true singleton, and a GameManager MonoBehaviour that serves as an entry point for your entire application.
once that GameManager’s Awake is called you initialize Game, and use that throughout the rest of your code.
public class Game {
private static Game _instance;
public static Game Instance {
get {
if(_instance == null) _instance = new Game(); // this can happen exactly once in the lifetime of your app
return _instance;
}
}
private Game() {
// constructor doesn't need to be empty
// but must be private
}
public void Initialize() {
// ...
}
}
public class GameManager : MonoBehaviour {
public void Awake() {
Game.Instance.Initialize(); // nothing is able to destroy this instance after this point
// you can always access it from anywhere with Game.Instance property
}
}
You could also place your GameManager before anything else in Script Execution Order to make sure that your Singleton runs before anything. Or(!) maybe you want it to be run last, depends on whether you want cache other instances with it or just parse some data etc.
This is btw the only static variable you ever need in your application.
Anything you want to work as static should now belong to this Game class, and be declared as just public.
@orionsyndrome thanks a lot! great explanation!
be watchful about MonoBehaviours, don’t use them as regular C# objects, because they serve as gateway wrappers to another system that is completely outside of your jurisdiction. MonoBehaviour classes should behave strictly passively, and as drivers to some logic that is attached to GameObject’s. it’s ok to have them communicate proactively between each other (but I’d use some kind of proxy anyway), and it’s ok if they have some sort of responsibility, but it should be always connected to their particular domain, so that when a domain expires, you can assume that all related MonoBehaviours expire as well.
the idea is that your “true” C# code – or some other more persistent MonoBehaviour that is central to your application, that you could attach to an empty GameObject and treat it specially – should govern the hard and/or persistent states and let’s say the ground truth of the system; everything else is just the means of getting your messages across to the underlying engine, whether it’s about graphics, animation, or some local state logic. it should precipitate in such a manner, that you have maximum control over the information flow anyway.
that is, at least, my style of working with them, and so far I haven’t ever had an issue like other people seem to report. namely phantom problems with serialization, null checking problems, the problems you’ve got with an object being destroyed and similar. simply don’t place too much faith in them, they are just shadows of the C++ system underneath. use them as simple puppets and they will serve you very predictably.
finally, never ever, inherit from an object that inherits from a MonoBehaviour. maybe other people have mixed opinions on this, but my intuition tells me that’s a no go zone, and I cannot think of a situation where it’s required.