- 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.
153 lines
6.5 KiB
C#
153 lines
6.5 KiB
C#
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));
|
||
}
|
||
}
|
||
}
|