using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace SLSUtilities.Narrative { [CreateAssetMenu(fileName = "Story Project Database", menuName = "SLSUtilities/Story System/Story Project Database")] public class StoryProjectDatabase : SerializedScriptableObject { // 巧妙利用 Odin 的嵌套分组语法:"父分组/子分组" // 这样就可以让 TitleGroup 作为父节点显示在最上方,而 BoxGroup 嵌套在其中,解决了 Title 被包裹在 Box 里的问题。 [TitleGroup("全局剧情数据库", "集中管理所有剧情相关数据的注册中心", Alignment = TitleAlignments.Centered)] [ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "nameKey")] [BoxGroup("全局剧情数据库/角色档案 (Characters)")] [LabelText("角色档案列表 (Character Profiles)")] public List characters = new List(); [ListDrawerSettings(ShowIndexLabels = true)] [BoxGroup("全局剧情数据库/变量数据 (Variables)")] [LabelText("变量数据组 (Variable Groups)")] public List variables = new List(); [ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "keyword")] [BoxGroup("全局剧情数据库/已注册的资产 (Registered Assets)")] [LabelText("关键词词条 (Keywords)")] public List keywords = new List(); [ListDrawerSettings(ShowIndexLabels = true, ListElementLabelName = "storyId")] [BoxGroup("全局剧情数据库/剧情入口 (Narrative Entries)")] [LabelText("剧情入口路由表 (Narrative Entries)")] public List narrativeEntries = new List(); [Button("自动扫描并注册数据 (Auto-Scan Directory)", ButtonSizes.Large, Icon = SdfIconType.Search)] [GUIColor(0.4f, 0.8f, 1f)] [PropertyTooltip("自动在当前数据库所在的文件夹(及其子文件夹)中寻找所有的 CharacterData、VariableData、KeywordData 和 NarrativeEntry,并自动填入上方的列表中。")] public void AutoPopulate() { #if UNITY_EDITOR // 获取当前 Database 资产所在的目录路径 string dbPath = AssetDatabase.GetAssetPath(this); if (string.IsNullOrEmpty(dbPath)) { Debug.LogWarning("[StorySystem] 请先将数据库资产 (Database) 保存到项目目录中。"); return; } string searchDirectory = System.IO.Path.GetDirectoryName(dbPath); // 搜索 CharacterData string[] charGuids = AssetDatabase.FindAssets($"t:{nameof(CharacterData)}", new[] { searchDirectory }); characters.Clear(); foreach (var guid in charGuids) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); var charData = AssetDatabase.LoadAssetAtPath(assetPath); if (charData != null && !characters.Contains(charData)) characters.Add(charData); } // 搜索 VariableData string[] varGuids = AssetDatabase.FindAssets($"t:{nameof(VariableData)}", new[] { searchDirectory }); variables.Clear(); foreach (var guid in varGuids) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); var varData = AssetDatabase.LoadAssetAtPath(assetPath); if (varData != null && !variables.Contains(varData)) variables.Add(varData); } // 搜索 KeywordData string[] kwGuids = AssetDatabase.FindAssets($"t:{nameof(KeywordData)}", new[] { searchDirectory }); keywords.Clear(); foreach (var guid in kwGuids) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); var kwData = AssetDatabase.LoadAssetAtPath(assetPath); if (kwData != null && !keywords.Contains(kwData)) keywords.Add(kwData); } // 搜索 NarrativeEntry string[] entryGuids = AssetDatabase.FindAssets($"t:{nameof(NarrativeEntry)}", new[] { searchDirectory }); narrativeEntries.Clear(); foreach (var guid in entryGuids) { var assetPath = AssetDatabase.GUIDToAssetPath(guid); var entryData = AssetDatabase.LoadAssetAtPath(assetPath); if (entryData != null && !narrativeEntries.Contains(entryData)) narrativeEntries.Add(entryData); } EditorUtility.SetDirty(this); AssetDatabase.SaveAssets(); Debug.Log($"[StorySystem] 自动扫描完成!共注册了 {characters.Count} 个角色档案、{variables.Count} 个变量数据组、{keywords.Count} 个关键词词条 和 {narrativeEntries.Count} 个剧情入口路由表。"); #else Debug.LogWarning("自动扫描 (AutoPopulate) 只能在 Unity 编辑器环境下运行。"); #endif } } }