Files
zeling_v2/Assets/_Game/Scripts/Editor/Shared/AssetCreationWizard.cs
Joywayer 6eaa83dc71 feat: Round 48 narrative systems improvements
- QuestSO: Add ValidateBranchCycles() DFS detection for branches[].nextQuest loop
- QuestSO: Mark three legacy prerequisite fields with v2.0 removal warning in Tooltip
- IQuestManager: Add QuestLockReason enum + QuestLockInfo struct (strongly-typed lock info)
- IQuestManager: Add GetQuestLockInfo() method to interface; GetQuestLockReason() now delegates to it
- IQuestEventSource: Add OnQuestStateChanged(questId, oldState, newState) unified event
- QuestManager: Implement GetQuestLockInfo(); fire OnQuestStateChanged on all state transitions
- DialogueManager: Add one-frame yield in HandleChoices before ShowChoices (skip-debounce fix)
- DialogueManager: Increment _playbackId in ForceEnd() to invalidate residual choice callbacks
- DialogueSequenceSO: Add UNITY_EDITOR debug log in TryGetActiveVariant on variant match
- WorldStateRegistry: Add OnBatchStateChanged event + BatchMark() batch-write API
- DialogueModule: List badge shows warning indicator for unconditional-shadowing variants
- DialogueModule: BuildVariantsCard shows logic mode (AND/OR) alongside flag conditions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 00:05:15 +08:00

162 lines
6.2 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;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
namespace BaseGames.Editor.Shared
{
/// <summary>
/// 资产快速创建向导 —— 弹出式 EditorWindow引导输入 ID 并预览文件名,
/// 一键创建 ScriptableObject 到指定文件夹。
/// 用法: AssetCreationWizard.Show&lt;QuestSO&gt;(folder, prefix, (asset, id) => { asset.questId = id; });
/// </summary>
public class AssetCreationWizard : EditorWindow
{
private string _folder;
private string _prefix;
private string _idInput = "";
private string _typeName;
private Type _assetType;
private Action<ScriptableObject, string> _onCreated;
private TextField _idField;
private Label _previewLabel;
// ── 公开入口 ─────────────────────────────────────────────────────────
/// <summary>
/// 打开向导。资产创建完成后以 (asset, id) 形式回调,调用方可在回调中自行设置 ID 字段。
/// </summary>
public static void Show<T>(string folder, string prefix, Action<T, string> onCreated)
where T : ScriptableObject
{
string displayName = typeof(T).Name.EndsWith("SO")
? typeof(T).Name[..^2]
: typeof(T).Name;
var win = CreateInstance<AssetCreationWizard>();
win.titleContent = new GUIContent($"新建 {displayName}");
win._folder = folder;
win._prefix = prefix;
win._assetType = typeof(T);
win._typeName = displayName;
win._onCreated = (so, id) => onCreated((T)so, id);
win.minSize = new Vector2(320, 132);
win.maxSize = new Vector2(480, 132);
win.ShowUtility();
}
// ── UI 构建 ──────────────────────────────────────────────────────────
private void CreateGUI()
{
var root = rootVisualElement;
root.style.paddingTop = 12;
root.style.paddingBottom = 12;
root.style.paddingLeft = 16;
root.style.paddingRight = 16;
// 说明行
var desc = new Label($"将在 {_folder} 中新建一个 {_typeName}");
desc.style.fontSize = 10;
desc.style.opacity = 0.55f;
desc.style.marginBottom = 10;
root.Add(desc);
// ID 输入行
var row = new VisualElement();
row.style.flexDirection = FlexDirection.Row;
row.style.alignItems = Align.Center;
row.style.marginBottom = 6;
var idLabel = new Label("ID");
idLabel.style.width = 36;
idLabel.style.unityTextAlign = TextAnchor.MiddleLeft;
idLabel.style.flexShrink = 0;
row.Add(idLabel);
_idField = new TextField { value = "" };
_idField.style.flexGrow = 1;
_idField.RegisterValueChangedCallback(evt =>
{
_idInput = evt.newValue;
RefreshPreview();
});
row.Add(_idField);
root.Add(row);
// 文件名预览
_previewLabel = new Label("文件名:(请输入 ID");
_previewLabel.style.fontSize = 10;
_previewLabel.style.opacity = 0.5f;
_previewLabel.style.marginBottom = 12;
root.Add(_previewLabel);
// 按钮行
var btnRow = new VisualElement();
btnRow.style.flexDirection = FlexDirection.Row;
btnRow.style.justifyContent = Justify.FlexEnd;
var cancelBtn = new Button(Close) { text = "取消" };
cancelBtn.style.width = 56;
cancelBtn.style.marginRight = 6;
btnRow.Add(cancelBtn);
var createBtn = new Button(DoCreate) { text = "创建" };
createBtn.style.width = 56;
btnRow.Add(createBtn);
root.Add(btnRow);
// 自动聚焦 ID 输入框
_idField.schedule.Execute(() => _idField.Focus()).StartingIn(50);
}
// ── 私有逻辑 ─────────────────────────────────────────────────────────
private void RefreshPreview()
{
if (_previewLabel == null) return;
_previewLabel.text = string.IsNullOrWhiteSpace(_idInput)
? "文件名:(请输入 ID"
: $"文件名:{_prefix}{_idInput}.asset";
}
private void DoCreate()
{
if (string.IsNullOrWhiteSpace(_idInput))
{
EditorUtility.DisplayDialog("ID 不能为空", "请输入有效的 ID 后再创建。", "确定");
return;
}
if (!Regex.IsMatch(_idInput, @"^[\w\-]+$"))
{
EditorUtility.DisplayDialog("ID 格式有误", "ID 只能包含字母、数字、下划线或连字符。", "确定");
return;
}
if (!Directory.Exists(_folder))
Directory.CreateDirectory(_folder);
string path = $"{_folder}/{_prefix}{_idInput}.asset";
if (File.Exists(path))
{
EditorUtility.DisplayDialog("文件已存在", $"路径已存在:\n{path}\n请更换 ID。", "确定");
return;
}
var asset = CreateInstance(_assetType) as ScriptableObject;
AssetDatabase.CreateAsset(asset, path);
Undo.RegisterCreatedObjectUndo(asset, $"创建 {_typeName} {_idInput}");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
_onCreated?.Invoke(asset, _idInput);
Close();
}
}
}