I want to see if I used the Observer pattern correctly.
Right now I'm using a VolumeManager.cs
that implements ISubject
and a MusicVolume.cs
that implements IObserver
. I'm doing this to make it easy for me to change volume along a lot of different sounds and musics.
My VolumeManager.cs
is set the execute first in the Script Execution Order
setting in Unity as to not interfere with adding to a list that has not yet been initialized yet. This class starts with many floats that can be changed in the inspector to change the games volume.
Then VolumeManager.cs
uses the singleton pattern to ensure there are no other classes like itself. Then it runs the update function to see if any changes where made. Is there a better way to check if a player has changed a volume instead of what I am doing right now with if(MainVolume != StartMainVolume)
?
Lastly it runs the 3 ISbject
functions.
public class VolumeManager : MonoBehaviour, ISubject
{
[Range(0.0f, 1.0f)]//Makes a slider to change floats in the inspector
public float MainVolume = 0.5f;
[Range(0.0f, 1.0f)]
public float SoundVolume = 0.5f;
[Range(0.0f, 1.0f)]
public float MusicVolume = 0.5f;
[Range(0.0f, 1.0f)]
public float StartMainVolume = 0.5f;
[Range(0.0f, 1.0f)]
public float StartSoundVolume = 0.5f;
[Range(0.0f, 1.0f)]
public float StartMusicVolume = 0.5f;
public static VolumeManager instance;
public List<IObserver> observers;
// Use this for initialization
void Awake()
{
observers = new List<IObserver>();
if (!instance)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
public void Update()
{
if (MainVolume != StartMainVolume || SoundVolume != StartSoundVolume || MusicVolume != StartMusicVolume)
{
StartMainVolume = MainVolume;
StartSoundVolume = SoundVolume;
StartMusicVolume = MusicVolume;
Debug.Log("Volume Manager updating!!");
notifyObservers();
}
Debug.Log(observers.Count);
}
public void registerObsever(IObserver o)
{
observers.Add(o);
}
public void removeObsever(IObserver o)
{
observers.Remove(o);
}
public void notifyObservers()
{
if (observers.Count > 0)
{
foreach (var item in observers)
{
item.update(MainVolume, SoundVolume, MusicVolume);
};
}
}
}
Here is my MusicManager.cs
. It holds its musicVolume
and mainVolume
floats to determine what volume music should be played. It also uses the singleton pattern to check for other instances of MusicManager.cs
. Then using a switch case it will play the appropriate music at the desired volume.
At the end of the class it has a public update()
that allows the ISubject
to change the MusicManagers.cs
volume.
public class MusicManager : MonoBehaviour, IObserver
{
static MusicManager instance = null;
private AudioSource music;
public float mainVolume = 0.5f;
public float musicVolume = 0.5f;
public ISubject subject;
public AudioClip SplashSound;
public AudioClip MenuMusic;
public AudioClip LevelOneMusic;
public AudioClip LevelTwoMusic;
void Start()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
}
else
{
instance = this;
subject = GameObject.Find("VolumeManager").GetComponent<ISubject>();
subject.registerObsever(this);
GameObject.DontDestroyOnLoad(gameObject);
music = GetComponent<AudioSource>();
OnLevelWasLoaded(0);
}
}
public void OnLevelWasLoaded(int level)
{
if (music.isPlaying)
{
music.Stop();
}
switch (level)
{
case 0:
music.clip = SplashSound;
break;
case 1:
music.clip = MenuMusic;
break;
case 2:
music.clip = LevelOneMusic;
break;
default:
Debug.LogError("error with music");
break;
}
music.volume = mainVolume * musicVolume;
music.Play();
}
public void update(float mainVolume, float soundVolume, float musicVolume)
{
this.mainVolume = mainVolume;
this.musicVolume = musicVolume;
}
}
Is this the correct way to use the Observer pattern? Leave no stone unturned. If you see other mistakes feel free to dig into my lack of understanding.
Both interfaces are below.
public interface ISubject
{
void registerObsever(IObserver o);
void removeObsever(IObserver o);
void notifyObservers();
}
public interface IObserver
{
void update(float mainVolume, float soundVolume, float musicVolume);
}