using System; using System.Linq; using System.Reflection; using UnityEngine; using Object = UnityEngine.Object; #if UNITY_EDITOR using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; #endif namespace Coffee.UIParticleInternal { public abstract class PreloadedProjectSettings : ScriptableObject #if UNITY_EDITOR { private class PreprocessBuildWithReport : IPreprocessBuildWithReport { int IOrderedCallback.callbackOrder => 0; void IPreprocessBuildWithReport.OnPreprocessBuild(BuildReport report) { Initialize(); } } [InitializeOnLoadMethod] [InitializeOnEnterPlayMode] private static void Initialize() { const BindingFlags flags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy; foreach (var t in TypeCache.GetTypesDerivedFrom(typeof(PreloadedProjectSettings<>))) { var defaultSettings = GetDefaultSettings(t); if (!defaultSettings) { // When create a new instance, automatically set it as default settings. defaultSettings = t.GetProperty("instance", flags) ?.GetValue(null, null) as PreloadedProjectSettings; } else if (GetPreloadedSettings(t).Length != 1) { SetDefaultSettings(defaultSettings); } } EditorApplication.QueuePlayerLoopUpdate(); } protected static string GetDefaultName(Type type, bool nicify) { var typeName = type.Name.Replace("ProjectSettings", ""); return nicify ? ObjectNames.NicifyVariableName(typeName) : typeName; } private static Object[] GetPreloadedSettings(Type type) { return PlayerSettings.GetPreloadedAssets() .Where(x => x && x.GetType() == type) .ToArray(); } protected static PreloadedProjectSettings GetDefaultSettings(Type type) { return GetPreloadedSettings(type).FirstOrDefault() as PreloadedProjectSettings ?? AssetDatabase.FindAssets($"t:{nameof(PreloadedProjectSettings)}") .Select(AssetDatabase.GUIDToAssetPath) .Select(AssetDatabase.LoadAssetAtPath) .FirstOrDefault(x => x && x.GetType() == type); } protected static void SetDefaultSettings(PreloadedProjectSettings asset) { if (!asset) return; var type = asset.GetType(); if (string.IsNullOrEmpty(AssetDatabase.GetAssetPath(asset))) { if (!AssetDatabase.IsValidFolder("Assets/ProjectSettings")) { AssetDatabase.CreateFolder("Assets", "ProjectSettings"); } var assetPath = $"Assets/ProjectSettings/{GetDefaultName(type, false)}.asset"; assetPath = AssetDatabase.GenerateUniqueAssetPath(assetPath); AssetDatabase.CreateAsset(asset, assetPath); } var preloadedAssets = PlayerSettings.GetPreloadedAssets(); var projectSettings = GetPreloadedSettings(type); PlayerSettings.SetPreloadedAssets(preloadedAssets .Where(x => x) .Except(projectSettings.Except(new[] { asset })) .Append(asset) .Distinct() .ToArray()); AssetDatabase.Refresh(); } } #else { } #endif public abstract class PreloadedProjectSettings : PreloadedProjectSettings where T : PreloadedProjectSettings { private static T s_Instance; #if UNITY_EDITOR private string _jsonText; public static T instance { get { if (s_Instance) return s_Instance; s_Instance = GetDefaultSettings(typeof(T)) as T; if (s_Instance) return s_Instance; s_Instance = CreateInstance(); if (!s_Instance) { s_Instance = null; return s_Instance; } SetDefaultSettings(s_Instance); return s_Instance; } } private void OnPlayModeStateChanged(PlayModeStateChange state) { switch (state) { case PlayModeStateChange.ExitingEditMode: _jsonText = EditorJsonUtility.ToJson(this); break; case PlayModeStateChange.ExitingPlayMode: if (_jsonText != null) { EditorJsonUtility.FromJsonOverwrite(_jsonText, this); _jsonText = null; } break; } } #else public static T instance => s_Instance ? s_Instance : s_Instance = CreateInstance(); #endif /// /// This function is called when the object becomes enabled and active. /// protected virtual void OnEnable() { #if UNITY_EDITOR var isDefaultSettings = !s_Instance || s_Instance == this || GetDefaultSettings(typeof(T)) == this; if (!isDefaultSettings) { DestroyImmediate(this, true); return; } EditorApplication.playModeStateChanged += OnPlayModeStateChanged; #endif if (s_Instance) return; s_Instance = this as T; } /// /// This function is called when the behaviour becomes disabled. /// protected virtual void OnDisable() { #if UNITY_EDITOR EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; #endif if (s_Instance != this) return; s_Instance = null; } #if UNITY_EDITOR protected sealed class PreloadedProjectSettingsProvider : SettingsProvider { private Editor _editor; private PreloadedProjectSettings _target; public PreloadedProjectSettingsProvider(string path) : base(path, SettingsScope.Project) { } public override void OnGUI(string searchContext) { if (!_target) { if (_editor) { DestroyImmediate(_editor); _editor = null; } _target = instance; _editor = Editor.CreateEditor(_target); } _editor.OnInspectorGUI(); } } #endif } }