using System; using System.IO; using System.Linq; #if UNITY_EDITOR using UnityEditorInternal; #endif using UnityEngine; namespace UnityEditor.Recorder { /// /// A class used to define a singleton instance that is stored as an asset. /// /// /// Unlike ScriptableSingleton, this class can be used outside of the editor. /// /// The type of the setting asset. abstract class SettingAsset : ScriptableObject where T : ScriptableObject { static T s_Instance; /// /// The asset instance. /// public static T Instance { get { if (s_Instance == null) s_Instance = CreateOrLoad(); return s_Instance; } } /// /// Creates a new instance. /// protected SettingAsset() { if (s_Instance != null) { Debug.LogError($"{nameof(SettingAsset)} already exists. Did you query the singleton in a constructor?"); } else { s_Instance = this as T; } } static T CreateOrLoad() { #if UNITY_EDITOR var filePath = GetFilePath(); if (!string.IsNullOrEmpty(filePath)) { s_Instance = InternalEditorUtility.LoadSerializedFileAndForget(filePath) .Cast() .FirstOrDefault(s => s != null); } #endif if (s_Instance == null) { s_Instance = CreateInstance(); s_Instance.hideFlags = HideFlags.DontSave; } return s_Instance; } #if UNITY_EDITOR /// /// Serializes the asset to disk. /// public static void Save() { if (s_Instance == null) { Debug.LogError($"Cannot save {nameof(SettingAsset)}: no instance!"); return; } var filePath = GetFilePath(); if (string.IsNullOrEmpty(filePath)) { return; } var folderPath = Path.GetDirectoryName(filePath); if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } InternalEditorUtility.SaveToSerializedFileAndForget(new[] { s_Instance }, filePath, true); } /// /// Gets the file path of the asset relative to the project root folder. /// /// The file path of the asset. protected static string GetFilePath() { foreach (var customAttribute in typeof(T).GetCustomAttributes(true)) { if (customAttribute is SettingFilePathAttribute attribute) { return attribute.FilePath; } } return string.Empty; } #endif } /// /// An attribute that specifies a file location relative to the Project folder or Unity's preferences folder. /// [AttributeUsage(AttributeTargets.Class)] class SettingFilePathAttribute : Attribute { internal string FilePath { get; } public SettingFilePathAttribute(string relativePath, Location location) { if (string.IsNullOrEmpty(relativePath)) throw new ArgumentException("Path is empty.", nameof(relativePath)); FilePath = CombineFilePath(relativePath, location); } static string CombineFilePath(string relativePath, Location location) { if (relativePath[0] == '/') relativePath = relativePath.Substring(1); switch (location) { #if UNITY_EDITOR case Location.PreferencesFolder: return InternalEditorUtility.unityPreferencesFolder + '/' + relativePath; #endif case Location.ProjectFolder: return relativePath; default: Debug.LogError(("Unhandled enum: " + location)); return relativePath; } } /// /// Specifies the folder location that Unity uses together with the relative path provided in the constructor. /// public enum Location { /// /// Use this location to save a file relative to the preferences folder. Useful for per-user files that are across all projects. /// PreferencesFolder, /// /// Use this location to save a file relative to the Project Folder. Useful for per-project files (not shared between projects). /// ProjectFolder, } } }