diff --git a/Assets/10.StoryEditor/CHANGELOG.md b/Assets/10.StoryEditor/CHANGELOG.md index b452d97..0cb3913 100644 --- a/Assets/10.StoryEditor/CHANGELOG.md +++ b/Assets/10.StoryEditor/CHANGELOG.md @@ -1,6 +1,13 @@ # Changelog 此包的所有更新日志会被记录在此文件中 +## [1.1.0] - 2026-01-08 +### Added +- 新增剧本设置窗口,在窗口中打开和导出剧本 +### Fixed +- 修改Graph创建时的默认位置,防止热更分包导致脚本丢失报错 +- 修改Graph导出时对加载器的加载逻辑,解决跨包无法加载的问题 + ## [1.0.7] - 2026-01-08 ### Fixed - 处理IResource多程序集共存产生的无法选择加载器问题 diff --git a/Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs b/Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs index 1c22093..dbd7738 100644 --- a/Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs +++ b/Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs @@ -28,7 +28,12 @@ namespace Stary.Evo.StoryEditor.Editor private void OnEnable() { minSize = maxSize = new Vector2(300, 200); - _options = GetPackages(); + + // 记录Package地址并返回选项 + var packages = GraphEditorTools.GetPackages(); + _paths.Clear(); + packages.ForEach(path => _paths.Add(Path.GetFileName(path), path)); + _options = _paths.Keys.ToArray(); } private void OnGUI() @@ -39,9 +44,7 @@ namespace Stary.Evo.StoryEditor.Editor EditorGUILayout.BeginVertical(); // Package选项 - EditorGUILayout.LabelField( "请选择剧本所在的Package:" , EditorStyles.boldLabel ) ; - var newIndex = EditorGUILayout.Popup( _selectedIndex , _options ) ; - _selectedIndex = newIndex; + _selectedIndex = GraphEditorTools.Popup("请选择剧本所在的Package:", _selectedIndex, _options); GUILayout.Space(15); @@ -69,7 +72,7 @@ namespace Stary.Evo.StoryEditor.Editor { try { - CreateScriptGraph(_options[newIndex]); + CreateScriptGraph(_options[_selectedIndex]); } catch (Exception e) { @@ -82,68 +85,6 @@ namespace Stary.Evo.StoryEditor.Editor EditorGUILayout.EndHorizontal(); } - /// - /// 获取所有Module Package - /// - private string[] GetPackages() - { - // 查找Modules目录 - List modules = new(); - GetDirectoryPaths(Application.dataPath, modules, "Modules"); - if (modules.Count == 0) - { - Debug.LogError("未找到任何Modules目录"); - return Array.Empty(); - } - - // 查找package - List packages = new(); - GetDirectoryPaths(modules[0], packages, "com."); - if (packages.Count == 0) - { - Debug.LogError("未找到任何Package"); - return Array.Empty(); - } - - // 记录Package地址并返回选项 - _paths.Clear(); - packages.ForEach(path => _paths.Add(Path.GetFileName(path), path)); - return _paths.Keys.ToArray(); - } - - /// - /// 获取符合条件的目录 - /// - /// 查找起始目录 - /// 查找结果 - /// 筛选条件(title) - /// 筛选条件(tail) - private static void GetDirectoryPaths(string root, List result, string title = null, string tail = null) - { - foreach (var dir in Directory.GetDirectories(root)) - { - // ReSharper disable once ReplaceWithSingleAssignment.True - var check = true; - - // 匹配文件头 - if (!string.IsNullOrEmpty(title) && !Path.GetFileName(dir).StartsWith(title)) - { - check = false; - } - // 匹配文件尾 - if (!string.IsNullOrEmpty(tail) && !Path.GetFileName(dir).EndsWith(tail)) - { - check = false; - } - - if(check) - result.Add(dir.Replace('\\', '/')); - - // 继续往下找 - GetDirectoryPaths(dir, result, title); - } - } - /// /// 创建剧本 /// @@ -161,14 +102,15 @@ namespace Stary.Evo.StoryEditor.Editor if (_paths.TryGetValue(packageID, out var path)) { // 包体目录排空 - var graphDir = Path.Combine(path, "Main","Res","Graphs"); + var graphDir = Path.Combine(Application.dataPath, "StoryEditor", "Graphs", + $"{(string.IsNullOrEmpty(packageID) ? "default" : packageID)}"); if (!Directory.Exists(graphDir)) + { Directory.CreateDirectory(graphDir); + } // 检查资源存在性 - var graphPath = Path.Combine(graphDir, $"{_scriptName}.asset").Replace(Application.dataPath, "").Replace('\\', '/'); - graphPath = graphPath[1..]; - var graphFilePath = Path.Combine("Assets", graphPath); + var graphFilePath = Path.Combine(graphDir.Replace(Application.dataPath,"Assets"), $"{_scriptName}.asset"); var existAsset = AssetDatabase.LoadAssetAtPath(graphFilePath); if (existAsset) { @@ -178,6 +120,8 @@ namespace Stary.Evo.StoryEditor.Editor } // 创建剧本图表 + var graphPath = Path.Combine(path, "Main","Res","Graphs", $"{_scriptName}.sg.json").Replace(Application.dataPath, "").Replace('\\', '/'); + graphPath = graphPath[1..]; var graph = CreateInstance(); graph.packageID = packageID; graph.graphPath = graphPath; diff --git a/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs b/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs new file mode 100644 index 0000000..d5f85ff --- /dev/null +++ b/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace Stary.Evo.StoryEditor.Editor +{ + public static class GraphEditorTools + { + public static int Popup(string content, int currentIndex, string[] options) + { + EditorGUILayout.LabelField( "请选择剧本所在的Package:" , EditorStyles.boldLabel ) ; + return EditorGUILayout.Popup( currentIndex , options ) ; + } + + /// + /// 获取所有Module Package + /// + public static List GetPackages() + { + // 查找Modules目录 + List modules = new(); + GetDirectoryPaths(Application.dataPath, modules, "Modules"); + if (modules.Count == 0) + { + Debug.LogError("未找到任何Modules目录"); + return new(); + } + + // 查找package + List packages = new(); + GetDirectoryPaths(modules[0], packages, "com."); + if (packages.Count == 0) + { + Debug.LogError("未找到任何Package"); + return new(); + } + + return packages; + } + + /// + /// 获取符合条件的目录 + /// + /// 查找起始目录 + /// 查找结果 + /// 筛选条件(title) + /// 筛选条件(tail) + private static void GetDirectoryPaths(string root, List result, string title = null, string tail = null) + { + foreach (var dir in Directory.GetDirectories(root)) + { + // ReSharper disable once ReplaceWithSingleAssignment.True + var check = true; + + // 匹配文件头 + if (!string.IsNullOrEmpty(title) && !Path.GetFileName(dir).StartsWith(title)) + { + check = false; + } + // 匹配文件尾 + if (!string.IsNullOrEmpty(tail) && !Path.GetFileName(dir).EndsWith(tail)) + { + check = false; + } + + if(check) + result.Add(dir.Replace('\\', '/')); + + // 继续往下找 + GetDirectoryPaths(dir, result, title); + } + } + + public static IEnumerable GetAllAssemblyNames() => + AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).OrderBy(n => n); + + /// + /// 获取继承 IResource 的所有类 + /// + public static HashSet IResourceTypes(string assembly) + { + if (string.IsNullOrEmpty(assembly)) + return new(); + var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assembly); + return asm == null ? new() : asm.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(i => i.Name == nameof(IResource))).Select(t => t.Name).ToHashSet(); + } + } +} \ No newline at end of file diff --git a/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs.meta b/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs.meta new file mode 100644 index 0000000..7442c60 --- /dev/null +++ b/Assets/10.StoryEditor/Editor/Graph/GraphEditorTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7264316efc98e9e47b55b6dca12b08d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs b/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs new file mode 100644 index 0000000..e7b9ff0 --- /dev/null +++ b/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using XNodeEditor; + +namespace Stary.Evo.StoryEditor.Editor +{ + public class GraphExportWindow : EditorWindow + { + private string[] _options = Array.Empty(); + private Dictionary _paths = new(); + + private int _selectedIndex; + private int _selectedStoryIndex; + private ScriptGraph _selectedStory; + private int _selectedAssemblyIndex; + private int _selectedLoaderIndex; + + [MenuItem("Evo/剧本编辑器/打开剧本")] + private static void Open() + { + // 打开一个浮动窗口 + GetWindow( "剧本配置" ) ; + } + + private void OnEnable() + { + minSize = maxSize = new Vector2(300, 400); + + // 记录Package地址并返回选项 + var packages = GraphEditorTools.GetPackages(); + _paths.Clear(); + packages.ForEach(path => _paths.Add(Path.GetFileName(path), path)); + _options = _paths.Keys.ToArray(); + } + + private void OnGUI() + { + GUILayout.Space(15); + EditorGUILayout.BeginHorizontal(); + GUILayout.Space(10); + EditorGUILayout.BeginVertical(); + + // Package选项 + _selectedIndex = GraphEditorTools.Popup("请选择剧本所在的Package:", _selectedIndex, _options); + + GUILayout.Space(15); + + // 显示已有剧本 + try + { + string[] storys; + var packageID = _options[_selectedIndex]; + var graphDir = Path.Combine(Application.dataPath, "StoryEditor", "Graphs", + $"{(string.IsNullOrEmpty(packageID) ? "default" : packageID)}"); + if (!Directory.Exists(graphDir)) + { + storys = Array.Empty(); + } + else + { + // 选择剧本 + var guids = AssetDatabase.FindAssets($"t:{nameof(ScriptGraph)}"); + var allAssets = guids.Select(AssetDatabase.GUIDToAssetPath) + .Select(AssetDatabase.LoadAssetAtPath) + .ToArray(); + storys = allAssets.Select(asset => asset.name).ToArray(); + _selectedStoryIndex = GraphEditorTools.Popup("请选择剧本:", _selectedStoryIndex, storys); + _selectedStory = allAssets[_selectedStoryIndex]; + GUILayout.Space(15); + + // 打开剧本按钮 + if (GUILayout.Button("打开剧本")) + { + var win = GetWindow (false, _selectedStory.name); + win.graph = _selectedStory; + } + GUILayout.Space(15); + + // 选择程序集 + var assemblyNames = GraphEditorTools.GetAllAssemblyNames().ToArray(); + _selectedAssemblyIndex = GraphEditorTools.Popup("请选择加载器所在的程序集:", _selectedAssemblyIndex, assemblyNames); + _selectedStory.assembly = assemblyNames.Length > 0 ? assemblyNames[_selectedAssemblyIndex] : null; + GUILayout.Space(15); + + // 选择加载器 + var loaders = GraphEditorTools.IResourceTypes(_selectedStory.assembly).ToArray(); + _selectedLoaderIndex = GraphEditorTools.Popup("请选择加载器:", _selectedLoaderIndex, loaders); + _selectedStory.loaderType = loaders.Length > 0 ? loaders[_selectedLoaderIndex] : null; + GUILayout.Space(15); + + // 导出剧本按钮 + if (GUILayout.Button("导出剧本")) + { + _selectedStory.Export(); + } + } + } + catch (Exception e) + { + Debug.LogException(e); + } + + EditorGUILayout.EndVertical(); + GUILayout.Space(10); + EditorGUILayout.EndHorizontal(); + } + } +} \ No newline at end of file diff --git a/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs.meta b/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs.meta new file mode 100644 index 0000000..7cd742d --- /dev/null +++ b/Assets/10.StoryEditor/Editor/Graph/GraphExportWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ba732ba5b1117f549b7d0f8cb6f88449 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs b/Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs index 820e74b..fee0ee6 100644 --- a/Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs +++ b/Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs @@ -124,38 +124,17 @@ namespace Stary.Evo.StoryEditor #endregion #region 导出设置 - -#if UNITY_EDITOR - - [BoxGroup("Export")] - [SerializeField, ValueDropdown(nameof(GetAllAssemblyNames))] - private string assembly; - private IEnumerable GetAllAssemblyNames() => - AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetName().Name).OrderBy(n => n); - - /// - /// 获取继承 IResource 的所有类 - /// - private HashSet IResourceTypes - { - get - { - if (string.IsNullOrEmpty(assembly)) - return new(); - var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().Name == assembly); - return asm == null ? new() : asm.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t.GetInterfaces().Any(i => i.Name == nameof(IResource))).Select(t => t.Name).ToHashSet(); - } - } -#else - private HashSet _iResourceTypes = new(); -#endif + + [BoxGroup("Export", centerLabel:true)] + [LabelText("程序集")] + public string assembly; /// /// 资源加载方式 /// - [BoxGroup("Export", centerLabel:true)] - [LabelText("资源加载方式"), ValueDropdown(nameof(IResourceTypes)),SerializeField] - private string loaderType; + [BoxGroup("Export")] + [LabelText("资源加载方式")] + public string loaderType; private IResource _loader; /// @@ -165,10 +144,14 @@ namespace Stary.Evo.StoryEditor { get { - if (_loader == null) + if (_loader == null && !string.IsNullOrEmpty(loaderType)) { - var type = Type.GetType(loaderType); - _loader = type == null ? null : (IResource)Activator.CreateInstance(type); + var asm = AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType(loaderType)).FirstOrDefault(t => t != null) ?? AppDomain.CurrentDomain.GetAssemblies().Select(a => a.GetType(loaderType, false, false)).FirstOrDefault(t => t != null); + + if (asm != null) + { + _loader = (IResource)Activator.CreateInstance(asm); + } } return _loader; @@ -269,7 +252,7 @@ namespace Stary.Evo.StoryEditor // 将转录完成的图表数据序列化为json var json = JsonConvert.SerializeObject(graph, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }); // 文件目录排空 - var graphFilePath = Path.Combine(Application.dataPath, graphPath.Replace(".asset", ".sg.json")); + var graphFilePath = Path.Combine(Application.dataPath, graphPath); if (!Directory.Exists(Path.GetDirectoryName(graphFilePath))) Directory.CreateDirectory(Path.GetDirectoryName(graphFilePath) ?? string.Empty); // 写入json diff --git a/Assets/10.StoryEditor/package.json b/Assets/10.StoryEditor/package.json index 7c1c6c3..d336f9d 100644 --- a/Assets/10.StoryEditor/package.json +++ b/Assets/10.StoryEditor/package.json @@ -1,6 +1,6 @@ { "name": "com.staryevo.storyeditor", - "version": "1.0.7", + "version": "1.1.0", "displayName": "10.StoryEditor", "description": "可视化剧本编辑器\n1.通过可视化图表编辑剧本内容\n2.将剧本导出为json\n3.解析剧本并执行", "unity": "2021.3",