- Home /
Singleton class always returning null
I have a simple singleton that is supposed to be setting up my background music and it's returning null when I try and instantiate it. The script is attached to a GameObject with an audio clip attached to the source.
Script attached to the game object:
using UnityEngine;
using System.Collections;
public class GameMusic : MonoBehaviour
{
public static GameMusic _instance = null;
public static GameMusic getInstance {
get {
if (_instance == null) {
_instance = GameObject.FindObjectOfType<GameMusic> ();
DontDestroyOnLoad (_instance.gameObject);
}
return _instance;
}
}
void Awake ()
{
if (_instance == null) {
_instance = getInstance;
DontDestroyOnLoad (_instance);
} else if (this != _instance) {
Destroy (this.gameObject);
}
}
}
When trying to access the instance:
public class AdjustSettings : MonoBehaviour
{
public Texture settingsMenu;
public Font customFont;
public GUIStyle backButtonStyle = null;
public float hSliderValue = 0.0f;
public static GameMusic gameMusic;
public void OnGUI ()
{
GUI.DrawTexture (new Rect (0, 0, Screen.width, Screen.height), settingsMenu);
setupStyles ();
Settings ();
}
public void Settings ()
{
float buttonXPos = Screen.width / 2;
float buttonYPos = Screen.height / 2;
float buttonWidth = 100;
float buttonHeight = 30;
if (GUI.Button (new Rect (buttonXPos - 50, Screen.height - 50, buttonWidth, buttonHeight), "", backButtonStyle)) {
Application.LoadLevel ("SettingsMenu");
}
hSliderValue = GUI.HorizontalSlider (new Rect (buttonXPos, buttonYPos, 100, 100), hSliderValue, 0.0f, 1.0f);
gameMusic.audio.volume = hSliderValue;
}
public void setupStyles ()
{
if (backButtonStyle == null) {
backButtonStyle = new GUIStyle (GUI.skin.button);
backButtonStyle.font = customFont;
backButtonStyle.hover.textColor = Color.yellow;
backButtonStyle.fontSize = 20;
}
}
void Awake ()
{
gameMusic = GameMusic.getInstance;
if (gameMusic == null)
Debug.Log ("The gameMusic instance is set to null.");
}
}
Any help would be appreciated.
PS, This is my first time posting here so please forgive any omissions as I'm certain they are unintentional and thank you!
Answer by Bunny83 · Dec 08, 2014 at 07:33 PM
Well, it's most likely that your AdjustSettings Awake is called before your GameMusic Awake function. Another possible reason is that you don't have a GameMusic script attached to any object or that object is disabled.
You might want to either setup your script execution order in the settings or change the Awake method in AdjustSettings into Start which is called after all Awake has been called.
edit
I just had another look at your singleton code. I think it would be better to implement your Awake like this:
void Awake ()
{
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad (_instance.gameObject);
}
else if (this != _instance)
{
Destroy (this.gameObject);
}
}
I'm pretty sure that inside of Awake while loading the scene FindObjectOfType can't find the instance. It's also not necessary since in Awake you can use "this".
Changing to Start() didn't help, still getting null. I'm presu$$anonymous$$g that my implementation of the static class isn't the issue?
@$$anonymous$$emnochdacoder: First of all that class isn't static. You still need an instance in the scene. If there is no instance on a GameObject somewhere in your scene, FindObjectOfType can't find anything.
So as i said in my answer are you sure you actually have the Game$$anonymous$$usic script attached to a GameObject in the scene? If not you either have to attach the script to a GameObject or change your "singleton" to support lazy initialization. There are different ways to implement that and it depends on if it's enough to just add the script to an empty gameobject or if your object is based on a prefab.
Attached is the screen shot of the game object that has the script attached. I may be doing this completely wrong and if so would like to know the correct way to set this up. In the long run all I'm trying to do is get the background audio to play across all my scenes and attach a volume slider in a settings menu to keep the in game volume level set.
Thank you for your responses by the way, I really do appreciate the help.
Stepped through with the debugger and found that _instance = GameObject.FindObjectOfType (); doesn't find the object reference. Which I would be ecstatic about if I knew why.
Answer by fafase · Dec 08, 2014 at 07:41 PM
You 'd rather go for a Singleton class:
public class SingletonMonoBehaviour<T>:MonoBehaviour where T : MonoBehaviour
{
protected T instance = null;
public T Instance{
get{
if(instance == null)
{
instance = (T)FindObjectOfType(typeof(T));
}
return instance;
}
}
}
This is a basic implementation that does not prevent multi instances. You can still add a check in the Awake or OnEnable but it needs to be called if Awake is overriden:
protected virtual void Awake(){
if(FindObjectsOfType(typeof(T)).Length > 1){
Debug.LogError("Multiple instances of " this.GetType());
}
}
This will only inform, some implementation destroy the extra one, well, I consider this wrong since if you have two instances, you cannot know which will be destroy. If one have data set in the inspector and one is added by mistake, the second may be run first and the first is destroyed and bam you are running a game that goes wrong and you do not know why... Just my view.
Then for any class you can use:
public class MyClass:Singleton<MyClass>{}
without rewriting the same code over many classes.
In an attempt to get past this issue I implemented what you have above and I'm still getting a null pointer error on the call. $$anonymous$$ore than happy to try just about anything at this point just to get this audio to play in the scenes.
Answer by Mapleman · Dec 11, 2014 at 07:38 AM
I really don't see the need for singleton class here. I'v set up my background music basicly the same way you are trying to do it, but without any singleton stuff. So, since you have your Audio gameobject, why don't just call DontDestroyOnLoad(gameObject) on Awake without any extra stuff? This way you can put your Audio gameobject on your main/start scene and it will be preserved over different scenes.
Then it's easy to access the components of your Audio-object by using GameObject.FindWithTag(...)
Or even better I think is to use some kind of notification system to control volume etc. I'm using this:
http://wiki.unity3d.com/index.php/NotificationCenter3_5
Sorry if I misunderstood your question or the need for the singleton...
Well, i think that he wants to have the gameobject in multiple scenes which of course will cause problems in combination with DontDestroyOnLoad due to possible duplicates. The best solution is probably to have the BG$$anonymous$$ script in a loading scene once. An alternative would be a lazy initilaized prefab based singleton class.
I still don't see the point :) Anyway, if you don't call DontDestroyOnLoad, the music will stop and start all over again. But if you try to just preserve the class by calling DontDestroyOnLoad(this), it actually preserves the whole gameobject and you'll end up with multiple instances. Singleton pattern in general doesn't make much sense in Unity, since if you have non-static members in your class they will be initialised on every scene switch because unity will create a new instance of that class (if attached to gameobject) no matter what if it isn't static.
Your answer
Follow this Question
Related Questions
How do I make an instance of a class global??? 1 Answer
Probably Dumb Question: Update(), Start(), etc. Don't Work Inside Classes (JavaScript)...? 2 Answers
How to list all the properties and methods from the class? 1 Answer
Is there anything wrong with using lots of static classes? 1 Answer
How to find audioclip in scene 0 Answers