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.
This commit is contained in:
81
Assets/_Game/Scripts/Dialogue/DialogueActorSO.cs
Normal file
81
Assets/_Game/Scripts/Dialogue/DialogueActorSO.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Dialogue
|
||||
{
|
||||
/// <summary>
|
||||
/// 对话角色定义 SO(架构 14_NarrativeModule §3)。
|
||||
/// 将 NPC 的显示名、头像、对话气泡颜色集中在一处管理。
|
||||
/// DialogueLine.actor 引用此 SO,修改头像/名称只需改一个资产,
|
||||
/// 无需批量编辑所有对话行。
|
||||
///
|
||||
/// 资产路径:Assets/_Game/Data/Dialogue/Actors/Actor_{actorId}.asset
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "BaseGames/Dialogue/DialogueActor")]
|
||||
public class DialogueActorSO : ScriptableObject
|
||||
{
|
||||
[Header("标识")]
|
||||
[Tooltip("唯一 ID,如 \"NPC_Elder\",供 DialogueLine 引用")]
|
||||
public string actorId;
|
||||
|
||||
[Header("显示")]
|
||||
[Tooltip("本地化 Key,格式如 \"NPC_Elder_Name\"")]
|
||||
public string nameKey;
|
||||
|
||||
[Tooltip("对话 UI 中显示的头像")]
|
||||
public Sprite portrait;
|
||||
|
||||
[Tooltip("对话气泡/说话人名称的主题颜色(可选)")]
|
||||
public Color accentColor = Color.white;
|
||||
|
||||
[Tooltip("是否为玩家角色(影响对话 UI 排版方向)")]
|
||||
public bool isPlayer;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// actorId → 资产路径,5 秒 TTL,跨所有 DialogueActorSO.OnValidate 共用。
|
||||
// 与 QuestSO / DialogueSequenceSO 保持一致的 O(1) 重复检测策略。
|
||||
private static System.Collections.Generic.Dictionary<string, string> s_actorIdToPath;
|
||||
private static double s_actorIdsCacheTime = -10.0;
|
||||
|
||||
private static System.Collections.Generic.Dictionary<string, string> GetActorIdCache()
|
||||
{
|
||||
double now = UnityEditor.EditorApplication.timeSinceStartup;
|
||||
if (s_actorIdToPath != null && now - s_actorIdsCacheTime < 5.0)
|
||||
return s_actorIdToPath;
|
||||
|
||||
s_actorIdToPath = new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.Ordinal);
|
||||
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:DialogueActorSO");
|
||||
foreach (var guid in guids)
|
||||
{
|
||||
var path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||||
var actor = UnityEditor.AssetDatabase.LoadAssetAtPath<DialogueActorSO>(path);
|
||||
if (actor != null && !string.IsNullOrEmpty(actor.actorId) && !s_actorIdToPath.ContainsKey(actor.actorId))
|
||||
s_actorIdToPath[actor.actorId] = path;
|
||||
}
|
||||
s_actorIdsCacheTime = now;
|
||||
return s_actorIdToPath;
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(actorId))
|
||||
{
|
||||
Debug.LogWarning($"[DialogueActorSO] '{name}' 缺少 actorId,保存前请填写。", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检测重复 actorId:缓存路径 vs 自身路径比对(O(1)),5 秒内无需重扫。
|
||||
var cache = GetActorIdCache();
|
||||
string myPath = UnityEditor.AssetDatabase.GetAssetPath(this);
|
||||
if (!string.IsNullOrEmpty(myPath) &&
|
||||
cache.TryGetValue(actorId, out var existingPath) &&
|
||||
existingPath != myPath)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"[DialogueActorSO] actorId '{actorId}' 与 " +
|
||||
$"'{System.IO.Path.GetFileNameWithoutExtension(existingPath)}' 重复!请修改其中一个。", this);
|
||||
s_actorIdsCacheTime = -10.0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user