Skip to content
One Last Click Docs

Specifics

[ProjectSettings] is the entry point for defining a project setting.

It marks a ScriptableObject type as a project setting and provides optional configuration for:

  • Serialized file name and location.
  • Project Settings display name.
  • Project Settings category.
  • Editor-only behaviour.

The attribute itself does not create or load settings. It provides the metadata used by the editor systems.

ProjectSettingsEditorResolver is responsible for resolving project settings metadata.

It discovers types marked with [ProjectSettings] and provides the information required by the other editor systems, such as:

  • The ProjectSettingsAttribute for a type.
  • The serialized file path.
  • The Project Settings provider path.
  • The Project Settings provider name.

This keeps attribute and path resolution in one place rather than duplicating the logic across the editor systems.

ProjectSettingsEditorDatabase manages settings while running in the Unity Editor.

Its main responsibilities are:

  1. Load an existing settings object.
  2. Create a default instance when one does not exist.
  3. Cache the instance while it is being used.
  4. Save changes back to ProjectSettings.

Settings are loaded using Unity’s internal serialized-file APIs rather than AssetDatabase.

ProjectSettings/ExampleProjectSettings.asset
↓
LoadSerializedFileAndForget()
↓
ScriptableObject instance

This allows the settings to behave like serialized Unity objects without requiring them to exist as normal assets in the Assets folder.

When settings are modified, SaveProjectSetting() serializes the object back to its configured file path.

A pre-save processor can also modify or validate the object immediately before serialization through IProjectSettingsPreSaveProcessor.

The ProjectSettingsSettingsProvider integrates project settings into Unity’s Project Settings window.

For every [ProjectSettings] type, a SettingsProvider is created.

The provider:

  1. Gets the settings instance from ProjectSettingsEditorDatabase.
  2. Creates a standard Unity Editor for the object.
  3. Draws the normal inspector.
  4. Detects changes.
  5. Saves the settings when changes are applied.

Because the standard inspector is used, project settings automatically support the same serialized fields and object references as normal ScriptableObject inspectors.

ProjectSettingsDatabase is the runtime counterpart to ProjectSettingsEditorDatabase.

It provides access to settings through:

var settings = ProjectSettingsDatabase.Get<ExampleProjectSettings>();

The runtime database maintains a dictionary mapping each settings type to its registered instance.

Unlike the editor database, the runtime database does not load settings from disk. The instances must already have been registered before they can be retrieved.

Calling it outside Play Mode is considered invalid.

The editor-only settings system cannot be used directly by a player build because the serialized files live inside ProjectSettings rather than the normal Unity asset pipeline.

The build pipeline bridges this gap.

Before a build starts, ProjectSettingsBuildPreprocessor:

  1. Finds all non-editor-only project settings.
  2. Loads their editor instances.
  3. Creates a temporary asset container.
  4. Creates runtime copies of each settings object.
  5. Adds those copies to the container.

The temporary structure looks like:

Assets/
└── _OneLastClickBuildTemp/
└── BuiltProjectSettingsContainer.asset
├── ExampleProjectSettings
├── GraphicsProjectSettings
└── ...

The container exists only for the build process.

During scene processing, the package injects a ProjectSettingsRegisterer into the first scene in the build.

The registerer contains a reference to the generated BuiltProjectSettingsContainer.

This reference is important because it makes the settings part of Unity’s normal build dependency graph.

First Scene
│
└── ProjectSettingsRegisterer
│
└── BuiltProjectSettingsContainer
├── ExampleProjectSettings
├── GraphicsProjectSettings
└── ...

As a result, Unity includes the settings and their referenced assets in the build.

ProjectSettingsRegisterer runs with a high execution priority when the application starts.

It iterates over the settings stored in the container and registers each instance with ProjectSettingsDatabase.

After registration:

ProjectSettingsDatabase.Get<ExampleProjectSettings>();

returns the instance included in the build.

The registerer is marked with DontDestroyOnLoad, allowing the registration object to persist for the lifetime of the application.

Play Mode does not run the normal player build pipeline, so the generated build container is not required.

Instead, the package registers the editor settings directly before the first scene loads.

This allows runtime code to use the same API in both cases:

Editor Play Mode
↓
Register editor settings
↓
ProjectSettingsDatabase
Built Player
↓
ProjectSettingsRegisterer
↓
ProjectSettingsDatabase

The two databases have intentionally different responsibilities.

Component Environment Responsibility
ProjectSettingsEditorDatabase Editor Load, create, cache, and save settings.
ProjectSettingsDatabase Runtime Provide access to registered settings instances.

Runtime code should use ProjectSettingsDatabase, while editor tooling should use ProjectSettingsEditorDatabase.

The complete lifecycle is:

EDITOR
│
[ProjectSettings]
│
↓
ProjectSettingsEditorResolver
│
↓
ProjectSettingsEditorDatabase
│
Load / Create / Save
│
↓
ProjectSettings
│
│
BUILD
│
↓
Create runtime settings copies
│
↓
BuiltProjectSettingsContainer
│
↓
Inject into first scene
│
↓
BUILD
│
↓
RUNTIME
│
↓
ProjectSettingsRegisterer
│
↓
ProjectSettingsDatabase
│
↓
ProjectSettingsDatabase.Get<T>()

This separation keeps the editor storage mechanism independent from runtime access while allowing the same ScriptableObject settings to be used throughout the project lifecycle.