using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine; namespace BaseGames.Editor { /// /// 集中管理 ScriptableObject 资产的 CRUD 操作(含 Undo 支持)。 /// public static class AssetOperations { // ── 创建 ────────────────────────────────────────────────────────────── /// /// 弹出 SaveFilePanel 让用户选择路径,创建并返回新资产。 /// 创建失败或用户取消时返回 null。 /// public static T Create(string defaultFolder, string defaultName) where T : ScriptableObject { if (!Directory.Exists(defaultFolder)) Directory.CreateDirectory(defaultFolder); string path = EditorUtility.SaveFilePanelInProject( "新建 " + typeof(T).Name, defaultName + ".asset", "asset", "选择保存路径", defaultFolder); if (string.IsNullOrEmpty(path)) return null; var asset = ScriptableObject.CreateInstance(); asset.name = Path.GetFileNameWithoutExtension(path); AssetDatabase.CreateAsset(asset, path); Undo.RegisterCreatedObjectUndo(asset, "Create " + typeof(T).Name); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return asset; } // ── 重命名 ─────────────────────────────────────────────────────────── /// /// 重命名资产(同时更新磁盘文件名和 asset.name)。 /// 返回 (true, null) 成功;(false, errorMsg) 失败。 /// public static (bool ok, string error) Rename(UnityEngine.Object asset, string newName) { if (asset == null) return (false, "资产为 null"); if (string.IsNullOrWhiteSpace(newName)) return (false, "名称不能为空"); newName = newName.Trim(); string path = AssetDatabase.GetAssetPath(asset); if (string.IsNullOrEmpty(path)) return (false, "资产不在 AssetDatabase 中"); // 先重命名磁盘文件,成功后再修改内部名称,避免 Undo 状态被失败操作污染 string err = AssetDatabase.RenameAsset(path, newName); if (!string.IsNullOrEmpty(err)) return (false, err); Undo.RecordObject(asset, "Rename " + asset.name); asset.name = newName; EditorUtility.SetDirty(asset); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return (true, null); } // ── 删除 ───────────────────────────────────────────────────────────── /// 弹出确认对话框,确认后删除资产文件。返回是否已删除。 public static bool Delete(UnityEngine.Object asset) { if (asset == null) return false; string path = AssetDatabase.GetAssetPath(asset); if (string.IsNullOrEmpty(path)) return false; if (!EditorUtility.DisplayDialog( "确认删除", $"删除资产:{asset.name}\n路径:{path}\n\n此操作不可撤销。", "删除", "取消")) return false; AssetDatabase.DeleteAsset(path); AssetDatabase.Refresh(); return true; } // ── 克隆 ───────────────────────────────────────────────────────────── /// 复制资产文件并返回克隆的资产;用户取消或失败时返回 null。 public static T Clone(T source, string defaultFolder) where T : ScriptableObject { if (source == null) return null; string srcPath = AssetDatabase.GetAssetPath(source); string path = EditorUtility.SaveFilePanelInProject( "克隆 " + source.name, source.name + "_Copy.asset", "asset", "选择保存路径", defaultFolder); if (string.IsNullOrEmpty(path)) return null; if (!AssetDatabase.CopyAsset(srcPath, path)) { Debug.LogError($"[AssetOperations] 克隆失败:{srcPath} → {path}"); return null; } AssetDatabase.Refresh(); var clone = AssetDatabase.LoadAssetAtPath(path); if (clone != null) Undo.RegisterCreatedObjectUndo(clone, "Clone " + source.name); return clone; } // ── 查询 ───────────────────────────────────────────────────────────── /// 在 AssetDatabase 中查找所有 T 类型资产。 public static List FindAll() where T : ScriptableObject { var result = new List(); string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name); foreach (var guid in guids) { string p = AssetDatabase.GUIDToAssetPath(guid); var asset = AssetDatabase.LoadAssetAtPath(p); if (asset != null) result.Add(asset); } return result; } // ── GUID 工具 ───────────────────────────────────────────────────────── public static string GetGuid(UnityEngine.Object asset) { if (asset == null) return string.Empty; return AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(asset)); } public static T LoadByGuid(string guid) where T : UnityEngine.Object { if (string.IsNullOrEmpty(guid)) return null; return AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid)); } } }