Easily Add Custom ‘Project Settings’ to Unreal Engine (.INI)

You might be placing all your settings and tweakable options in Blueprints or even hard-coded in C++. Unreal Engine does have the option to easily add more configuration settings in the INI config file system using the Developer Settings class. You are probably familiar with the existence of these configuration INI files already. DefaultGame.ini, DefaultEngine.ini, etc. are built using this class and the Unreal Editor’s Project Settings and Editor Settings use this system.

New in Unreal Engine 5 is DeveloperSettingsBackedByCVars which adds easy binding with Console Variables (CVars) and project/editor settings. I’m explaining this new feature at the bottom of this post.

This system requires some (basic) C++ to define the variables, so even without programming experience, it’s relatively easy to use.

Setting up Developer Settings & Configuration Files

// Example of configuration file content. These files are located in MyProject/Config/*.ini

[/Script/ActionRoguelike.SSaveGameSettings]
SaveSlotName=SaveSlot03
DummyTablePath=/Game/ActionRoguelike/Monsters/DT_Monsters.DT_Monsters

By deriving a new C++ class from UDeveloperSettings you can easily add your own. The [CategoryName] will be your Project + ClassName: [/Script/ActionRoguelike.SaveGameSettings] in the case of my open-source Action Roguelike GitHub project.

DeveloperSettings is a Module. Creating a UDeveloperSettings derived class will add this module to your .uproject automatically. If it doesn’t or you want all your modules in the .Build.cs file then you should add DeveloperSettings manually.

Configuration Files use key-value pairs Key=Value and support file paths and even arrays. We’ll be filling an FString and asset path to assign a DataTable via the INI file.

Defining Developer Settings in C++

Below you’ll find an example from the sample project, make sure you expose it to the editor as well (EditAnywhere) so it shows up in your Project Settings automatically (see screenshot below).

UCLASS(Config=Game, defaultconfig, meta = (DisplayName="Save Game Settings")) // Give it a better looking name in UI
class ACTIONROGUELIKE_API USSaveGameSettings : public UDeveloperSettings
{
	GENERATED_BODY()

public:
	/* Default slot name if UI doesn't specify any */ 
	UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "General")
	FString SaveSlotName;
	
	/* Soft path will be converted to content reference before use */
	UPROPERTY(Config, EditAnywhere, BlueprintReadOnly, Category = "General", AdvancedDisplay)
	TSoftObjectPtr<UDataTable> DummyTablePath;

	USSaveGameSettings();
};

Config – Exposes the variable to the INI file specified in the UCLASS in the top (Game = DefaultGame.ini)

EditAnywhere – Exposes it to the Project Settings window.

BlueprintReadOnly – Exposes variables to be accessed in Blueprint Graph via the “GetClassDefaults” node.

defaultconfig “Save object config only to Default INIs, never to local INIs.” (local INIs are in your MyProject/Saved/Config/… folder)

Config=Game – Store in DefaultGame.ini, other examples include Engine, Input.

Unreal Editor Project Settings with the new custom settings.

To access the developer settings in C++ we use the CDO (Class Default Object) as that is already automatically instanced for us and accessed using GetDefault<T>();

void USSaveGameSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
	Super::Initialize(Collection);

	const USSaveGameSettings* SGSettings = GetDefault<USSaveGameSettings>(); // Access via CDO
	// Access defaults from DefaultGame.ini
	SlotName = SGSettings->SaveSlotName;

	// Make sure it's loaded into memory .Get() only resolves if already loaded previously elsewhere in code
	UDataTable* DummyTable = SGSettings->DummyTablePath.LoadSynchronous();
}

We can’t store direct pointers to content files, but we can use soft asset paths and resolve them in code once we need them. Make sure you actually load in the asset manually as otherwise it may or may not sit in memory yet (eg. when you already opened the asset once in your current editor session).

The neat thing about configuration files is that the values are stored as plain text and not compiled to binary, unlike C++ and Blueprint. They can be changed easily even after you already packaged your game.

Developer Settings Blueprint Access

Getting read-only access to the configuration settings is very easy using the GetClassDefaults node. Make sure you mark your variables BlueprintReadOnly for them to show up.

blueprint node Get Class Defaults to access Developer Settings.

Game User Settings

If you want to store player configurable settings there is a different class available: GameUserSettings. This class already includes a bunch of settings including graphical options. Here you might want to add mouse sensitivity, FOV, etc.

DeveloperSettingsBackedByCVars

New in Unreal Engine 5.0 is DeveloperSettingsBackedByCVars which adds easy binding with Console Variables (CVars) and project/editor settings.

“The base class of auto-discovered settings object where some or all of the settings are stored in console variables instead of config variables.” – from Unreal Engine source code.

This new class lets us easily bind console variables to project settings and easily change and store defaults either per developer or project-wide.

An example of this can be found in the incredible Lyra Starter Game which was released with UE5.0. The LyraWeaponsDebugSettings has several properties for debugging trace hits. By using DeveloperSettingsBackedByCVars and the ConsoleVariable meta-specifier you can bind the variables together.

// Should we do debug drawing for bullet traces (if above zero, sets how long (in seconds)
UPROPERTY(config, EditAnywhere, Category=General, meta=(ConsoleVariable="lyra.Weapon.DrawBulletTraceDuration")
float DrawBulletTraceDuration;

The Console Variable is still defined elsewhere. In the example, you can find the CVar inside LyraGameplayAbility_RangedWeapon.cpp:

namespace LyraConsoleVariables
{
	static float DrawBulletTracesDuration = 0.0f;
	static FAutoConsoleVariableRef CVarDrawBulletTraceDuraton(
		TEXT("lyra.Weapon.DrawBulletTraceDuration"),
		DrawBulletTracesDuration,
		TEXT("Should we do debug drawing for bullet traces (if above zero, sets how long (in seconds))"),
		ECVF_Default);
}

Then access the CVar by using the Namespace and the static float, not the variable in the Settings file.

if (LyraConsoleVariables::DrawBulletTracesDuration > 0.0f)
{
	static float DebugThickness = 1.0f;
	DrawDebugLine(GetWorld(), StartTrace, EndTrace, FColor::Red, false, LyraConsoleVariables::DrawBulletTracesDuration, 0, DebugThickness);
}

This new addition can be quite nice to easily set defaults in your project settings that are either per-use or per project. The shown example specifies UCLASS(config=EditorPerProjectUserSettings) to have this stored per developer.

Closing

For more details and examples we’re using Developer Settings to configure a SaveGame system in my Unreal Engine C++ Pro Course. The project source is available to browse on GitHub. Make sure you check it out!

Follow me on Twitter for more Unreal-related content or have a look at my other C++ Tutorials and tricks such as an easy way to Auto-detect optimal graphics settings.

4 Responses

  1. Hi Tom

    Thanks for the article 🙂

    I was wondering if its possible to use UObject pointer instead of Asset references as member variable for USSaveGameSettings (referring to your example).
    So, a UClass with EditInlineNew paired with Instanced specifier for UProperty.

    I saw a similar setup in WaterEditorSettings (FWaterBodyLakeDefaults uses UWaterWavesBase ObjectPtr), but cannot get it to work. It always reset the pointer to null on editor reload.

    Regards

    Amit

    • I have never tried that either, only soft references to assets which work pretty well. You could also try to limit the allowed class to assign with things such as “AllowedClasses” or similar meta-specifiers.

  2. You are able to create custom config files that exist outside of the main default config files, as well as access them at runtime. I’m not sure how to get those files to show up in the project settings. Do they HAVE to be subclasses of developer or user settings?

Leave a comment on this post!