- Home /
DontDestroyOnLoad duplicate object when using a singleton
I have the following problem. I am using the following code to create a pseudo-singleton out of a MonoBehavior derived class. The reason I am doing this inheritance is because I need the singleton to be running some coroutines.
public class ChallengeManager : MonoBehaviour
{
static ChallengeManager _Instance;
public static ChallengeManager Instance {
get {
if(_Instance == null) {
_Instance = (new GameObject ("ChallengeManager")).AddComponent<ChallengeManager> ();
DontDestroyOnLoad(_Instance);
}
return _Instance;
}
}
(...)
}
I am calling DontDestroyOnLoad in order to have my ChallengeManager singleton instantiation still attached to a Game Object when another scene loads. That's because I want my coroutines that run in the script to also persist.
The problem is, every time I switch between scenes another game object with a ChallengeManager script attached spawns (more precisely when calling a method through _Instance of my singleton). Is there a workaround for this or is there something I'm missing in my code?
Thank you for your anticipated response.
Answer by sotirosn · May 01, 2013 at 05:35 AM
I have a persistant singleton template Singleton.cs:
using UnityEngine;
using System.Collections;
public class Singleton<Instance> : MonoBehaviour where Instance : Singleton<Instance> {
public static Instance instance;
public bool isPersistant;
public virtual void Awake() {
if(isPersistant) {
if(!instance) {
instance = this as Instance;
}
else {
DestroyObject(gameObject);
}
DontDestroyOnLoad(gameObject);
}
else {
instance = this as Instance;
}
}
}
Then I derive form it in Server.cs for example:
public class Server : Singleton<Server> {
public void Start() {
Debug.Log ("Start");
}
public void OnGUI() {
if(GUI.Button(new Rect(10, 10, 80, 30), "Restart")) {
Application.LoadLevel(0);
}
}
}
Server.Start() is only called once if the isPersistant is checked because Awake will other wise destroy the gameObject before the first frame each time the level is restarted.
I'd like to add that this will not work if your singleton gameobject is not a root level gameobject in your scene. To make sure your gameobject is a root level gameobject you can add:
transform.parent = null;
inside the isPersistant block on line10
In my case I was using gameobjects as folders to organize my hierarchy and scratching my head wondering why the gameobject kept getting created. The reason was the parent of the singleton gameobject was getting destroyed and so my singleton game object was also getting destroyed.
Answer by hololabs · Mar 25, 2013 at 05:11 PM
It seems like you're calling DontDestroyOnLoad on the component instead of the GameObject. In this case, the GameObject will be destroyed when you switch scenes and the component will get destroyed as a result.
This should solve it:
if(_Instance == null) {
GameObject g = new GameObject ("ChallengeManager");
_Instance = g.AddComponent<ChallengeManager> ();
DontDestroyOnLoad(g);
}
Answer by an_n · Nov 30, 2016 at 12:06 PM
I fixed such problem with this way:
using UnityEngine;
public class GameController : MonoBehaviour {
private static GameController _instance;
public static GameController instance {
get {
if (_instance == null) {
_instance = FindObjectOfType(typeof (GameController)) as GameController;
if (_instance == null) // create new object
DontDestroyOnLoad(_instance);
}
return _instance;
}
}
}
..and then you can use this singleton by just calling this instance getter, like that: GameController.instance.SomeFunction();
Answer by DiGiaCom-Tech · May 25, 2016 at 05:53 PM
@sotirosn .... Wouldn't this be more appropriate/flexible as an all around 'Awake' method?
That is, maybe I want only one but don't want it to be persistant. Maybe I want many and want them all to be persistant. Maybe neither or any combination thereof.
using UnityEngine;
using System.Collections;
public class Singleton<Instance> : MonoBehaviour where Instance : Singleton<Instance>
{
public static Instance instance { get; set; }
public bool IsSingelton = false;
public bool IsPersistant = false;
public virtual void Awake()
{
// If there can be only one...
if (IsSingelton) {
// Check if instance already exists
if (!instance)
{
// Set initial instance
instance = this as Instance;
}
else
{
// We don't want additional instances
Destroy(gameObject);
return;
}
} else {
// Set instance
instance = this as Instance;
}
// Check if we want to persist this gameObject between loads
if (IsPersistant)
{
// Make sure this object stays on reloads
DontDestroyOnLoad(gameObject);
}
}
}
Answer by kleber-swf · Oct 28, 2016 at 02:31 PM
There is a very flexible and robust singleton solution that I'm using:
http://bit.ly/2fe6kFb
I created it some time ago and it was polished by the community.
I'm using it in almost all my projects right now :).