4
\$\begingroup\$

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);
}
\$\endgroup\$
  • 4
    \$\begingroup\$ Why would you ever use the observer pattern in C# when you can use events? \$\endgroup\$ – svick Feb 18 '16 at 21:02
  • \$\begingroup\$ I don't know much about events. Do you have a link to a tutorial? \$\endgroup\$ – Funlamb Feb 19 '16 at 3:02
2
\$\begingroup\$

I haven't looked at your implementation, but simply from the title itself I can tell you're doing unnecessary work. Event and Delegate are C# build-in features for Observer pattern. Here is a tutorial: https://youtu.be/G5R4C8BLEOc

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.