Search Unity

Resolved Strategy for storing application-wide constants

Discussion in 'Scripting' started by JasonC_, Aug 18, 2021.

  1. JasonC_

    JasonC_

    Joined:
    Oct 27, 2019
    Posts:
    66
    I would like to have some application-wide (i.e. all scenes) settings (such as color palettes, default fonts, etc.), editable in the inspector, that I can refer to from code in various components.

    The settings don't need to be modifiable at runtime (it's not a problem if they are; it's just not a requirement); just a design time thing to make it easier for me to tweak and preview common settings during development, or to change them if necessary in the futue.

    What's my best option?

    Unity 2020.3.16f1 LTS.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,736
    ScriptableObjects are a decent solution tailored to this type of situation.
     
    JasonC_ and lordofduct like this.
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,534
    agreed, scriptableobjects work good for this

    Every one of my games tends to get a SO called "GameSettings":
    upload_2021-8-18_13-47-33.png

    I'll implement it as a Singleton, and I tend to use it as the "startup" section of my game. Like so:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections.Generic;
    3.  
    4. using com.spacepuppy;
    5. using com.spacepuppy.Project;
    6. using com.spacepuppy.Scenes;
    7. using com.spacepuppy.SPInput;
    8. using com.spacepuppy.Utils;
    9.  
    10. using com.vivarium.UserInput;
    11.  
    12. namespace com.vivarium
    13. {
    14.  
    15.     public class Game : GameSettings
    16.     {
    17.  
    18.         public const float GRAV = -20f;
    19.         public const string MAIN_INPUT = "Main.Input";
    20.  
    21.         public const string SCRN_START = "StartScreen";
    22.  
    23.         public static float DEFAULT_TIMEINDAY = SPTimePeriod.SECONDS_IN_DAY;
    24.         public static int DEFAULT_DAYSINSEASON = 24;
    25.  
    26.         #region Singleton Access
    27.  
    28.         private static Game _instance;
    29.         private static System.Action<Game> _onBeforeInitializeCallback;
    30.  
    31.         public static bool Initialized { get { return _instance != null; } }
    32.  
    33.         public static Game Settings { get { return _instance; } }
    34.  
    35.         public static void Init(System.Action<Game> onBeforeInitializeCallback = null)
    36.         {
    37.             _onBeforeInitializeCallback = onBeforeInitializeCallback;
    38.             _instance = GameSettings.GetGameSettings<Game>();
    39.         }
    40.  
    41.         #endregion
    42.  
    43.         #region Fields
    44.  
    45.         [Header("Resources")]
    46.         [SerializeField()]
    47.         public com.vivarium.Factories.VivariumFactoryConfiguration DefaultFactoryConfiguration;
    48.  
    49.         [Header("Default Transition Settings")]
    50.         [SerializeField()]
    51.         public float DefaultTransitionDuration = 1f;
    52.         [SerializeField()]
    53.         public Color DefaultTransitionColor = Color.black;
    54.  
    55.         [Header("Falling & Gravity")]
    56.         [SerializeField()]
    57.         public float GravityMagnitude = 20.0f;
    58.         [SerializeField()]
    59.         public float TerminalVelocity = -30f;
    60.         [SerializeField()]
    61.         public float TerminalDepth = -100.0f;
    62.  
    63.         [Header("Drag & Drop Settings")]
    64.         public float DragInitiationMouseDownDuration = 1f;
    65.  
    66.         [Header("Tank Settings")]
    67.         [SerializeField()]
    68.         [TimeUnitsSelector("Hours")]
    69.         public float DefaultStartTime;
    70.         [Range(Constants.TEMPERATURE_MIN, Constants.TEMPERATURE_MAX)]
    71.         public float DefaultTemperature = 80f;
    72.         [Range(0f, 1f)]
    73.         public float DefaultHumidity = 0.5f;
    74.  
    75.         [Header("Character Settings")]
    76.         [FixedPercent.Config(0f, 1f, true)]
    77.         public FixedPercent CriticalEnergy = 0.2M;
    78.         [FixedPercent.Config(0f, 1f, true)]
    79.         public FixedPercent CriticalEnergyBuffer = 0.1M;
    80.  
    81.         [FixedPercent.Config(0f, 1f, true)]
    82.         public FixedPercent CriticalHunger = 0.2M;
    83.         [FixedPercent.Config(0f, 1f, true)]
    84.         public FixedPercent CriticalHungerBuffer = 0.1M;
    85.  
    86.         [FixedPercent.Config(0f, 1f, true)]
    87.         public FixedPercent CriticalHealth = 0.2M;
    88.         [FixedPercent.Config(0f, 1f, true)]
    89.         public FixedPercent CriticalHealthBuffer = 0.1M;
    90.  
    91.         [FixedPercent.Config(0f, 1f, true)]
    92.         public FixedPercent CriticalHappiness = 0.2M;
    93.         [FixedPercent.Config(0f, 1f, true)]
    94.         public FixedPercent CriticalHappinessBuffer = 0.1M;
    95.  
    96.         [FixedPercent.Config(0f, 1f, true)]
    97.         public FixedPercent CriticalProductivity = 0.2M;
    98.         [FixedPercent.Config(0f, 1f, true)]
    99.         public FixedPercent CriticalProductivityBuffer = 0.1M;
    100.  
    101.  
    102.         //[System.NonSerialized()]
    103.         //private SPSceneManager _sceneManager;
    104.         //[System.NonSerialized()]
    105.         //private VivariumUIController _ui;
    106.         //[System.NonSerialized()]
    107.         //private VivariumStats _vivar;
    108.         //private PlantCareController _plantCare;
    109.  
    110.         [System.NonSerialized]
    111.         private HashSet<string> _permanentTokenLedgerCategories = new HashSet<string>();
    112.  
    113.         #endregion
    114.  
    115.         #region Constructor
    116.  
    117.         protected override void OnInitialized()
    118.         {
    119.             _instance = this;
    120.             GameLoop.Init();
    121.             CustomTimeLayersData.RegisterTimeLayers();
    122.             if (_onBeforeInitializeCallback != null)
    123.             {
    124.                 var d = _onBeforeInitializeCallback;
    125.                 _onBeforeInitializeCallback = null;
    126.                 d(this);
    127.             }
    128.  
    129.             //################
    130.             // Initialize Services - this should always happen first, any given service shouldn't rely on accessing other services on creation
    131.             //########
    132.             {
    133.                 //create services
    134.                 var sceneManager = Services.Create<ISceneManager, SPSceneManager>(true);
    135.  
    136.                 var inputManager = Services.Create<IInputManager, SPInputManager>(true);
    137.                 inputManager.Add(MAIN_INPUT, InputFactory.CreateInputDevice(MAIN_INPUT));
    138.  
    139.                 Services.Create<ICameraManager, com.spacepuppy.Cameras.SPCameraManager>(true);
    140.             }
    141.  
    142.             //load permanent ledgertoken categories
    143.             _permanentTokenLedgerCategories.Clear();
    144.             var txt = Resources.Load<TextAsset>("PermanentTokenCategories");
    145.             if (txt != null)
    146.             {
    147.                 using (var reader = new System.IO.StringReader(txt.text))
    148.                 {
    149.                     string ln;
    150.                     while ((ln = reader.ReadLine()) != null)
    151.                     {
    152.                         if (!string.IsNullOrEmpty(ln))
    153.                         {
    154.                             _permanentTokenLedgerCategories.Add(ln.Trim());
    155.                         }
    156.                     }
    157.                 }
    158.             }
    159.  
    160.         }
    161.  
    162.         #endregion
    163.  
    164.         #region Properties
    165.  
    166.         public HashSet<string> PermanentTokenLedgerCategories
    167.         {
    168.             get { return _permanentTokenLedgerCategories; }
    169.         }
    170.  
    171.         #endregion
    172.  
    173.         #region Static Methods
    174.  
    175.         public static void QuitGame()
    176.         {
    177. #if UNITY_EDITOR
    178.             UnityEditor.EditorApplication.isPlaying = false;
    179. #else
    180.             Application.Quit();
    181. #endif
    182.         }
    183.  
    184.         #endregion
    185.  
    186.     }
    187.  
    188. }
    Note - it may inherit from GameSettings, but that just inherits from ScriptableObject. It merely exists to contain some boilerplate that all my projects have.
    Code (csharp):
    1. using UnityEngine;
    2.  
    3. namespace com.spacepuppy.Project
    4. {
    5.  
    6.     /// <summary>
    7.     /// A singleton Game entry point
    8.     /// </summary>
    9.     public abstract class GameSettings : ScriptableObject
    10.     {
    11.        
    12.         public const string PATH_DEFAULTSETTINGS = "GameSettings";
    13.         public const string PATH_DEFAULTSETTINGS_FULL = @"Assets/Resources/GameSettings.asset";
    14.  
    15.         #region CONSTRUCTOR
    16.  
    17.         protected void Awake()
    18.         {
    19.             if (_instance != null && _instance != this)
    20.                 throw new System.InvalidOperationException("Attempted to create multiple GameSettings. Please get instances of the game settings via the static interface GameSettingsBase.GetGameSettings.");
    21.         }
    22.  
    23.         protected abstract void OnInitialized();
    24.  
    25.         protected virtual void OnDestroy()
    26.         {
    27.             if (_instance == this)
    28.                 _instance = null;
    29.         }
    30.  
    31.         #endregion
    32.  
    33.         #region Static Factory
    34.  
    35.         private static GameSettings _instance;
    36.  
    37.         public static GameSettings GetGameSettings(string path = null)
    38.         {
    39.             if(_instance == null)
    40.             {
    41.                 if (path == null) path = PATH_DEFAULTSETTINGS;
    42.                 //_instance = Object.Instantiate(Resources.Load(path)) as GameSettings;
    43.                 _instance = Resources.Load(path) as GameSettings;
    44.                 if (_instance != null) _instance.OnInitialized();
    45.                 return _instance;
    46.             }
    47.             else
    48.             {
    49.                 return _instance;
    50.             }
    51.         }
    52.  
    53.         public static T GetGameSettings<T>(string path = null) where T : GameSettings
    54.         {
    55.             if (_instance == null)
    56.             {
    57.                 if (path == null) path = PATH_DEFAULTSETTINGS;
    58.                 //_instance = Object.Instantiate(Resources.Load(path)) as T;
    59.                 _instance = Resources.Load(path) as T;
    60.                 if (_instance != null) _instance.OnInitialized();
    61.                 return _instance as T;
    62.             }
    63.             else
    64.             {
    65.                 return _instance as T;
    66.             }
    67.         }
    68.  
    69.         #endregion
    70.  
    71.     }
    72.  
    73. }
     
    Kurt-Dekker and JasonC_ like this.