UHFPS
  • 👋Welcome
  • 🎲Installation
    • URP - Project Setup
    • HDRP - Project Setup
    • Packages Setup
  • 🎮Getting Started
    • References
  • ⬆️Update Process
  • 📑Changelog
  • 📍Roadmap
  • ❔Support
  • 📚Guides
    • Managing Inputs
      • Adding Inputs to UI
      • Setting up Input System
    • State Machines
      • Adding Player States
      • Adding AI States
    • Save/Load Manager
      • Previous Scene Persistency
      • Encryption
    • Customizing UI
    • Interactions
    • Inventory
    • Player Items
    • Dynamic Objects
    • Motion Controller
      • External Motions
    • Hiding System
    • Narration System
    • Jumpscares
    • Puzzles
    • Objectives
    • Cutscenes
    • Options Manager
    • Game Manager
    • Localization
    • URP Specific
  • ⚙️Integrations
    • Emerald AI 3.0
    • AI Tree
    • Meet and Talk
Powered by GitBook
On this page
  • Adding New Options
  • Observing Options
  • Creating New Options
  • Creating New OptionBehaviour
  1. Guides

Options Manager

PreviousCutscenesNextGame Manager

Last updated 1 month ago

The Options Manager component allows you to define options according to the project requirements. These can be settings related to graphics, sound, controls and other game elements. You can customize the different options according to what your game needs, creating a more customizable experience.

Adding New Options

  1. Locate the Game Options asset in the Scriptables folder.

  1. Select the Game Options asset and click the Open Options Builder button. This will launch the Options Builder window, allowing you to edit existing options or add new ones.

  1. To add new sections or options, click the plus icon beside the Options Profile name or the Section name.

You can move or rearrange sections or options by simply dragging the element to the desired location or section.

  1. After adding an option to the section, you must define the following values: Name, Title, and Prefab.

    • Name serves as the key used in the configuration file or as an identifier for the option observer.

    • Title is the display name of the option shown in the UI.

    • Prefab is the object instantiated when the options are built.

Under the prefab selection, choose the prefab you believe is best suited for your option. Keep in mind that some options use specific value types, so you cannot use a Slider (float) for an option that is based on an int value type (e.g., Language).

  1. Once you've finished modifying the options, be sure to save the asset by clicking the Save Asset button.

  2. Navigate to the Options Manager component within the GAMEMANAGER object and click Refresh Options. This will update and display any newly added sections or options under Option Links.

When new sections are added or a section transform is not assigned, you’ll need to manually update the options UI by creating a new layout group similar to the existing ones. After that, assign a transform where the options for that section will be instantiated.

  1. Finally, click Build Options to automatically remove the old option references and generate new ones based on the current Options Asset.

Observing Options

There are three ways to observe your newly created options:

  • Use the ObserveOption() method directly within your component to monitor the option value.

  • Utilize the CustomOptionObservers component to create and attach a custom observer module.

  • Employ the OptionObserver component to invoke a specified reflected type.

ObserverOption Method

Here is the code that observes the option directly (the value must be converted to the option type):

using UnityEngine;

namespace UHFPS.Runtime
{
    public class TestObserver : MonoBehaviour
    {
        public bool BoolValue;

        private void Start()
        {
            OptionsManager.ObserveOption("some_option", (value) => BoolValue = (bool)value);
        }
    }
}

CustomOptionObservers Method

In some circumstances, you will need to write a custom options observer that does not belong to any script, such as the Audio Listener class. In that case, you can write your own OptionObserverType.

using System;
using UnityEngine;

namespace UHFPS.Runtime
{
    [Serializable]
    public class OptionListenerVolume : OptionObserverType
    {
        public override string Name => "Audio Listener Volume";

        public override void OptionUpdate(object value)
        {
            if (value == null)
                return;

            AudioListener.volume = (float)value;
        }
    }
}

After writing a custom OptionObserverType, you can add the CustomOptionObservers component to the desired location and add your newly created option to the list of option observers. The Observe Option Name must be the same as the option name in the Options Builder.

OptionObserver Method

In certain situations, you may simply want to observe an option that reflects its value onto a field, property, or method. For this, the OptionObserver component is ideal. For example, if you only want to display the FPS Counter when the show_fps option is set to true, you can create a custom method that accepts a bool parameter and use it with the OptionObserver.

Creating New Options

  1. Create a new class that will inherit from the OptionModule class.

There are several functions you can override to define how an option should behave:

  • ContextName - Specifies the path and name used for the option selector.

  • OnApplyOption() - Called when options are applied through the UI. Use this to implement what happens when a user confirms their settings.

  • OnLoadOption(bool fromFile) - Invoked at Start, this loads the option settings. The fromFile parameter indicates if the data is being loaded from saved settings.

  • OnBuildOption(OptionBehaviour behaviour) - Triggered when you click Build Options. Use this to define how the option should be constructed.

  • OnBuildOptionRuntime() - A helper function to define or modify option values during runtime, useful for dynamic setups.

  1. Override OnApplyOption() so we can define what happens after the settings are confirmed.

public override void OnApplyOption()
{
    int value = (int)Value;
    bool converted = value != 0;

    if (Behaviour.IsChanged)
    {
        // do something when the setting changes
        // e.g. apply the setting
    }

    // store the value in SerializableData, which is then serialized into the config file.
    Options.SerializableData[Name] = new(converted);
}

In this example, we'll define a simple boolean setting:

  • You can use the Value parameter to quickly access the current value set through the UI.

  • The data type of the Value parameter depends on the associated Option Behaviour component. For instance, the OptionsRadio behaviour returns an integer type. Be careful to correctly convert the Value into the appropriate type.

  • You can use Behaviour.IsChanged to verify if the option was changed through the UI.

  • Finally, make sure to store the Value into SerializableData, using the option's Name as the key.

  1. Override OnLoadOption(bool fromFile) to define what happens when the setting is loaded.

public override void OnLoadOption(bool fromFile)
{
    bool optionValue = DefaultValue;

    if (fromFile && CheckOption(JTokenType.Boolean, out bool value))
        optionValue = value;

    // do something after loading the value
    // e.g. apply loaded setting

    Behaviour.SetOptionValue(optionValue);
}

Within this method, you can use the fromFile parameter to determine whether the option is being loaded from a file. If it is, use CheckOption to retrieve the correct value from the config file.

Finally, assign the value back to the Behaviour to ensure the settings UI reflects the correct value whether it's loaded from the file or using a default fallback.

  1. Override OnBuildOption(OptionBehaviour behavior) to define how the setting will be constructed.

public override void OnBuildOption(OptionBehaviour behaviour)
{
    behaviour.SetOptionData(new StorableCollection()
    {
        { "options", new GString[] { OffName, OnName } },
        { "defaultValue", DefaultValue ? 1 : 0 }
    });
}

In my case, I passed the option names and the default value into the OptionBehaviour, where I then populated the Options variable. This variable is used to cycle between the On and Off states of the setting.

In some cases, you may also need to override the OnBuildOptionRuntime() method, which allows you to populate option states dynamically when the game starts.

In this example, we used it to populate the option states with available screen resolutions, ensuring the settings reflect the current environment at runtime.

public override void OnBuildOptionRuntime()
{
    string[] resolutions = Options.Resolutions.Select(x => $"{x.width}x{x.height}").ToArray();
    GString[] gStrings = resolutions.Select(x => new GString($"{x}")).ToArray();
    Behaviour.SetOptionData(new StorableCollection() { { "options", gStrings } });
}

Creating New OptionBehaviour

  1. Create a new class that will inherit from the OptionBehaviour class.

You’ll be prompted to implement a few essential methods when creating a custom option:

  • GetOptionValue() – Return the actual value of the setting here. This value will be used during the OnApplyOption() call to apply the current setting.

  • SetOptionValue(object value) – Define how the setting should behave when a value is loaded, typically used in the OnLoadOption() method.

  • SetOptionData(StorableCollection data) – Use this method to define how the option states should be populated when using OnBuildOptionRuntime(), such as loading data like available screen resolutions or custom values.

public override void SetOptionValue(object value)
{
    int radio = Convert.ToInt32(value);
    SetOption(radio);
    IsChanged = false;
}

public override object GetOptionValue()
{
    return RadioIndex;
}

public override void SetOptionData(StorableCollection data)
{
    if(data.TryGetValue("options", out GString[] options))
        Options = options.Select(x => new GString(x)).ToArray();

    if (data.TryGetValue("defaultValue", out int value))
        RadioIndex = value;
}

public void SetOption(int index)
{
    RadioIndex = index;
    IsChanged = true;
}

There are additional helpful properties available, such as:

  • IsChanged – Indicates whether the option value has been modified through the UI. This is useful for checking if an update or save action is necessary.

There are also several predefined OptionBehaviours you can use out of the box:

  • Radio Indicators M – (Int) A radio selector with large horizontal indicators.

  • Radio Indicators S – (Int) A radio selector with smaller horizontal indicators.

  • Radio Many – (Int) A radio selector without horizontal indicators, suitable for a larger set of options.

  • Slider – (Int/Float) A simple slider for adjusting values.

  • SEPARATOR – A visual separator used to organize and distinguish different option groups in the UI.

  1. Newly created OptionBehaviours can be added to the Options Asset, which makes them available for selection within the Options Builder.

📚