using System; using System.IO; using System.Linq; using UnityEditor; using UnityEditorInternal; using UnityEngine; namespace HybridCLR.Editor.Settings { public class ScriptableSingleton : ScriptableObject where T : ScriptableObject { private static T s_Instance; public static T Instance { get { if (!s_Instance) { LoadOrCreate(); } return s_Instance; } } public static T LoadOrCreate() { string filePath = GetFilePath(); if (!string.IsNullOrEmpty(filePath)) { var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath); s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance??CreateInstance(); } else { Debug.LogError($"save location of {nameof(ScriptableSingleton)} is invalid"); } return s_Instance; } public static void Save(bool saveAsText = true) { if (!s_Instance) { Debug.LogError("Cannot save ScriptableSingleton: no instance!"); return; } string filePath = GetFilePath(); if (!string.IsNullOrEmpty(filePath)) { string directoryName = Path.GetDirectoryName(filePath); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } UnityEngine.Object[] obj = new T[1] { s_Instance }; InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText); } } protected static string GetFilePath() { return typeof(T).GetCustomAttributes(inherit: true) .Cast() .FirstOrDefault(v => v != null) ?.filepath; } } [AttributeUsage(AttributeTargets.Class)] public class FilePathAttribute : Attribute { internal string filepath; /// /// 单例存放路径 /// /// 相对 Project 路径 public FilePathAttribute(string path) { if (string.IsNullOrEmpty(path)) { throw new ArgumentException("Invalid relative path (it is empty)"); } if (path[0] == '/') { path = path.Substring(1); } filepath = path; } } }