Skip to content

Instantly share code, notes, and snippets.

@floere
Last active September 25, 2024 20:02
Show Gist options
  • Save floere/82abea6d79201911979196b002ed9d76 to your computer and use it in GitHub Desktop.
Save floere/82abea6d79201911979196b002ed9d76 to your computer and use it in GitHub Desktop.
Godot Config global for loading/storing user settings
extends Node
# This is the "global class" Config.
## Emitted once when the Config is loaded for the first time (after the game is loaded).
signal loaded
## Emitted when an audio volume was set.
signal audio_volume_set(bus_name: String, value_in_db: float)
## Emitted when the mouse cursor size was set.
signal visual_cursor_size_set(size: Vector2)
## Emitted when display fullscreen was set.
signal display_fullscreen_set(value: bool)
## Emitted when the display size was set.
signal display_size_set(size: Vector2i)
# Use as follows:
#
# Example 1: A volume slider.
# NOTE: the node is named like the Audio Bus
#
# extends HSlider
#
# func _enter_tree() -> void:
# self.value_changed.connect(func(_changed):
# Config.save_linear_volume(self.name, self.value / self.max_value))
# Config.loaded.connect(func():
# self.value = Config.get_linear_volume(self.name) * max_value)
#
#
# Example 2: A fullscreen checkbox.
#
# extends CheckBox
#
# func _enter_tree() -> void:
# Config.loaded.connect(func():
# self.button_pressed = Config.display_fullscreen)
# self.pressed.connect(func():
# Config.save_display_fullscreen(self.button_pressed))
## The filename of the user settings.
const settings_filename = "user://settings.cfg"
# Configuration variables.
# These are the variables which always hold the latest settings.
# Use these (or the convenience `get_` methods) to access the current settings.
# Some emit events. Listen to these to actually update something in the Game,
# such as the volume.
# Structure: <section>_<variable_name>
# They will be stored under <section> with the name <variable_name>.
## Display
var display_fullscreen: bool = false:
set(value):
display_fullscreen = value
display_fullscreen_set.emit(value)
var display_size: Vector2 = Vector2i(1024, 768):
set(value):
display_size = value
display_size_set.emit(value)
## Visual
var visual_cursor_size: float = 1.0:
set(value):
visual_cursor_size = value
visual_cursor_size_set.emit(Vector2(value, value))
var visual_font_name: String = "Pixel"
## Audio
## Use setters that emit signals when you want to set values elsewhere in the game.
## For example, when the volume of an audio bus needs to be set, the
## AudioManager should listen to the signal and update the actual volume
## on the bus.
var audio_master_enabled = true
var audio_master_volume = 1.0:
set(value):
audio_master_volume = value
audio_volume_set.emit("Master", value)
var audio_music_enabled = true
var audio_music_volume = 1.0:
set(value):
audio_music_volume = value
audio_volume_set.emit("Music", value)
var audio_sound_enabled = true
var audio_sound_volume = 1.0:
set(value):
audio_sound_volume = value
audio_volume_set.emit("Sound", value)
## The configuration variables.
var configuration_variables: Array = []
func _ready():
# Load variables into a list for dynamic handling.
load_configuration_variables()
# Load parameters from the config file when the game has finished loading.
Events.game_loaded.connect(load_config)
func load_configuration_variables() -> void:
var temporary_configuration_variables = get_script().get_script_property_list()
# Pop the script name itself.
temporary_configuration_variables.pop_front()
# Map to only the variable names.
configuration_variables = temporary_configuration_variables \
.map(func(variable): return variable.get("name")) \
# Remove the variable that holds the configuration variables itself.
.filter(func(variable_name): return variable_name != "configuration_variables")
func load_config():
setup()
# React this this signal to initialize values on sliders/checkboxes etc.
loaded.emit()
# Convenience methods - use these to have fixed method names
# (instead of checkboxes and sliders having to use eg. `save("visual_cursor_size", value)`).
func get_linear_volume(bus_name) -> float:
var value = get("audio_%s_volume" % bus_name.to_lower())
return db_to_linear(value)
func save_linear_volume(bus_name, value) -> void:
var value_in_db = linear_to_db(value)
save("audio_%s_volume" % bus_name.to_lower(), value_in_db)
func save_font_name(value) -> void:
save("visual_font_name", value)
func save_cursor_size(value) -> void:
save("visual_cursor_size", value)
func save_display_size(value: Vector2i) -> void:
save("display_size", value)
func save_display_fullscreen(value: bool) -> void:
save("display_fullscreen", value)
# Helper methods.
## This creates a config file if needed and fills it with default values.
## If a config file exists, will load the values into the variables.
func setup() -> void:
var config = ConfigFile.new()
var err = config.load(settings_filename)
for configuration_variable in configuration_variables:
# Splits up the var name into section_some_name -> section and some_name.
var section_and_key = split(configuration_variable)
var section = section_and_key[0]
var key = section_and_key[1]
# Initialize value in variable from file if possible / in file if necessary.
if not err == ERR_FILE_NOT_FOUND:
# If there is an entry in the file.
if config.has_section_key(section, key):
# Set variable from config file.
set("%s_%s" % [section, key], config.get_value(section, key))
else:
# Otherwise store it in the file.
config.set_value(section, key, get(configuration_variable))
config.save(settings_filename)
## Helper function to store a variable in the config file.
func save(variable_name, value):
# Store in variable.
set(variable_name, value)
# Store in config file.
var section_and_key = split(variable_name)
var section = section_and_key[0]
var key = section_and_key[1]
# Load the file before saving into it.
var config = ConfigFile.new()
var err = config.load(settings_filename)
if err:
print("Error when loading config file: ", err)
else:
config.set_value(section, key, value)
config.save(settings_filename)
## Helper function to split a configuration variable name into section and variable name.
func split(variable_name) -> Array:
return variable_name.split("_", true, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment