using System; using System.IO; using UnityEditor; using UnityEditorInternal; using UnityEngine; namespace HybridCLR.Editor { public class ScriptableSingleton : ScriptableObject where T : ScriptableObject { private static T s_Instance; public static T Instance { get { if (!s_Instance) { CreateAndLoad(); } return s_Instance; } } private static void CreateAndLoad() { string filePath = GetFilePath(); if (!string.IsNullOrEmpty(filePath)) { var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath); s_Instance = arr.Length > 0 ? arr[0] as T : CreateInstance(); } else { Debug.LogError($"{nameof(ScriptableSingleton)}: 请指定单例存档路径! "); } } public 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() { Type typeFromHandle = typeof(T); object[] customAttributes = typeFromHandle.GetCustomAttributes(inherit: true); object[] array = customAttributes; foreach (object obj in array) { if (obj is FilePathAttribute) { FilePathAttribute filePathAttribute = obj as FilePathAttribute; return filePathAttribute.filepath; } } return string.Empty; } } [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; } } }