Skip to content

Instantly share code, notes, and snippets.

@Doppelkeks
Last active November 24, 2023 13:29
Show Gist options
  • Save Doppelkeks/b1c1c081a11923e2ef760c9cd770dc9a to your computer and use it in GitHub Desktop.
Save Doppelkeks/b1c1c081a11923e2ef760c9cd770dc9a to your computer and use it in GitHub Desktop.
Unity provides the SettingsProvider API to create custom project settings. Additionally, there exists the ScriptableSingleton class to create a scriptable objects that can be statically referenced and kept outside the assets folder. This combines both APIs, to create custom project settings in the editor without the hassle.
[FilePath("ProjectSettings/" + nameof(ExampleProjectSettings) + ".asset", FilePathAttribute.Location.ProjectFolder)]
public class ExampleProjectSettings : SimpleSettings<ExampleProjectSettings> {
/// <summary>
/// the display path in the project settings window
/// </summary>
private const string WINDOW_PATH = "Tools/" + nameof(ExampleProjectSettings);
public bool exampleSettingBool;
/// <summary>
/// Register the settings provider for the project settings
/// </summary>
/// <returns></returns>
[SettingsProvider]
public static SettingsProvider RegisterInProjectSettings() {
return new SimpleSettingsProvider<ExampleProjectSettings>(WINDOW_PATH);
}
}
using UnityEditor;
using UnityEngine;
public class SimpleProjectSettings<T> : ScriptableSingleton<T> where T : SimpleSettings<T> {
public void OnEnable() {
hideFlags &= ~HideFlags.NotEditable;
}
public void Save() {
base.Save(true);
}
}
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
public class SimpleSettingsProvider<T> : SettingsProvider where T : SimpleSettings<T> {
private SerializedObject serializedData;
private string headline;
public SimpleSettingsProvider(string path, string headline=null, string[] keywords = null) : base(path, SettingsScope.Project) {
this.headline = headline ?? typeof(T).Name;
this.keywords = keywords ?? (new string[] { typeof(T).Name });
}
public override void OnActivate(string searchContext, VisualElement rootElement) {
serializedData = new SerializedObject(SimpleSettings<T>.instance);
CreateHeaderAndAddStyles(rootElement);
// Create a scroll view with a inspector inside
ScrollView scrollView = new ScrollView(ScrollViewMode.Vertical);
InspectorElement inspectorRoot = new InspectorElement();
scrollView.Add(inspectorRoot);
rootElement.Add(scrollView);
// Bind a callback to save changes
// Binding it to the root is enough
rootElement.RegisterCallback<SerializedPropertyChangeEvent>(ValueChanged);
// Populate the properties
inspectorRoot.Bind(serializedData);
}
protected virtual void CreateHeaderAndAddStyles(VisualElement root) {
// create and add label
Label title = new Label(headline);
title.style.fontSize = 20;
title.style.unityFontStyleAndWeight = FontStyle.Bold;
title.style.marginBottom = 10;
title.AddToClassList(nameof(title));
title.style.paddingTop = title.style.paddingRight = 2;
title.style.paddingBottom = 0;
title.style.paddingLeft = 5;
root.Add(title);
}
protected virtual void ValueChanged(SerializedPropertyChangeEvent evt) {
(serializedData.targetObject as T).Save();
}
}
@Doppelkeks
Copy link
Author

Thanks to the Unity Essential Success team for cooking up the classes this solution extends upon.
Nonetheless, please be aware that this code has not yet been battletested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment