- Home /
The question is answered, right answer was accepted
Design Patterns (Singleton Issue)
Hello,
I'm asking this type of questions a lot, I guess that's the best way of learning.
Here is the issue:
I've converted all of my project to C# from Unity Script, now, I didn't check if everything was working until I converted all of my scripts.
I've tried to attach my new C# scripts to Empty GameObjects (that will take care of stuff) and I received an error, that these classes has no MonoBehaviour and it needed to inherit from it, in order to attach to GameObjects.
So what I did, was simply to inherit from MonoBehaviour to these classes, but, and here's the catch, I somehow received an error that says I can't return "new" MonoBehavior objects to other classes (scripts or objects), why is that? well here is why:
What I did, was to create a "FactoryObject" in order to return objects and classes as Singletons (like HUD or Particle Managers and so on).
Part of the factory script:
//This will return the AudioControl object.
static AudioControl ac;
public static AudioControl GetAudioControl()
{
if(ac == null)
ac = new AudioControl();
return ac;
}
//This will return the ParticleControl object.
static ParticleControl pc;
public static ParticleControl GetParticleControl()
{
if(pc == null)
pc = new ParticleControl();
return pc;
}
and on and on..
Now I received plenty of errors telling me I can't create a "new" out of a "MonoBehaviour" class, only AddComponent. Well I personally have no idea how to convert my singleton classes in a way that it will be returned for every class as components (and single components of course, I don't want to create multiple un-related components, of course that's the main idea of singleton).
I need to return these classes in a way that I could reach each of these classes methods/functions, if they will be returned as Objects or GameObjects, then I could only use "SendMessage" which I dislike this option and try to avoid using it.
Otherwise I would have just get them as GameObjects for every script, but again, I don't want this option, since it's very restricted and not effective.
Thank You for your replies!
Answer by Bunny83 · Feb 14, 2013 at 05:35 PM
Îf a class is derived from MonoBehaviour and you want to use it as singleton, i usually use this:
public class ParticleControl : MonoBehaviour
{
private static ParticleControl m_Instance = null;
public static ParticleControl Instance
{
get
{
if (m_Instance == null)
{
m_Instance = (ParticleControl)FindObjectOfType(typeof(ParticleControl));
if (m_Instance == null)
m_Instance = (new GameObject("ParticleControl")).AddComponent<ParticleControl>();
DontDestroyOnLoad(m_Instance.gameObject);
}
return m_Instance;
}
}
}
Thanks you for your answer, however, I still don't fully understand it... can you show me the code in another class for getting this instance?
var particalControlInstance = ParticalControl.Instance;
particalControlInstance .DoSomething(myArg);
//or you can always use the .Instance
ParticalControl.Instance.DoSomething(myArg);
This is the normal way to do a singleton. Here's the official .net reference on how to implement one. Which is essentially exactly what BUnny83 posted.
Factories are for creating instances of objects. It's an entirely different pattern from a singleton. Usually singletons are accessed directly, though I've sometimes had a manager class with a reference to the singleton (mainly so I'm not typing Instance all the time).
Partical$$anonymous$$anager.Control.DoSomething(myArg);
One thing to note is that your singleton won't be created/instantiated until you access the instance property. This can be important to keep in $$anonymous$$d.
I see, so if I choose to set values to a $$anonymous$$onoBehaviour class through the inspector, and that class is used as a Singleton, will the values remain?
Also, do you have another design pattern in $$anonymous$$d, except Singleton?
Edited:
I'm getting plenty of errors in unity after these attempts to change the code (nothing wrong in the build though)
" ArgumentException: CompareBaseObjects can only be called from the main thread. Constructors and field initializers will be executed from the loading thread when loading a scene. Don't use this function in the constructor or field initializers, ins$$anonymous$$d move initialization code to the Awake or Start function. "
" UnityException: You are not allowed to call this function when declaring a variable. $$anonymous$$ove it to the line after without a variable declaration. If you are using C# don't use this function in the constructor or field initializers, Ins$$anonymous$$d move initialization to the Awake or Start function. "
$$anonymous$$y "Unity singleton" version does 2 things. If it doesn't have a cached instance it will search for an instance in the current scene. Only if there is no instance it will create one. $$anonymous$$ost manager classes are placed into an initial loader / bootstrap scene which is loaded only once at the start-up of your application. This allows you to serialize settings the Unity way and still have direct access to the class from everywhere. If you use it like this, make sure you don't access ParticalControl.Instance in Awake of another script. That might be too early and the serialized instance hasn't been created yet.
If you only need the serialized version you can replace the creation of an instance with a Debug.LogWarning() that tells you to not use it too early ;)
I've edited my last entry, I received these two problems. I have so many errors to repair since I converted my work to C#. And from some reason the singleton issue is so strange. I moved the instance to an Awake() function since I had no choice, and I don't know where are the problems, I can't debug or anything, do you have any suggestions?