SettingsManager - Handling user settings in Unity

In the last article we dealt with the Storage module, which is responsible for storing simple data. The Settings module is intended for managing a special form of this data: User settings. These can be, for example, the desired resolution, volume, language, and much more. To manage the storage of this special data I developed the SettingsService.

Why an extra service for user settings?

You may have wondered why I provided an extra service for user settings. StorageManagers are great and under the hood SettingsManager uses SimpleStorageManager to store the data. But for user settings, there are a few extra things we should think about.

When the user changes settings, we want to respond in a certain way. For example, we want to load new language files when the user changes languages. Or we might need to adjust the volume of AudioSources when the user changes the volume. Of course, we could trigger an event every time data changes in the game. But then figuring out what data is relevant to us would be pretty cumbersome.

Also, many of us will be working in teams. So we probably want to structure the stored data somehow. The main reason is that the settings need to be recorded somewhere. Of course, you can just write down what data you have stored and where. But let's not kid ourselves. Documentation is usually a rather tedious task and likes to be the first thing to fall under the rug. It would be much nicer if the data was simply structured in a class and you didn't have to document it at all.

Structuring settings data

To make the user settings easily accessible, we can group them as variables in a class. We can declare this class as serializable so that Unity can convert it to a JSON string via the JsonUtility.

Awesome Accessories already has a SettingsData class with all the basic settings needed for the various modules.

    [System.Serializable]
    public class SettingsData : ISettingsData
    {
        public float masterVolume = 1f;
        public float musicVolume = 1f;
        public float soundVolume = 1f;
        public float voiceVolume = 1f;
        public string language = "EN";

        public ISettingsData FromJson(string jsonString)
        {
            return JsonUtility.FromJson<SettingsData>(jsonString);
        }
    }

Naturally, you will also want to register your own settings. The best way to do this is to extend the class. This could look like this, for example:

using Aureola.Settings;

[System.Serializable]
public class MyAwesomeSettingsData : SettingsData, ISettingsData
{
    public bool subtitles = true;
    public bool debugging = false;
    public string shadowQuality = "medium";
    public string textureQuality = "medium";

    new public ISettingsData FromJson(string jsonString)
    {
        return JsonUtility.FromJson<MyAwesomeSettingsData>(jsonString);
    }
}

Then you have to register your own settings class with the manager and load the data afterwards.

SettingsManager.service?.Register(new MyAwesomeSettingsData());
SettingsManager.service?.Load();

Admittedly, this is still a bit awkward. I am still working on an automatism in this respect.

Access settings

Once the SettingsManager is set up and the data is loaded, you can access all settings. Important: Currently only integer, float, string and boolean are supported.

You can set variables with the corresponding Set() methods:

SettingsManager.service?.Set("myFloatValue", myFloat);
SettingsManager.service?.Set("myStringValue", myString);
SettingsManager.service?.Set("myIntValue", myInt);
SettingsManager.service?.Set("myBooleanValue", myBoolean);

As soon as you have changed a setting, the data is saved immediately. Afterwards the SettingsUpdated is triggered

You can access data with the corresponding Get() methods:

SettingsManager.service?.Get("myFloatValue", 0f);
SettingsManager.service?.Get("myStringValue", "");
SettingsManager.service?.Get("myIntValue", 0);
SettingsManager.service?.Get("myBooleanValue", false);

Event: SettingsUpdated

As mentioned before, other Managers & Behaviours must be able to react to changes in the settings. That's why every time the settings are changed, the SettingsUpdated event is published in the Settings channel. In the TranslationManager (which I will talk about in another article), the integration then looks like this, for example:

private void OnEnable()
{
    PubSubManager.service?.Subscribe(Channel.SETTINGS, typeof(SettingsUpdated), OnSettingsUpdated);
}

private void OnDisable()
{
    PubSubManager.service?.Unsubscribe(Channel.SETTINGS, typeof(SettingsUpdated), OnSettingsUpdated);
 }

private void OnSettingsUpdated(IGameEvent gameEvent)
{
    var SettingsUpdated = (SettingsUpdated) gameEvent;
    var settingsData = (SettingsData) SettingsUpdated.settings;

    _service.ChangeLanguage(settingsData.language);
}

Your Feedback is important!

What do you think of the services presented here? Is there anything missing or does something not work as expected? As always, I'm happy to listen to your feedback. Let me know what you think about this module. You can use the comment section below the article for this. You can also find other ways to contact me here. If you found a bug or want an enhancement, please create an issue in the GitHub repository. Further documentation can be found in the README of the corresponding module.

There are no comments yet.