agreed, scriptableobjects work good for this
Every one of my games tends to get a SO called “GameSettings”:
I’ll implement it as a Singleton, and I tend to use it as the “startup” section of my game. Like so:
using UnityEngine;
using System.Collections.Generic;
using com.spacepuppy;
using com.spacepuppy.Project;
using com.spacepuppy.Scenes;
using com.spacepuppy.SPInput;
using com.spacepuppy.Utils;
using com.vivarium.UserInput;
namespace com.vivarium
{
public class Game : GameSettings
{
public const float GRAV = -20f;
public const string MAIN_INPUT = "Main.Input";
public const string SCRN_START = "StartScreen";
public static float DEFAULT_TIMEINDAY = SPTimePeriod.SECONDS_IN_DAY;
public static int DEFAULT_DAYSINSEASON = 24;
#region Singleton Access
private static Game _instance;
private static System.Action<Game> _onBeforeInitializeCallback;
public static bool Initialized { get { return _instance != null; } }
public static Game Settings { get { return _instance; } }
public static void Init(System.Action<Game> onBeforeInitializeCallback = null)
{
_onBeforeInitializeCallback = onBeforeInitializeCallback;
_instance = GameSettings.GetGameSettings<Game>();
}
#endregion
#region Fields
[Header("Resources")]
[SerializeField()]
public com.vivarium.Factories.VivariumFactoryConfiguration DefaultFactoryConfiguration;
[Header("Default Transition Settings")]
[SerializeField()]
public float DefaultTransitionDuration = 1f;
[SerializeField()]
public Color DefaultTransitionColor = Color.black;
[Header("Falling & Gravity")]
[SerializeField()]
public float GravityMagnitude = 20.0f;
[SerializeField()]
public float TerminalVelocity = -30f;
[SerializeField()]
public float TerminalDepth = -100.0f;
[Header("Drag & Drop Settings")]
public float DragInitiationMouseDownDuration = 1f;
[Header("Tank Settings")]
[SerializeField()]
[TimeUnitsSelector("Hours")]
public float DefaultStartTime;
[Range(Constants.TEMPERATURE_MIN, Constants.TEMPERATURE_MAX)]
public float DefaultTemperature = 80f;
[Range(0f, 1f)]
public float DefaultHumidity = 0.5f;
[Header("Character Settings")]
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalEnergy = 0.2M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalEnergyBuffer = 0.1M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHunger = 0.2M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHungerBuffer = 0.1M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHealth = 0.2M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHealthBuffer = 0.1M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHappiness = 0.2M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalHappinessBuffer = 0.1M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalProductivity = 0.2M;
[FixedPercent.Config(0f, 1f, true)]
public FixedPercent CriticalProductivityBuffer = 0.1M;
//[System.NonSerialized()]
//private SPSceneManager _sceneManager;
//[System.NonSerialized()]
//private VivariumUIController _ui;
//[System.NonSerialized()]
//private VivariumStats _vivar;
//private PlantCareController _plantCare;
[System.NonSerialized]
private HashSet<string> _permanentTokenLedgerCategories = new HashSet<string>();
#endregion
#region Constructor
protected override void OnInitialized()
{
_instance = this;
GameLoop.Init();
CustomTimeLayersData.RegisterTimeLayers();
if (_onBeforeInitializeCallback != null)
{
var d = _onBeforeInitializeCallback;
_onBeforeInitializeCallback = null;
d(this);
}
//################
// Initialize Services - this should always happen first, any given service shouldn't rely on accessing other services on creation
//########
{
//create services
var sceneManager = Services.Create<ISceneManager, SPSceneManager>(true);
var inputManager = Services.Create<IInputManager, SPInputManager>(true);
inputManager.Add(MAIN_INPUT, InputFactory.CreateInputDevice(MAIN_INPUT));
Services.Create<ICameraManager, com.spacepuppy.Cameras.SPCameraManager>(true);
}
//load permanent ledgertoken categories
_permanentTokenLedgerCategories.Clear();
var txt = Resources.Load<TextAsset>("PermanentTokenCategories");
if (txt != null)
{
using (var reader = new System.IO.StringReader(txt.text))
{
string ln;
while ((ln = reader.ReadLine()) != null)
{
if (!string.IsNullOrEmpty(ln))
{
_permanentTokenLedgerCategories.Add(ln.Trim());
}
}
}
}
}
#endregion
#region Properties
public HashSet<string> PermanentTokenLedgerCategories
{
get { return _permanentTokenLedgerCategories; }
}
#endregion
#region Static Methods
public static void QuitGame()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
#endregion
}
}
Note - it may inherit from GameSettings, but that just inherits from ScriptableObject. It merely exists to contain some boilerplate that all my projects have.
using UnityEngine;
namespace com.spacepuppy.Project
{
/// <summary>
/// A singleton Game entry point
/// </summary>
public abstract class GameSettings : ScriptableObject
{
public const string PATH_DEFAULTSETTINGS = "GameSettings";
public const string PATH_DEFAULTSETTINGS_FULL = @"Assets/Resources/GameSettings.asset";
#region CONSTRUCTOR
protected void Awake()
{
if (_instance != null && _instance != this)
throw new System.InvalidOperationException("Attempted to create multiple GameSettings. Please get instances of the game settings via the static interface GameSettingsBase.GetGameSettings.");
}
protected abstract void OnInitialized();
protected virtual void OnDestroy()
{
if (_instance == this)
_instance = null;
}
#endregion
#region Static Factory
private static GameSettings _instance;
public static GameSettings GetGameSettings(string path = null)
{
if(_instance == null)
{
if (path == null) path = PATH_DEFAULTSETTINGS;
//_instance = Object.Instantiate(Resources.Load(path)) as GameSettings;
_instance = Resources.Load(path) as GameSettings;
if (_instance != null) _instance.OnInitialized();
return _instance;
}
else
{
return _instance;
}
}
public static T GetGameSettings<T>(string path = null) where T : GameSettings
{
if (_instance == null)
{
if (path == null) path = PATH_DEFAULTSETTINGS;
//_instance = Object.Instantiate(Resources.Load(path)) as T;
_instance = Resources.Load(path) as T;
if (_instance != null) _instance.OnInitialized();
return _instance as T;
}
else
{
return _instance as T;
}
}
#endregion
}
}