Files
zeling_v2/Assets/_Game/Scripts/Editor/Shared/AssetOperations.cs
Joywayer 446fd5dcd0 feat: Add WorldStateFlagAttribute and custom property drawer for enhanced dialogue management
- Implemented WorldStateFlagAttribute to mark string fields as world state flags.
- Created NarrativeNPCEditor for custom inspector to visualize dialogue version activation states.
- Developed WorldStateFlagDrawer to provide dropdown menu for known flags in the inspector.
- Introduced ActorModule for managing DialogueActorSO assets, including viewing, creating, and deleting actors.
- Added DialogueModule for managing DialogueSequenceSO assets with detailed previews and action bars.
- Established QuestModule for managing QuestSO assets, including objectives and branches.
- Implemented QuestManagerPostprocessor to automatically refresh QuestManager's quest list on asset changes.
2026-05-24 00:36:11 +08:00

153 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
namespace BaseGames.Editor
{
/// <summary>
/// 集中管理 ScriptableObject 资产的 CRUD 操作(含 Undo 支持)。
/// </summary>
public static class AssetOperations
{
// ── 创建 ──────────────────────────────────────────────────────────────
/// <summary>
/// 弹出 SaveFilePanel 让用户选择路径,创建并返回新资产。
/// 创建失败或用户取消时返回 null。
/// </summary>
public static T Create<T>(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<T>();
asset.name = Path.GetFileNameWithoutExtension(path);
AssetDatabase.CreateAsset(asset, path);
Undo.RegisterCreatedObjectUndo(asset, "Create " + typeof(T).Name);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return asset;
}
// ── 重命名 ───────────────────────────────────────────────────────────
/// <summary>
/// 重命名资产(同时更新磁盘文件名和 asset.name
/// 返回 (true, null) 成功;(false, errorMsg) 失败。
/// </summary>
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);
}
// ── 删除 ─────────────────────────────────────────────────────────────
/// <summary>弹出确认对话框,确认后删除资产文件。返回是否已删除。</summary>
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;
}
// ── 克隆 ─────────────────────────────────────────────────────────────
/// <summary>复制资产文件并返回克隆的资产;用户取消或失败时返回 null。</summary>
public static T Clone<T>(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<T>(path);
if (clone != null)
Undo.RegisterCreatedObjectUndo(clone, "Clone " + source.name);
return clone;
}
// ── 查询 ─────────────────────────────────────────────────────────────
/// <summary>在 AssetDatabase 中查找所有 T 类型资产。</summary>
public static List<T> FindAll<T>() where T : ScriptableObject
{
var result = new List<T>();
string[] guids = AssetDatabase.FindAssets("t:" + typeof(T).Name);
foreach (var guid in guids)
{
string p = AssetDatabase.GUIDToAssetPath(guid);
var asset = AssetDatabase.LoadAssetAtPath<T>(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<T>(string guid) where T : UnityEngine.Object
{
if (string.IsNullOrEmpty(guid)) return null;
return AssetDatabase.LoadAssetAtPath<T>(AssetDatabase.GUIDToAssetPath(guid));
}
}
}