using UnityEngine; using System; using System.Linq; #if UNITY_EDITOR using UnityEditor; #endif namespace Coffee.MiniProfiler { internal abstract class ScriptableSettings : ScriptableObject { #if UNITY_EDITOR public virtual string GetSettingPath() { return GetSettingPath(GetType()); } internal static string GetSettingPath(Type type) { return $"Project/{ObjectNames.NicifyVariableName(type.Name.Replace("ProjectSettings", ""))}"; } #endif public virtual string GetDefaultAssetPath() { return $"Assets/ProjectSettings/{GetType().Name}.asset"; } } internal abstract class ScriptableSettings : ScriptableSettings where T : ScriptableSettings { //################################ // Public Members. //################################ public static T Instance => _instance ? _instance : _instance = GetOrCreate(); private static T _instance; protected virtual bool bootOnEditorMode => true; protected virtual bool bootOnPlayMode => true; protected abstract void OnBoot(); #if UNITY_EDITOR //################################ // Private Members. //################################ private bool enabled; private static T GetOrCreate() { return PlayerSettings.GetPreloadedAssets() .OfType() .FirstOrDefault() ?? CreateInstance(); } private void OnPlayModeStateChanged(PlayModeStateChange state) { switch (state) { case PlayModeStateChange.EnteredEditMode: enabled = true; Boot(); break; case PlayModeStateChange.ExitingEditMode: case PlayModeStateChange.ExitingPlayMode: enabled = false; break; } } private void Boot() { var preloadedAssets = PlayerSettings.GetPreloadedAssets(); var assets = preloadedAssets.OfType().ToArray(); var first = assets.FirstOrDefault() ?? this as T; // If there are no preloaded assets, registry the first asset. // If there are multiple preloaded assets, unregisters all but the first one. if (assets.Length != 1) { // The first asset is not saved. if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(first))) { if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings")) AssetDatabase.CreateFolder("Assets", "ProjectSettings"); var assetName = ObjectNames.NicifyVariableName(first.GetType().Name.Replace("ProjectSettings", "")); var assetPath = AssetDatabase.GenerateUniqueAssetPath($"Assets/ProjectSettings/{assetName}.asset"); AssetDatabase.CreateAsset(first, assetPath); } PlayerSettings.SetPreloadedAssets(preloadedAssets .Where(x => x) .Except(assets) .Concat(new[] {first}) .ToArray()); } // Another asset is registered as a preload asset. if (first != this) { UnityEngine.Debug.LogError($"Another asset '{first}' is registered as a preload '{typeof(T).Name}' asset." + $"\nThis instance ('{this}') will be ignored. Check 'Project Settings > Player > Preload Assets'", this); return; } _instance = first; // Do nothing on editor mode. if (!bootOnEditorMode && !EditorApplication.isPlayingOrWillChangePlaymode) { return; } // Do nothing on play mode. if (!bootOnPlayMode && EditorApplication.isPlayingOrWillChangePlaymode) { return; } OnBoot(); } //################################ // Unity Callbacks. //################################ private void OnEnable() { enabled = true; Boot(); EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } private void OnDisable() { EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; } protected virtual void OnValidate() { if (enabled) Boot(); } #else private static T GetOrCreate() { return CreateInstance(); } private void OnEnable() { if (!bootOnPlayMode) return; _instance = this as T; OnBoot(); } #endif } [CustomEditor(typeof(ScriptablePreferenceSettings<>), true)] internal class ScriptablePreferenceSettingsEditor : Editor { private static readonly GUIContent _button = new GUIContent("Open Project Settings"); public override void OnInspectorGUI() { if (GUILayout.Button(_button)) { SettingsService.OpenProjectSettings(((ScriptableSettings) target).GetSettingPath()); } } } internal abstract class ScriptablePreferenceSettings : ScriptableSingleton where T : ScriptablePreferenceSettings { } #if UNITY_EDITOR [CustomEditor(typeof(ScriptableSettings), true)] internal class ScriptableSettingsEditor : Editor { private static readonly GUIContent _button = new GUIContent("Open Project Settings"); public override void OnInspectorGUI() { if (GUILayout.Button(_button)) { SettingsService.OpenProjectSettings(((ScriptableSettings) target).GetSettingPath()); } } } internal class ScriptableSettingsProvider : SettingsProvider where T : ScriptableSettings { public ScriptableSettingsProvider(string path) : base(path) { } public ScriptableSettingsProvider() : base(ScriptableSettings.GetSettingPath(typeof(T))) { } protected SerializedObject serializedObject { get; private set; } protected ScriptableSettings target { get; private set; } public sealed override void OnGUI(string searchContext) { if (!target) { target = ScriptableSettings.Instance; serializedObject = new SerializedObject(target); } OnGUI(); } protected virtual void OnGUI() { serializedObject.UpdateIfRequiredOrScript(); var iterator = serializedObject.GetIterator(); var enterChildren = true; while (iterator.NextVisible(enterChildren)) { if (iterator.propertyPath != "m_Script") EditorGUILayout.PropertyField(iterator, true); enterChildren = false; } serializedObject.ApplyModifiedProperties(); } } internal class ScriptablePreferenceSettingsProvider : SettingsProvider where T : ScriptableSettings { public ScriptablePreferenceSettingsProvider(string path) : base(path, SettingsScope.User) { } public ScriptablePreferenceSettingsProvider() : base(ScriptableSettings.GetSettingPath(typeof(T)), SettingsScope.User) { } protected SerializedObject serializedObject { get; private set; } protected ScriptableSettings target { get; private set; } public sealed override void OnGUI(string searchContext) { if (!target) { target = ScriptableSettings.Instance; serializedObject = new SerializedObject(target); } OnGUI(); } protected virtual void OnGUI() { serializedObject.UpdateIfRequiredOrScript(); var iterator = serializedObject.GetIterator(); var enterChildren = true; while (iterator.NextVisible(enterChildren)) { if (iterator.propertyPath != "m_Script") EditorGUILayout.PropertyField(iterator, true); enterChildren = false; } serializedObject.ApplyModifiedProperties(); } } #endif }