【a】可视化剧本编辑器 10.StoryEditor
This commit is contained in:
8
Assets/10.StoryEditor/Editor.meta
Normal file
8
Assets/10.StoryEditor/Editor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c40af5c425ac0874aabc6979f2a6e289
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/Editor/Graph.meta
Normal file
8
Assets/10.StoryEditor/Editor/Graph.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7418c79b3f95ac9428a8836bccd4cb7a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
193
Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs
Normal file
193
Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
// ReSharper disable Unity.PerformanceCriticalCodeInvocation
|
||||
|
||||
namespace Stary.Evo.StoryEditor.Editor
|
||||
{
|
||||
public class GraphCreateWindow : EditorWindow
|
||||
{
|
||||
private int _selectedIndex;
|
||||
private string _scriptName = "graph";
|
||||
private bool _showTip;
|
||||
private string _tip;
|
||||
|
||||
private string[] _options = Array.Empty<string>();
|
||||
private Dictionary<string, string> _paths = new();
|
||||
|
||||
[MenuItem("Evo/剧本编辑器/创建剧本")]
|
||||
private static void Open()
|
||||
{
|
||||
// 打开一个浮动窗口
|
||||
GetWindow<GraphCreateWindow>( "创建配置" ) ;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
minSize = maxSize = new Vector2(300, 200);
|
||||
_options = GetPackages();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Space(15);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.BeginVertical();
|
||||
|
||||
// Package选项
|
||||
EditorGUILayout.LabelField( "请选择剧本所在的Package:" , EditorStyles.boldLabel ) ;
|
||||
var newIndex = EditorGUILayout.Popup( _selectedIndex , _options ) ;
|
||||
_selectedIndex = newIndex;
|
||||
|
||||
GUILayout.Space(15);
|
||||
|
||||
EditorGUILayout.LabelField( "请输入剧本名称" , EditorStyles.boldLabel ) ;
|
||||
var newName = EditorGUILayout.TextField(_scriptName);
|
||||
if (_scriptName != newName)
|
||||
{
|
||||
_scriptName = newName;
|
||||
_showTip = false;
|
||||
}
|
||||
|
||||
if (_showTip)
|
||||
{
|
||||
GUIStyle redBold = new GUIStyle(EditorStyles.boldLabel)
|
||||
{
|
||||
normal = { textColor = Color.red }
|
||||
};
|
||||
EditorGUILayout.LabelField(_tip, redBold) ;
|
||||
}
|
||||
|
||||
GUILayout.Space(15);
|
||||
|
||||
// 创建按钮
|
||||
if (GUILayout.Button("创建剧本"))
|
||||
{
|
||||
try
|
||||
{
|
||||
CreateScriptGraph(_options[newIndex]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
GUILayout.Space(10);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有Module Package
|
||||
/// </summary>
|
||||
private string[] GetPackages()
|
||||
{
|
||||
// 查找Modules目录
|
||||
List<string> modules = new();
|
||||
GetDirectoryPaths(Application.dataPath, modules, "Modules");
|
||||
if (modules.Count == 0)
|
||||
{
|
||||
Debug.LogError("未找到任何Modules目录");
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
// 查找package
|
||||
List<string> packages = new();
|
||||
GetDirectoryPaths(modules[0], packages, "com.");
|
||||
if (packages.Count == 0)
|
||||
{
|
||||
Debug.LogError("未找到任何Package");
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
// 记录Package地址并返回选项
|
||||
_paths.Clear();
|
||||
packages.ForEach(path => _paths.Add(Path.GetFileName(path), path));
|
||||
return _paths.Keys.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取符合条件的目录
|
||||
/// </summary>
|
||||
/// <param name="root">查找起始目录</param>
|
||||
/// <param name="result">查找结果</param>
|
||||
/// <param name="title">筛选条件(title)</param>
|
||||
/// <param name="tail">筛选条件(tail)</param>
|
||||
private static void GetDirectoryPaths(string root, List<string> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建剧本
|
||||
/// </summary>
|
||||
/// <param name="packageID">包体ID</param>
|
||||
private void CreateScriptGraph(string packageID)
|
||||
{
|
||||
// 检查剧本名称是否有效
|
||||
if (string.IsNullOrEmpty(_scriptName))
|
||||
{
|
||||
_tip = "剧本名称不能为空";
|
||||
_showTip = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_paths.TryGetValue(packageID, out var path))
|
||||
{
|
||||
// 包体目录排空
|
||||
var graphDir = Path.Combine(path, "Main","Res","Graphs");
|
||||
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 existAsset = AssetDatabase.LoadAssetAtPath<ScriptGraph>(graphFilePath);
|
||||
if (existAsset)
|
||||
{
|
||||
_tip = "该名称的剧本已存在";
|
||||
_showTip = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建剧本图表
|
||||
var graph = CreateInstance<ScriptGraph>();
|
||||
graph.packageID = packageID;
|
||||
graph.graphPath = graphPath;
|
||||
|
||||
AssetDatabase.CreateAsset(graph, graphFilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError($"未记录Package路径:{packageID}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs.meta
Normal file
11
Assets/10.StoryEditor/Editor/Graph/GraphCreateWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7ef12db2a2d4ab4d869971023fcd175
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
14
Assets/10.StoryEditor/Editor/Graph/ScriptGraphEditor.cs
Normal file
14
Assets/10.StoryEditor/Editor/Graph/ScriptGraphEditor.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using XNodeEditor;
|
||||
|
||||
namespace Stary.Evo.StoryEditor.Editor
|
||||
{
|
||||
[CustomNodeGraphEditor(typeof(ScriptGraph))]
|
||||
public class ScriptGraphEditor : NodeGraphEditor
|
||||
{
|
||||
public override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
NodeEditorWindow.current.panOffset = new(-350, -75);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/Editor/Graph/ScriptGraphEditor.cs.meta
Normal file
11
Assets/10.StoryEditor/Editor/Graph/ScriptGraphEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0280f1b09f736a549a585dfe16f0b7da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/Editor/Node.meta
Normal file
8
Assets/10.StoryEditor/Editor/Node.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b06721b22b647a540a75c6f31a275c04
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Assets/10.StoryEditor/Editor/Node/BeginNodeEditor.cs
Normal file
19
Assets/10.StoryEditor/Editor/Node/BeginNodeEditor.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using UnityEditor;
|
||||
using XNodeEditor;
|
||||
|
||||
namespace Stary.Evo.StoryEditor.Editor
|
||||
{
|
||||
[CustomNodeEditor(typeof(BeginNode))]
|
||||
public class BeginNodeEditor : NodeEditor
|
||||
{
|
||||
public override void OnBodyGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("剧本开始");
|
||||
base.OnBodyGUI();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/Editor/Node/BeginNodeEditor.cs.meta
Normal file
11
Assets/10.StoryEditor/Editor/Node/BeginNodeEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ec8ed12f5fca194db8bf75ce458fb23
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
19
Assets/10.StoryEditor/Editor/Node/EndNodeEditor.cs
Normal file
19
Assets/10.StoryEditor/Editor/Node/EndNodeEditor.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using UnityEditor;
|
||||
using XNodeEditor;
|
||||
|
||||
namespace Stary.Evo.StoryEditor.Editor
|
||||
{
|
||||
[CustomNodeEditor(typeof(EndNode))]
|
||||
public class EndNodeEditor : NodeEditor
|
||||
{
|
||||
public override void OnBodyGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("剧本结束");
|
||||
base.OnBodyGUI();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/Editor/Node/EndNodeEditor.cs.meta
Normal file
11
Assets/10.StoryEditor/Editor/Node/EndNodeEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3651de4913e805c459198efe5e22e685
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
30
Assets/10.StoryEditor/README.md
Normal file
30
Assets/10.StoryEditor/README.md
Normal file
@@ -0,0 +1,30 @@
|
||||
<h1>可视化剧本编辑器</h1>
|
||||
|
||||
<div style="line-height:2.5">
|
||||
|
||||
<h3>创建剧本</h3>
|
||||
|
||||
1. 点击Unity顶部菜单栏`Evo/剧本编辑器/创建剧本`
|
||||
2. 在打开的小窗口中选择要创建剧本的包,然后点击`创建剧本`
|
||||
3. 新创建的剧本会在`*/Modules/[包体ID]/Main/Res/Graphs`路径下生成
|
||||
|
||||
<h3>编辑剧本</h3>
|
||||
|
||||
1. 双击剧本打开可视化编辑窗口
|
||||
2. 右键空白处可以新建节点(节点类型详见后续附录)
|
||||
3. 按住Exit端口(或其他Output端口)可以拖拽出一条连接线,将其拖到任一节点的Enter端口(或其他Input端口)可以在两个节点之间建立连接,原则上禁止将一个节点的Exit端口与其自身的Enter端口相连,其造成的死循环问题自负
|
||||
4. 原则上剧本需要从`Begin节点`连接到`End节点`
|
||||
5. 剧本会自动保存,但建议在关闭窗口前Ctrl+S进行手动保存以防万一
|
||||
|
||||
<h3>导出剧本</h3>
|
||||
|
||||
1. 导出剧本前需要先选择`资源加载方式`,为此需要创建一个继承`IResource`接口的类,实现接口后选中剧本,即可在Inspector面板选择资源加载方式
|
||||
2. 选择资源加载方式后,点击`导出`即可在同级目录下生成剧本的json文件
|
||||
|
||||
<h3>使用剧本</h3>
|
||||
|
||||
1. 使用前需要先调用ScriptPlayer.Init初始化剧本执行模块,并根据需要将字幕组件(SpriteRenderer)和音频组件(AudioSource)传入,加载器(IResource)是必传项
|
||||
2. 确保剧本的json文件能够被加载到(例如热更环境下需要将其打包到对应的AB包中),随后调用ScriptPlayer.Play即可播放剧本
|
||||
3. 在场景结束时需要调用ScriptPlayer.Release释放资源,以防止内存泄漏
|
||||
|
||||
</div>
|
||||
7
Assets/10.StoryEditor/README.md.meta
Normal file
7
Assets/10.StoryEditor/README.md.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c353f6ac45228214bad134444f74f490
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime.meta
Normal file
8
Assets/10.StoryEditor/RunTime.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9652a98bfb5f6774ebed08cc80657c5c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Data.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Data.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 63c081c2eb3009043870d3700881058e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Data/Graph.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Data/Graph.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52e52a16b8e597e48a3c645e65005b09
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
29
Assets/10.StoryEditor/RunTime/Data/Graph/GraphData.cs
Normal file
29
Assets/10.StoryEditor/RunTime/Data/Graph/GraphData.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class GraphData
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本名称
|
||||
/// </summary>
|
||||
public string name;
|
||||
/// <summary>
|
||||
/// 节点
|
||||
/// </summary>
|
||||
public List<NodeData> nodes = new();
|
||||
/// <summary>
|
||||
/// 起始节点索引
|
||||
/// </summary>
|
||||
public int startNodeIndex;
|
||||
/// <summary>
|
||||
/// 资源加载类型
|
||||
/// </summary>
|
||||
public string resourceType;
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Data/Graph/GraphData.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Data/Graph/GraphData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c69fad22bb97abf42a50d1b2d0e74a42
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Data/Node.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Data/Node.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 77eaac9f599722345a3a3ae21a895fbd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/10.StoryEditor/RunTime/Data/Node/BeginNodeData.cs
Normal file
25
Assets/10.StoryEditor/RunTime/Data/Node/BeginNodeData.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
[Serializable]
|
||||
public class BeginNodeData : NodeData
|
||||
{
|
||||
public List<int> next = new();
|
||||
|
||||
public BeginNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public BeginNodeData(NodeData data)
|
||||
{
|
||||
name = data.name;
|
||||
type = NodeType.Begin;
|
||||
}
|
||||
|
||||
public new BeginNodePlayer GetPlayer(GraphPlayer graph)=>
|
||||
new(graph, this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc62c71e8fb75c24eb096c74a651309d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
25
Assets/10.StoryEditor/RunTime/Data/Node/EndNodeData.cs
Normal file
25
Assets/10.StoryEditor/RunTime/Data/Node/EndNodeData.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
[Serializable]
|
||||
public class EndNodeData : NodeData
|
||||
{
|
||||
public List<int> pre = new();
|
||||
|
||||
public EndNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public EndNodeData(NodeData data)
|
||||
{
|
||||
name = data.name;
|
||||
type = NodeType.End;
|
||||
}
|
||||
|
||||
public new EndNodePlayer GetPlayer(GraphPlayer graph) =>
|
||||
new(graph, this);
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Data/Node/EndNodeData.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Data/Node/EndNodeData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 407d564c2ca4c65499f8bf38949aec58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/10.StoryEditor/RunTime/Data/Node/FlowNodeData.cs
Normal file
34
Assets/10.StoryEditor/RunTime/Data/Node/FlowNodeData.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 空节点(流程节点)数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class FlowNodeData : NodeData
|
||||
{
|
||||
public List<int> pre = new();
|
||||
public List<int> next = new();
|
||||
|
||||
/// <summary>
|
||||
/// 后续连接执行类型
|
||||
/// </summary>
|
||||
public NodeExecuteType executeType = NodeExecuteType.Async;
|
||||
|
||||
public FlowNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public FlowNodeData(NodeData data)
|
||||
{
|
||||
name = data.name;
|
||||
type = NodeType.Empty;
|
||||
}
|
||||
|
||||
public new FlowNodePlayer GetPlayer(GraphPlayer graph)=>
|
||||
new(graph, this);
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Data/Node/FlowNodeData.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Data/Node/FlowNodeData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0bafbcc38995f04c9189cc5a1491099
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/10.StoryEditor/RunTime/Data/Node/NodeData.cs
Normal file
23
Assets/10.StoryEditor/RunTime/Data/Node/NodeData.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class NodeData
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点名称
|
||||
/// </summary>
|
||||
public string name = "Unnamed Node";
|
||||
/// <summary>
|
||||
/// 节点类型
|
||||
/// </summary>
|
||||
public NodeType type = NodeType.Empty;
|
||||
|
||||
public NodePlayer GetPlayer(GraphPlayer graph)=>
|
||||
new(graph, this);
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Data/Node/NodeData.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Data/Node/NodeData.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d46e1dcd9310eed41a27d6e0c8c5066b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本段落节点数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class ScriptParagraphNodeData : FlowNodeData
|
||||
{
|
||||
/// <summary>
|
||||
/// 字幕路径
|
||||
/// </summary>
|
||||
public ResourcePathData captionPath;
|
||||
/// <summary>
|
||||
/// 语音路径
|
||||
/// </summary>
|
||||
public ResourcePathData audioPath;
|
||||
|
||||
public ScriptParagraphNodeData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public ScriptParagraphNodeData(FlowNodeData data) : base(data)
|
||||
{
|
||||
type = NodeType.Paragraph;
|
||||
executeType = data.executeType;
|
||||
}
|
||||
|
||||
public new ScriptParagraphNodePlayer GetPlayer(GraphPlayer graph) =>
|
||||
new(graph,this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2ddf1032a2ad2541ab9ddae9772d0d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/10.StoryEditor/RunTime/NodeEnum.cs
Normal file
42
Assets/10.StoryEditor/RunTime/NodeEnum.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public enum NodeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 空节点(流程节点)
|
||||
/// </summary>
|
||||
Empty,
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
Begin,
|
||||
/// <summary>
|
||||
/// 结束节点
|
||||
/// </summary>
|
||||
End,
|
||||
/// <summary>
|
||||
/// 剧本段落
|
||||
/// </summary>
|
||||
Paragraph
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点执行类型
|
||||
/// </summary>
|
||||
public enum NodeExecuteType
|
||||
{
|
||||
/// <summary>
|
||||
/// 异步
|
||||
/// </summary>
|
||||
[LabelText("异步"), Tooltip("异步执行意味着Exit连接的多条支路将同时进行")]
|
||||
Async,
|
||||
/// <summary>
|
||||
/// 同步
|
||||
/// </summary>
|
||||
[LabelText("同步"), Tooltip("同步执行意味着Exit连接的多条支路将依次进行")]
|
||||
Sync
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/NodeEnum.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/NodeEnum.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d4772e98c371834284ea14f4b641510
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Player.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Player.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edf21b4cd79b9cf459596d41bb2a1da2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Player/Graph.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Player/Graph.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a786bcde0f40a044b8e37ee058a125d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
101
Assets/10.StoryEditor/RunTime/Player/Graph/GraphPlayer.cs
Normal file
101
Assets/10.StoryEditor/RunTime/Player/Graph/GraphPlayer.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class GraphPlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本名称
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// 节点
|
||||
/// </summary>
|
||||
public List<NodePlayer> Nodes = new();
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
public BeginNodePlayer BeginNode;
|
||||
/// <summary>
|
||||
/// 结束节点
|
||||
/// </summary>
|
||||
public EndNodePlayer EndNode;
|
||||
|
||||
/// <summary>
|
||||
/// 当前进行中的节点
|
||||
/// </summary>
|
||||
public List<NodePlayer> CurrentNodes = new();
|
||||
|
||||
/// <summary>
|
||||
/// 播放完成
|
||||
/// </summary>
|
||||
public bool Finished;
|
||||
|
||||
/// <summary>
|
||||
/// CTS
|
||||
/// </summary>
|
||||
public CancellationTokenSource Cts = new();
|
||||
|
||||
public GraphPlayer(GraphData data, IResource loader)
|
||||
{
|
||||
// 检查加载方式
|
||||
if (loader.GetType().ToString() != data.resourceType)
|
||||
{
|
||||
Debug.LogError($"加载方式不匹配,剧本无法加载:{data.name}\n需要的加载方式: {data.resourceType}");
|
||||
return;
|
||||
}
|
||||
|
||||
Name = data.name;
|
||||
data.nodes.ForEach(node =>
|
||||
{
|
||||
switch (node)
|
||||
{
|
||||
case BeginNodeData beginNode:
|
||||
Nodes.Add(beginNode.GetPlayer(this));
|
||||
break;
|
||||
case EndNodeData endNode:
|
||||
Nodes.Add(endNode.GetPlayer(this));
|
||||
break;
|
||||
case ScriptParagraphNodeData paraNode:
|
||||
Nodes.Add(paraNode.GetPlayer(this));
|
||||
break;
|
||||
case FlowNodeData flowNode:
|
||||
Nodes.Add(flowNode.GetPlayer(this));
|
||||
break;
|
||||
}
|
||||
});
|
||||
BeginNode = (BeginNodePlayer)Nodes[data.startNodeIndex];
|
||||
BeginNode.Connect();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行剧本
|
||||
/// </summary>
|
||||
public async UniTask Execute()
|
||||
{
|
||||
// 初始化结束标志
|
||||
Finished = false;
|
||||
// 执行剧本
|
||||
_ = BeginNode.Execute();
|
||||
// 等待剧本完成
|
||||
await UniTask.WaitUntil(() => Finished);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止剧本
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
// 取消并重置CancelToken
|
||||
Cts.Cancel();
|
||||
Cts = new();
|
||||
// 标记剧本结束
|
||||
Finished = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce92dbf7b238c4442b75ea500c804107
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Player/Node.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Player/Node.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: adab23e0caf805e45b47a9a0c88ec076
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/10.StoryEditor/RunTime/Player/Node/BeginNodePlayer.cs
Normal file
43
Assets/10.StoryEditor/RunTime/Player/Node/BeginNodePlayer.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class BeginNodePlayer : NodePlayer
|
||||
{
|
||||
public new BeginNodeData Data;
|
||||
public List<NodePlayer> Next = new();
|
||||
|
||||
public BeginNodePlayer(GraphPlayer graph, BeginNodeData data) : base(graph, data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public override bool Connect()
|
||||
{
|
||||
Debug.Log("BeginNodePlayer: Connect");
|
||||
if (!base.Connect())
|
||||
return false;
|
||||
|
||||
Data.next.ForEach(index => Next.Add(Graph.Nodes[index]));
|
||||
Next.ForEach(node => node.Connect());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async UniTask Execute()
|
||||
{
|
||||
Init();
|
||||
Debug.Log($"开始执行剧本: {Graph.Name}");
|
||||
await base.Execute();
|
||||
await MoveNext();
|
||||
}
|
||||
|
||||
public override UniTask MoveNext()
|
||||
{
|
||||
Next.ForEach(node => node?.Execute());
|
||||
return base.MoveNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a5fb5ecf3bf3ef64996bd3a7905884f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/10.StoryEditor/RunTime/Player/Node/EndNodePlayer.cs
Normal file
38
Assets/10.StoryEditor/RunTime/Player/Node/EndNodePlayer.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class EndNodePlayer : NodePlayer
|
||||
{
|
||||
public new EndNodeData Data;
|
||||
|
||||
public List<NodePlayer> Pre = new();
|
||||
|
||||
public EndNodePlayer(GraphPlayer graph, EndNodeData data) : base(graph, data)
|
||||
{
|
||||
Data = data;
|
||||
}
|
||||
|
||||
public override bool Connect()
|
||||
{
|
||||
if(!base.Connect())
|
||||
return false;
|
||||
|
||||
Data.pre.ForEach(index => Pre.Add(Graph.Nodes[index]));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override UniTask Execute()
|
||||
{
|
||||
Init();
|
||||
Debug.Log($"剧本执行完成: {Graph.Name}");
|
||||
// 标记剧本完成
|
||||
Graph.Finished = true;
|
||||
ScriptPlayer.ReleaseGraph();
|
||||
return base.Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6894cd8522d600447966dc11b2a13905
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
66
Assets/10.StoryEditor/RunTime/Player/Node/FlowNodePlayer.cs
Normal file
66
Assets/10.StoryEditor/RunTime/Player/Node/FlowNodePlayer.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class FlowNodePlayer : NodePlayer
|
||||
{
|
||||
public new FlowNodeData Data;
|
||||
|
||||
public List<NodePlayer> Pre = new();
|
||||
public List<NodePlayer> Next = new();
|
||||
|
||||
/// <summary>
|
||||
/// 后续连接执行类型
|
||||
/// </summary>
|
||||
public NodeExecuteType ExecuteType;
|
||||
|
||||
public FlowNodePlayer(GraphPlayer graph, FlowNodeData data) : base(graph, data)
|
||||
{
|
||||
Data = data;
|
||||
ExecuteType = data.executeType;
|
||||
}
|
||||
|
||||
public override bool Connect()
|
||||
{
|
||||
if(!base.Connect())
|
||||
return false;
|
||||
|
||||
Data.pre.ForEach(index => Pre.Add(Graph.Nodes[index]));
|
||||
Data.next.ForEach(index => Next.Add(Graph.Nodes[index]));
|
||||
Next.ForEach(node => node.Connect());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
public override async UniTask Execute()
|
||||
{
|
||||
await base.Execute();
|
||||
await MoveNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向下继续执行
|
||||
/// </summary>
|
||||
public override async UniTask MoveNext()
|
||||
{
|
||||
// 异步执行(并行)
|
||||
if (ExecuteType == NodeExecuteType.Async)
|
||||
{
|
||||
Next.ForEach(node => node?.Execute());
|
||||
}
|
||||
// 同步执行(串行)
|
||||
else
|
||||
{
|
||||
foreach (var node in Next.Where(node => node != null))
|
||||
{
|
||||
await node.Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58805d324b439d242a445138bc3a33a2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
65
Assets/10.StoryEditor/RunTime/Player/Node/NodePlayer.cs
Normal file
65
Assets/10.StoryEditor/RunTime/Player/Node/NodePlayer.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class NodePlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点名称
|
||||
/// </summary>
|
||||
protected string Name;
|
||||
/// <summary>
|
||||
/// 图表
|
||||
/// </summary>
|
||||
protected GraphPlayer Graph;
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
protected NodeData Data;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化标志
|
||||
/// </summary>
|
||||
protected bool Initialized;
|
||||
|
||||
public NodePlayer(GraphPlayer graph, NodeData data)
|
||||
{
|
||||
Graph = graph;
|
||||
Name = data.name;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接节点
|
||||
/// </summary>
|
||||
public virtual bool Connect()
|
||||
{
|
||||
if(Initialized)
|
||||
return false;
|
||||
Initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
public virtual void Init() => Graph.CurrentNodes.Add(this);
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
public virtual UniTask Execute()
|
||||
{
|
||||
Graph.CurrentNodes.Remove(this);
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
/// <summary>
|
||||
/// 向下继续执行
|
||||
/// </summary>
|
||||
public virtual UniTask MoveNext() => UniTask.CompletedTask;
|
||||
/// <summary>
|
||||
/// 停止执行
|
||||
/// </summary>
|
||||
public virtual UniTask Stop() => UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Player/Node/NodePlayer.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Player/Node/NodePlayer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2b76627cea0be464c864e744ae0f0b45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public class ScriptParagraphNodePlayer : FlowNodePlayer
|
||||
{
|
||||
public new ScriptParagraphNodeData Data;
|
||||
|
||||
/// <summary>
|
||||
/// 字幕路径
|
||||
/// </summary>
|
||||
public ResourcePathData CaptionPath;
|
||||
/// <summary>
|
||||
/// 语音路径
|
||||
/// </summary>
|
||||
public ResourcePathData AudioPath;
|
||||
|
||||
public ScriptParagraphNodePlayer(GraphPlayer graph, ScriptParagraphNodeData data) : base(graph, data)
|
||||
{
|
||||
Data = data;
|
||||
CaptionPath = data.captionPath;
|
||||
AudioPath = data.audioPath;
|
||||
}
|
||||
|
||||
public override async UniTask Execute()
|
||||
{
|
||||
Init();
|
||||
await ScriptPlayer.PlayScriptPara(this, Graph.Cts.Token);
|
||||
await base.Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b263f1eba1f46094cab95e1c34ff2605
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
149
Assets/10.StoryEditor/RunTime/Player/ScriptPlayer.cs
Normal file
149
Assets/10.StoryEditor/RunTime/Player/ScriptPlayer.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using DG.Tweening;
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本播放器
|
||||
/// </summary>
|
||||
public static class ScriptPlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源加载工具
|
||||
/// </summary>
|
||||
private static IResource _loader;
|
||||
|
||||
/// <summary>
|
||||
/// 动画单次时长
|
||||
/// </summary>
|
||||
private const float AnimationTime = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// 字幕组件
|
||||
/// </summary>
|
||||
private static SpriteRenderer _caption;
|
||||
/// <summary>
|
||||
/// 音频组件
|
||||
/// </summary>
|
||||
private static AudioSource _audio;
|
||||
|
||||
/// <summary>
|
||||
/// 当前执行的剧本
|
||||
/// </summary>
|
||||
private static GraphPlayer _graph;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化剧本播放器
|
||||
/// </summary>
|
||||
/// <param name="loader">资源加载工具</param>
|
||||
/// <param name="audio">音频组件</param>
|
||||
/// <param name="caption">字幕组件</param>
|
||||
public static void Init(IResource loader, AudioSource audio = null, SpriteRenderer caption = null)
|
||||
{
|
||||
_loader = loader;
|
||||
_caption = caption;
|
||||
_audio = audio;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 释放资源
|
||||
/// </summary>
|
||||
public static void Release()
|
||||
{
|
||||
_loader = null;
|
||||
_caption = null;
|
||||
_audio = null;
|
||||
Resources.UnloadUnusedAssets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放剧本
|
||||
/// </summary>
|
||||
/// <param name="packageID">包体ID</param>
|
||||
/// <param name="scriptName">剧本名称(不用加_txt后缀)</param>
|
||||
public static async UniTask Play(string packageID, string scriptName)
|
||||
{
|
||||
// 资源加载工具排空
|
||||
if (_loader == null)
|
||||
{
|
||||
Debug.LogError("资源加载工具未准备好");
|
||||
return;
|
||||
}
|
||||
|
||||
// 加载剧本
|
||||
ResourcePathData path = new(packageID,scriptName);
|
||||
var json = await _loader.Load<TextAsset>(path);
|
||||
var scriptData = JsonConvert.DeserializeObject<GraphData>(json.text, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
|
||||
if (scriptData == null)
|
||||
{
|
||||
Debug.LogError($"剧本加载失败:[{path.packageID}]{path.path}");
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析并执行剧本
|
||||
_graph = new(scriptData, _loader);
|
||||
await _graph.Execute();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放剧本段落
|
||||
/// </summary>
|
||||
/// <param name="para">段落数据</param>
|
||||
/// <param name="ct">CT</param>
|
||||
public static async UniTask PlayScriptPara(ScriptParagraphNodePlayer para, CancellationToken ct)
|
||||
{
|
||||
// 设置变量
|
||||
if (_caption)
|
||||
_caption.sprite = await _loader.Load<Sprite>(para.CaptionPath);
|
||||
if (_audio)
|
||||
_audio.clip = await _loader.Load<AudioClip>(para.AudioPath);
|
||||
|
||||
// 淡入
|
||||
_caption?.DOColor(Color.white, AnimationTime);
|
||||
_audio?.DOFade(1, AnimationTime);
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(AnimationTime), cancellationToken:ct);
|
||||
|
||||
// 等音频播放完
|
||||
if (_audio)
|
||||
{
|
||||
_audio.Play();
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(_audio.clip.length), cancellationToken:ct);
|
||||
}
|
||||
|
||||
// 淡出
|
||||
_caption?.DOColor(Color.clear, AnimationTime);
|
||||
_audio?.DOFade(0, AnimationTime);
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(AnimationTime), cancellationToken:ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止剧本
|
||||
/// </summary>
|
||||
public static async UniTask Stop()
|
||||
{
|
||||
_graph.Stop();
|
||||
|
||||
var hasAnim = false;
|
||||
if (_caption && _caption.color != Color.clear)
|
||||
{
|
||||
_caption?.DOColor(Color.clear, AnimationTime);
|
||||
hasAnim = true;
|
||||
}
|
||||
if (_audio && _audio.volume > 0)
|
||||
{
|
||||
_audio?.DOFade(0, AnimationTime);
|
||||
hasAnim = true;
|
||||
}
|
||||
if(hasAnim)
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(AnimationTime));
|
||||
|
||||
ReleaseGraph();
|
||||
}
|
||||
|
||||
public static void ReleaseGraph() => _graph = null;
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Player/ScriptPlayer.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Player/ScriptPlayer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b9bd336685230b746bc67a3bb9dc2102
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/Resource.meta
Normal file
8
Assets/10.StoryEditor/RunTime/Resource.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6e59434304b735b44b6f025c51615cfa
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/10.StoryEditor/RunTime/Resource/IResource.cs
Normal file
24
Assets/10.StoryEditor/RunTime/Resource/IResource.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
public interface IResource
|
||||
{
|
||||
/// <summary>
|
||||
/// 加载资源
|
||||
/// Json => 资源
|
||||
/// </summary>
|
||||
/// <param name="pathData">资源路径</param>
|
||||
/// <typeparam name="T">资源类型</typeparam>
|
||||
UniTask<T> Load<T>(ResourcePathData pathData) where T : Object;
|
||||
|
||||
/// <summary>
|
||||
/// 保存资源
|
||||
/// 资源 => Json
|
||||
/// </summary>
|
||||
/// <param name="asset">资源</param>
|
||||
/// <param name="packageID">包体ID</param>
|
||||
UniTask<ResourcePathData> Save<T>(T asset, string packageID = null) where T : Object;
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/RunTime/Resource/IResource.cs.meta
Normal file
11
Assets/10.StoryEditor/RunTime/Resource/IResource.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 68c399cab4c86fc4b961516a2802b94d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
38
Assets/10.StoryEditor/RunTime/Resource/ResourcePathData.cs
Normal file
38
Assets/10.StoryEditor/RunTime/Resource/ResourcePathData.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源路径数据
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct ResourcePathData
|
||||
{
|
||||
/// <summary>
|
||||
/// 包体ID
|
||||
/// </summary>
|
||||
public string packageID;
|
||||
|
||||
/// <summary>
|
||||
/// 资源路径
|
||||
/// </summary>
|
||||
public string path;
|
||||
|
||||
public ResourcePathData(string packageID, string path)
|
||||
{
|
||||
this.packageID = packageID;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void AddPath(params string[] tail)
|
||||
{
|
||||
if(tail == null || tail.Length == 0)
|
||||
return;
|
||||
|
||||
foreach (var t in tail)
|
||||
{
|
||||
path = string.IsNullOrEmpty(path) ? t : System.IO.Path.Combine(path, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fd990a65a7964740b2d98d2557afbb8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/VisualEditor.meta
Normal file
8
Assets/10.StoryEditor/RunTime/VisualEditor.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 48bedbe80b198ef4d8811fac3b41d204
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/VisualEditor/Graph.meta
Normal file
8
Assets/10.StoryEditor/RunTime/VisualEditor/Graph.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f25ed558908de741b709bb8f3df2427
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
272
Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs
Normal file
272
Assets/10.StoryEditor/RunTime/VisualEditor/Graph/ScriptGraph.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.Utilities;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
[Serializable]
|
||||
public class ScriptGraph : NodeGraph
|
||||
{
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
[SerializeField, ReadOnly]
|
||||
private BeginNode begin;
|
||||
/// <summary>
|
||||
/// 结束节点
|
||||
/// </summary>
|
||||
[SerializeField, ReadOnly]
|
||||
private EndNode end;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化标识
|
||||
/// </summary>
|
||||
[SerializeField, ReadOnly]
|
||||
private bool initialized;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (nodes == null)
|
||||
{
|
||||
Debug.LogError("Graph异常,无有效节点列表存在");
|
||||
return;
|
||||
}
|
||||
|
||||
// 初始化
|
||||
if (!initialized)
|
||||
{
|
||||
_ = DelayInitialize();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorUtility.SetDirty(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
// 清理所有节点
|
||||
nodes.ForEach(DestroyImmediate);
|
||||
nodes.Clear();
|
||||
}
|
||||
|
||||
#region 初始化
|
||||
|
||||
/// <summary>
|
||||
/// 延迟初始化
|
||||
/// </summary>
|
||||
private async UniTask DelayInitialize()
|
||||
{
|
||||
// 等待Graph资产落盘
|
||||
#if UNITY_EDITOR
|
||||
while (string.IsNullOrEmpty(UnityEditor.AssetDatabase.GetAssetPath(this)))
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(0.01));
|
||||
}
|
||||
#endif
|
||||
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(0.01));
|
||||
|
||||
// 创建初始节点
|
||||
InitBeginNode();
|
||||
InitEndNode();
|
||||
CreateSampleScriptParagraph();
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化起始节点
|
||||
/// </summary>
|
||||
private void InitBeginNode()
|
||||
{
|
||||
var existing = nodes.FirstOrDefault(n => n is BeginNode) as BeginNode;
|
||||
if (existing != null)
|
||||
{
|
||||
begin = existing;
|
||||
return;
|
||||
}
|
||||
|
||||
begin = NodeBase.Create<BeginNode>(this, "Begin", Vector2.zero);
|
||||
}
|
||||
/// <summary>
|
||||
/// 初始化结束节点
|
||||
/// </summary>
|
||||
private void InitEndNode()
|
||||
{
|
||||
var existing = nodes.FirstOrDefault(n => n is EndNode) as EndNode;
|
||||
if (existing != null)
|
||||
{
|
||||
end = existing;
|
||||
return;
|
||||
}
|
||||
|
||||
end = NodeBase.Create<EndNode>(this, "End", new Vector2(NodeBase.DefaultNodeXInterval, NodeBase.DefaultNodeYInterval)* 2);
|
||||
}
|
||||
/// <summary>
|
||||
/// 创建剧本段落样例
|
||||
/// </summary>
|
||||
private void CreateSampleScriptParagraph()
|
||||
{
|
||||
// 创建新的剧本段落
|
||||
var paragraph = ScriptParagraphNode.Create(this, $"{name}_para_sample", new Vector2(NodeBase.DefaultNodeXInterval, NodeBase.DefaultNodeYInterval));
|
||||
|
||||
// 将剧本与起始节点和结束节点相连
|
||||
begin.GetOutputPort("exit").Connect(paragraph.GetInputPort("enter"));
|
||||
paragraph.GetOutputPort("exit").Connect(end.GetInputPort("enter"));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 导出设置
|
||||
|
||||
/// <summary>
|
||||
/// 资源加载方式
|
||||
/// </summary>
|
||||
[BoxGroup("Export", centerLabel:true)]
|
||||
[LabelText("资源加载方式"), ValueDropdown(nameof(_iResourceTypes)),SerializeField]
|
||||
private string loaderType;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// 获取继承 IResource 的所有类
|
||||
/// </summary>
|
||||
private HashSet<string> _iResourceTypes = AssemblyUtilities.GetTypes(AssemblyCategory.Scripts)
|
||||
.Where(t => t.IsClass && typeof(IResource).IsAssignableFrom(t)).Select(t => t.ToString()).ToHashSet();
|
||||
#else
|
||||
private HashSet<string> _iResourceTypes = new();
|
||||
#endif
|
||||
|
||||
private IResource _loader;
|
||||
/// <summary>
|
||||
/// 资源加载器
|
||||
/// </summary>
|
||||
public IResource Loader
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_loader == null)
|
||||
{
|
||||
var type = Type.GetType(loaderType);
|
||||
_loader = type == null ? null : (IResource)Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
return _loader;
|
||||
}
|
||||
}
|
||||
|
||||
[BoxGroup("Export")]
|
||||
[LabelText("包体ID")]
|
||||
public string packageID;
|
||||
|
||||
[BoxGroup("Export")]
|
||||
[LabelText("Graph目录地址")]
|
||||
public string graphPath;
|
||||
|
||||
[BoxGroup("Export")]
|
||||
[Button("导出")]
|
||||
public async void Export()
|
||||
{
|
||||
// 资源加载方式排空
|
||||
if (Loader == null)
|
||||
{
|
||||
Debug.LogError("导出失败,没有选择资源加载方式");
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<Node, NodeData> dataMatch = new();
|
||||
Dictionary<NodeData, NodeBase> nodeMatch = new();
|
||||
|
||||
// 创建图表数据
|
||||
GraphData graph = new();
|
||||
graph.name = name;
|
||||
graph.resourceType = loaderType;
|
||||
|
||||
// 转录节点数据
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if(node is not NodeBase bNode)
|
||||
continue;
|
||||
|
||||
NodeData dNode = new();
|
||||
switch (bNode)
|
||||
{
|
||||
case BeginNode beginNode:
|
||||
dNode = await beginNode.Export();
|
||||
break;
|
||||
case EndNode endNode:
|
||||
dNode = await endNode.Export();
|
||||
break;
|
||||
case ScriptParagraphNode paraNode:
|
||||
dNode = await paraNode.Export();
|
||||
break;
|
||||
case FlowNode flowNode:
|
||||
dNode = await flowNode.Export();
|
||||
break;
|
||||
default:
|
||||
Debug.LogError($"节点{node.name}[type:{node.GetType()}]没有定义对应的数据转换逻辑,该节点将被跳过,此举将可能导致graph出现流程断裂或其他未知异常");
|
||||
break;
|
||||
}
|
||||
|
||||
dataMatch.Add(bNode, dNode);
|
||||
nodeMatch.Add(dNode, bNode);
|
||||
graph.nodes.Add(dNode);
|
||||
}
|
||||
|
||||
// 转录节点连接
|
||||
foreach (var data in nodeMatch.Keys)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
// 起始节点
|
||||
case BeginNodeData beginData:
|
||||
// 记录图表首个节点的索引
|
||||
graph.startNodeIndex = graph.nodes.IndexOf(beginData);
|
||||
// 连接后部节点
|
||||
nodeMatch[beginData].GetOutputPort("exit").GetConnections().ForEach(oNode => beginData.next.Add(graph.nodes.IndexOf(dataMatch[oNode.node])));
|
||||
break;
|
||||
// 结束节点
|
||||
case EndNodeData endData:
|
||||
// 连接前部节点
|
||||
nodeMatch[endData].GetInputPort("enter").GetConnections().ForEach(oNode => endData.pre.Add(graph.nodes.IndexOf(dataMatch[oNode.node])));
|
||||
break;
|
||||
// 空节点(流程节点)
|
||||
case FlowNodeData flowData:
|
||||
// 连接前部节点
|
||||
nodeMatch[flowData].GetInputPort("enter").GetConnections().ForEach(oNode => flowData.pre.Add(graph.nodes.IndexOf(dataMatch[oNode.node])));
|
||||
// 连接后部节点
|
||||
nodeMatch[flowData].GetOutputPort("exit").GetConnections().ForEach(oNode => flowData.next.Add(graph.nodes.IndexOf(dataMatch[oNode.node])));
|
||||
break;
|
||||
// 处理异常情况
|
||||
default:
|
||||
Debug.LogError($"节点{data.name}[type:{data.GetType()}]没有定义对应的数据转换逻辑,该节点将被跳过,此举将可能导致graph出现流程断裂或其他未知异常");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log("转录完成");
|
||||
|
||||
// 将转录完成的图表数据序列化为json
|
||||
var json = JsonConvert.SerializeObject(graph, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
|
||||
// 文件目录排空
|
||||
var graphFilePath = Path.Combine(Application.dataPath, graphPath.Replace(".asset", ".sg.json"));
|
||||
if (!Directory.Exists(Path.GetDirectoryName(graphFilePath)))
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(graphFilePath) ?? string.Empty);
|
||||
// 写入json
|
||||
await File.WriteAllTextAsync(graphFilePath, json);
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.AssetDatabase.ImportAsset(graphFilePath.Replace(Application.dataPath, "Assets"));
|
||||
#endif
|
||||
|
||||
Debug.Log("剧本导出完成");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96f13bed6a3bb274abdf828cac308231
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/RunTime/VisualEditor/Node.meta
Normal file
8
Assets/10.StoryEditor/RunTime/VisualEditor/Node.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90e4f51c40e7d9340a88df558072b353
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/10.StoryEditor/RunTime/VisualEditor/Node/BeginNode.cs
Normal file
20
Assets/10.StoryEditor/RunTime/VisualEditor/Node/BeginNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
[Serializable, CreateNodeMenu("")]
|
||||
public class BeginNode : NodeBase
|
||||
{
|
||||
[Output]
|
||||
public Exit exit;
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
public new async UniTask<BeginNodeData> Export() => new(await base.Export());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7bdd4ad134525043830b54f6a2391f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/10.StoryEditor/RunTime/VisualEditor/Node/EndNode.cs
Normal file
20
Assets/10.StoryEditor/RunTime/VisualEditor/Node/EndNode.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 结束节点
|
||||
/// </summary>
|
||||
[Serializable, CreateNodeMenu("")]
|
||||
public class EndNode : NodeBase
|
||||
{
|
||||
[Input]
|
||||
public Enter enter;
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
public new async UniTask<EndNodeData> Export() => new(await base.Export());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e8d1b2bbe7b8154c8822cf7c0b6e064
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
128
Assets/10.StoryEditor/RunTime/VisualEditor/Node/FlowNode.cs
Normal file
128
Assets/10.StoryEditor/RunTime/VisualEditor/Node/FlowNode.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
[Serializable,CreateNodeMenu("")]
|
||||
public class FlowNode : NodeBase
|
||||
{
|
||||
[HorizontalGroup("Port", order:0)]
|
||||
[Input]
|
||||
public Enter enter;
|
||||
[HorizontalGroup("Port")]
|
||||
[Output]
|
||||
public Exit exit;
|
||||
|
||||
/// <summary>
|
||||
/// Exit端口连接的节点的执行类型
|
||||
/// </summary>
|
||||
[BoxGroup("Config", order:5, showLabel:false)]
|
||||
[LabelText("后续执行")]
|
||||
public NodeExecuteType exitNodeExecuteType = NodeExecuteType.Async;
|
||||
|
||||
/// <summary>
|
||||
/// 创建新的流程节点
|
||||
/// </summary>
|
||||
/// <param name="graph">所在的块</param>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="position">节点位置</param>
|
||||
/// <param name="prePorts">节点前部连接</param>
|
||||
/// <param name="nextPorts">节点后部连接</param>
|
||||
public static T Create<T>(NodeGraph graph, string name = null, Vector2 position = default,
|
||||
List<NodePort> prePorts = null, List<NodePort> nextPorts = null) where T:FlowNode
|
||||
{
|
||||
// 创建节点
|
||||
var node = NodeBase.Create<T>(graph, name, position);
|
||||
if (node == null)
|
||||
return null;
|
||||
|
||||
// 将剧本与前部节点和后部节点相连
|
||||
var enterPort = node.GetInputPort("enter");
|
||||
if (enterPort != null && prePorts != null)
|
||||
prePorts.ForEach(port => port.Connect(enterPort));
|
||||
var exitPort = node.GetInputPort("exit");
|
||||
if (exitPort != null && nextPorts != null)
|
||||
nextPorts.ForEach(port => port.Connect(exitPort));
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
#region 工具方法
|
||||
|
||||
/// <summary>
|
||||
/// 将自身的入口连接传递给指定端口
|
||||
/// </summary>
|
||||
/// <param name="otherPort">指定端口</param>
|
||||
/// <param name="deleteSelf">传递后删除自身连接</param>
|
||||
public void GiveEnterPortToOtherPort(NodePort otherPort, bool deleteSelf)
|
||||
=> GiveConnectionToOtherPort(GetInputPort("enter"), otherPort, deleteSelf);
|
||||
|
||||
/// <summary>
|
||||
/// 将自身的出口连接传递给指定端口
|
||||
/// </summary>
|
||||
/// <param name="otherPort">指定端口</param>
|
||||
/// <param name="deleteSelf">传递后删除自身连接</param>
|
||||
public void GiveExitPortToOtherPort(NodePort otherPort, bool deleteSelf = false)
|
||||
=> GiveConnectionToOtherPort(GetOutputPort("exit"), otherPort, deleteSelf);
|
||||
|
||||
#endregion
|
||||
|
||||
#region 节点操作
|
||||
|
||||
/// <summary>
|
||||
/// 向前插入节点
|
||||
/// </summary>
|
||||
[HorizontalGroup("Insert", order:1)]
|
||||
[Button("(←)插入节点")]
|
||||
public void InsertForward()
|
||||
{
|
||||
// 生成新的空节点
|
||||
var newNode = SelectionNode.Create(graph);
|
||||
// 将本节点的Enter连接给到新的节点的Enter
|
||||
GiveEnterPortToOtherPort(newNode.GetInputPort("enter"), true);
|
||||
// 将新的节点的Exit连接到本节点的Enter
|
||||
GetInputPort("enter").Connect(newNode.GetOutputPort("exit"));
|
||||
// 将新节点设定为本节点位置
|
||||
newNode.position = position;
|
||||
|
||||
// 将包括自身在内的所有后续节点向后移动一个默认间隔
|
||||
newNode.DoActionToAllNodesAfter((thisNode, otherNode) => otherNode.position = thisNode.position + new Vector2(DefaultNodeXInterval, DefaultNodeYInterval));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向后插入节点
|
||||
/// </summary>
|
||||
[HorizontalGroup("Insert")]
|
||||
[Button("插入节点(→)")]
|
||||
public void InsertBackward()
|
||||
{
|
||||
// 生成新的空节点
|
||||
var newNode = SelectionNode.Create(graph);
|
||||
// 将本节点的Exit连接给到新的节点的Exit
|
||||
GiveExitPortToOtherPort(newNode.GetOutputPort("exit"), true);
|
||||
// 将新的节点的Enter连接到本节点的Exit
|
||||
GetOutputPort("exit").Connect(newNode.GetInputPort("enter"));
|
||||
// 将新节点设置为本节点位置后移一格
|
||||
newNode.position = position + new Vector2(DefaultNodeXInterval, DefaultNodeYInterval);
|
||||
|
||||
// 将所有后续节点向后移动一个默认间隔
|
||||
newNode.DoActionToAllNodesAfter((thisNode, otherNode) => otherNode.position = thisNode.position + new Vector2(DefaultNodeXInterval, DefaultNodeYInterval));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 导出
|
||||
/// </summary>
|
||||
public new async UniTask<FlowNodeData> Export()
|
||||
{
|
||||
FlowNodeData node = new(await base.Export());
|
||||
node.executeType = exitNodeExecuteType;
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e85b8c018e8b68b418fff4c389089665
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
134
Assets/10.StoryEditor/RunTime/VisualEditor/Node/NodeBase.cs
Normal file
134
Assets/10.StoryEditor/RunTime/VisualEditor/Node/NodeBase.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
[Serializable,CreateNodeMenu("")]
|
||||
public class NodeBase : Node
|
||||
{
|
||||
[Serializable] public class Enter { }
|
||||
[Serializable] public class Exit { }
|
||||
|
||||
/// <summary>
|
||||
/// 默认节点位置间隔(x轴)
|
||||
/// </summary>
|
||||
public const float DefaultNodeXInterval = 250;
|
||||
/// <summary>
|
||||
/// 默认节点位置间隔(y轴)
|
||||
/// </summary>
|
||||
public const float DefaultNodeYInterval = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 创建新的节点
|
||||
/// </summary>
|
||||
/// <param name="graph">所在的块</param>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="position">节点位置</param>
|
||||
public static T Create<T>(NodeGraph graph, string name = null, Vector2 position = default) where T : NodeBase
|
||||
{
|
||||
// 创建节点
|
||||
var node = graph.AddNode<T>();
|
||||
|
||||
if (node == null)
|
||||
{
|
||||
Debug.LogError("节点创建失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// 将节点落盘
|
||||
UnityEditor.AssetDatabase.AddObjectToAsset(node, graph);
|
||||
UnityEditor.EditorUtility.SetDirty(graph);
|
||||
#endif
|
||||
|
||||
// 设置节点变量
|
||||
node.position = position;
|
||||
node.name = name ?? $"{name}_para_sample";
|
||||
|
||||
// 初始化节点
|
||||
node.Init();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除自身
|
||||
/// </summary>
|
||||
public void DestroySelf()
|
||||
{
|
||||
// 清理连接
|
||||
ClearConnections();
|
||||
// 移除与Graph之间的联系
|
||||
if (graph)
|
||||
{
|
||||
graph.nodes.Remove(this);
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.AssetDatabase.RemoveObjectFromAsset(this);
|
||||
#endif
|
||||
}
|
||||
// 销毁自身
|
||||
DestroyImmediate(this);
|
||||
}
|
||||
|
||||
public UniTask<NodeData> Export()
|
||||
{
|
||||
NodeData nodeData = new();
|
||||
nodeData.name = name;
|
||||
return UniTask.FromResult(nodeData);
|
||||
}
|
||||
|
||||
#region 工具方法
|
||||
|
||||
/// <summary>
|
||||
/// 将端口A的连接传递给端口B
|
||||
/// </summary>
|
||||
/// <param name="portA">端口A</param>
|
||||
/// <param name="portB">端口B</param>
|
||||
/// <param name="deleteA">移除端口A的连接</param>
|
||||
public static void GiveConnectionToOtherPort(NodePort portA, NodePort portB, bool deleteA = false)
|
||||
{
|
||||
// 排除端口A与端口B重合的情况
|
||||
if (portA == portB)
|
||||
return;
|
||||
|
||||
// 获取当前Enter端口的所有连接
|
||||
var enterPorts = portA.GetConnections();
|
||||
|
||||
enterPorts.ForEach(port =>
|
||||
{
|
||||
// 将Enter端口与该端口断开
|
||||
if (deleteA)
|
||||
port.Disconnect(portA);
|
||||
// 将指定的端口与该端口连接
|
||||
port.Connect(portB);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对所有之后的节点执行动作
|
||||
/// </summary>
|
||||
/// <param name="action">指定动作</param>
|
||||
public void DoActionToAllNodesAfter(Action<NodeBase, NodeBase> action)
|
||||
{
|
||||
// 所有出口端口
|
||||
Ports.Where(p => p.IsOutput).ToList().ForEach(port =>
|
||||
{
|
||||
// 所有出口端口连接的所有端口
|
||||
port.GetConnections().ForEach(otherSide =>
|
||||
{
|
||||
// 所有出口端口连接的所有端口的是NodeBase的节点
|
||||
if (otherSide.node is NodeBase baseNode)
|
||||
{
|
||||
action?.Invoke(this, baseNode);
|
||||
baseNode.DoActionToAllNodesAfter(action);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 128757ef2e3a23c48b67ade15852c36c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 剧本自然段
|
||||
/// </summary>
|
||||
[Serializable, CreateNodeMenu("创建剧本自然段", order = 5)]
|
||||
public class ScriptParagraphNode : FlowNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// (仅供辨识,不起实际作用)
|
||||
/// </summary>
|
||||
[BoxGroup("Info", order:10, showLabel:false)]
|
||||
[LabelText("名称"),LabelWidth(30)]
|
||||
public string displayName;
|
||||
|
||||
/// <summary>
|
||||
/// 字幕
|
||||
/// </summary>
|
||||
[BoxGroup("Info")]
|
||||
[LabelText("字幕"),LabelWidth(30)]
|
||||
public Sprite caption;
|
||||
/// <summary>
|
||||
/// 语音
|
||||
/// </summary>
|
||||
[BoxGroup("Info")]
|
||||
[LabelText("音频"),LabelWidth(30)]
|
||||
public AudioClip audio;
|
||||
|
||||
/// <summary>
|
||||
/// 创建新的剧本段落节点
|
||||
/// </summary>
|
||||
/// <param name="graph">所在的块</param>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="position">节点位置</param>
|
||||
/// <param name="prePorts">节点前部连接</param>
|
||||
/// <param name="nextPorts">节点后部连接</param>
|
||||
/// <param name="caption">字幕</param>
|
||||
/// <param name="audio">音频</param>
|
||||
public static ScriptParagraphNode Create(NodeGraph graph, string name = null, Vector2 position = default, List<NodePort> prePorts = null, List<NodePort> nextPorts = null, Sprite caption = null, AudioClip audio = null)
|
||||
{
|
||||
// 创建节点
|
||||
var node = Create<ScriptParagraphNode>(graph,name,position, prePorts, nextPorts);
|
||||
if (node == null)
|
||||
return null;
|
||||
|
||||
// 设置变量
|
||||
node.caption = caption;
|
||||
node.audio = audio;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
protected override void Init()
|
||||
{
|
||||
base.Init();
|
||||
UpdateName();
|
||||
}
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (graph == null)
|
||||
{
|
||||
Debug.LogWarning($"项目中存在幽灵节点:{name}");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateName();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新名称
|
||||
/// </summary>
|
||||
private void UpdateName()
|
||||
{
|
||||
if (string.IsNullOrEmpty(displayName))
|
||||
{
|
||||
displayName = graph.name + "_para";
|
||||
}
|
||||
|
||||
name = displayName;
|
||||
}
|
||||
|
||||
public new async UniTask<ScriptParagraphNodeData> Export()
|
||||
{
|
||||
var node = new ScriptParagraphNodeData(await base.Export());
|
||||
var sGraph = (ScriptGraph)graph;
|
||||
|
||||
// 记录音频资源地址
|
||||
if (audio)
|
||||
node.audioPath = await sGraph.Loader.Save(audio,sGraph.packageID);
|
||||
|
||||
// 记录字幕资源地址
|
||||
if (caption)
|
||||
node.captionPath = await sGraph.Loader.Save(caption,sGraph.packageID);
|
||||
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e0dc65de1614514883efb2a6848e5a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
using XNode;
|
||||
|
||||
namespace Stary.Evo.StoryEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// 空节点
|
||||
/// (可选择应用哪种节点)
|
||||
/// </summary>
|
||||
[Serializable, CreateNodeMenu("创建空节点", order = 0)]
|
||||
public class SelectionNode : FlowNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 创建新的空节点
|
||||
/// </summary>
|
||||
/// <param name="graph">所在的块</param>
|
||||
/// <param name="name">节点名称</param>
|
||||
/// <param name="position">节点位置</param>
|
||||
/// <param name="prePorts">节点前部连接</param>
|
||||
/// <param name="nextPorts">节点后部连接</param>
|
||||
public static SelectionNode Create(NodeGraph graph, string name = null, Vector2 position = default,
|
||||
List<NodePort> prePorts = null, List<NodePort> nextPorts = null)
|
||||
{
|
||||
var node = Create<SelectionNode>(graph, name, position, prePorts, nextPorts);
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建剧本段落
|
||||
/// </summary>
|
||||
[BoxGroup("Option", order:20, showLabel:false)]
|
||||
[Button("剧本段落")]
|
||||
public void ChooseScriptParagraph()
|
||||
{
|
||||
// 记录动作缓存
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Undo.RecordObject(graph, "Delete Node");
|
||||
#endif
|
||||
|
||||
// 在本节点位置创建剧本段落节点
|
||||
var node = ScriptParagraphNode.Create(graph, position: position);
|
||||
GiveEnterPortToOtherPort(node.GetInputPort("enter"), true);
|
||||
GiveExitPortToOtherPort(node.GetOutputPort("exit"), true);
|
||||
|
||||
// 移除本节点
|
||||
DestroySelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79ef9a28ecfd1384a96569da56b2022b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
693
Assets/10.StoryEditor/Sample~/Sample.unity
Normal file
693
Assets/10.StoryEditor/Sample~/Sample.unity
Normal file
@@ -0,0 +1,693 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!29 &1
|
||||
OcclusionCullingSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_OcclusionBakeSettings:
|
||||
smallestOccluder: 5
|
||||
smallestHole: 0.25
|
||||
backfaceThreshold: 100
|
||||
m_SceneGUID: 00000000000000000000000000000000
|
||||
m_OcclusionCullingData: {fileID: 0}
|
||||
--- !u!104 &2
|
||||
RenderSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 9
|
||||
m_Fog: 0
|
||||
m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
|
||||
m_FogMode: 3
|
||||
m_FogDensity: 0.01
|
||||
m_LinearFogStart: 0
|
||||
m_LinearFogEnd: 300
|
||||
m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
|
||||
m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
|
||||
m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
|
||||
m_AmbientIntensity: 1
|
||||
m_AmbientMode: 0
|
||||
m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
|
||||
m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_HaloStrength: 0.5
|
||||
m_FlareStrength: 1
|
||||
m_FlareFadeSpeed: 3
|
||||
m_HaloTexture: {fileID: 0}
|
||||
m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
|
||||
m_DefaultReflectionMode: 0
|
||||
m_DefaultReflectionResolution: 128
|
||||
m_ReflectionBounces: 1
|
||||
m_ReflectionIntensity: 1
|
||||
m_CustomReflection: {fileID: 0}
|
||||
m_Sun: {fileID: 0}
|
||||
m_UseRadianceAmbientProbe: 0
|
||||
--- !u!157 &3
|
||||
LightmapSettings:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 12
|
||||
m_GIWorkflowMode: 1
|
||||
m_GISettings:
|
||||
serializedVersion: 2
|
||||
m_BounceScale: 1
|
||||
m_IndirectOutputScale: 1
|
||||
m_AlbedoBoost: 1
|
||||
m_EnvironmentLightingMode: 0
|
||||
m_EnableBakedLightmaps: 1
|
||||
m_EnableRealtimeLightmaps: 0
|
||||
m_LightmapEditorSettings:
|
||||
serializedVersion: 12
|
||||
m_Resolution: 2
|
||||
m_BakeResolution: 40
|
||||
m_AtlasSize: 1024
|
||||
m_AO: 0
|
||||
m_AOMaxDistance: 1
|
||||
m_CompAOExponent: 1
|
||||
m_CompAOExponentDirect: 0
|
||||
m_ExtractAmbientOcclusion: 0
|
||||
m_Padding: 2
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_LightmapsBakeMode: 1
|
||||
m_TextureCompression: 1
|
||||
m_FinalGather: 0
|
||||
m_FinalGatherFiltering: 1
|
||||
m_FinalGatherRayCount: 256
|
||||
m_ReflectionCompression: 2
|
||||
m_MixedBakeMode: 2
|
||||
m_BakeBackend: 1
|
||||
m_PVRSampling: 1
|
||||
m_PVRDirectSampleCount: 32
|
||||
m_PVRSampleCount: 512
|
||||
m_PVRBounces: 2
|
||||
m_PVREnvironmentSampleCount: 256
|
||||
m_PVREnvironmentReferencePointCount: 2048
|
||||
m_PVRFilteringMode: 1
|
||||
m_PVRDenoiserTypeDirect: 1
|
||||
m_PVRDenoiserTypeIndirect: 1
|
||||
m_PVRDenoiserTypeAO: 1
|
||||
m_PVRFilterTypeDirect: 0
|
||||
m_PVRFilterTypeIndirect: 0
|
||||
m_PVRFilterTypeAO: 0
|
||||
m_PVREnvironmentMIS: 1
|
||||
m_PVRCulling: 1
|
||||
m_PVRFilteringGaussRadiusDirect: 1
|
||||
m_PVRFilteringGaussRadiusIndirect: 5
|
||||
m_PVRFilteringGaussRadiusAO: 2
|
||||
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
|
||||
m_PVRFilteringAtrousPositionSigmaIndirect: 2
|
||||
m_PVRFilteringAtrousPositionSigmaAO: 1
|
||||
m_ExportTrainingData: 0
|
||||
m_TrainingDataDestination: TrainingData
|
||||
m_LightProbeSampleCountMultiplier: 4
|
||||
m_LightingDataAsset: {fileID: 0}
|
||||
m_LightingSettings: {fileID: 0}
|
||||
--- !u!196 &4
|
||||
NavMeshSettings:
|
||||
serializedVersion: 2
|
||||
m_ObjectHideFlags: 0
|
||||
m_BuildSettings:
|
||||
serializedVersion: 3
|
||||
agentTypeID: 0
|
||||
agentRadius: 0.5
|
||||
agentHeight: 2
|
||||
agentSlope: 45
|
||||
agentClimb: 0.4
|
||||
ledgeDropHeight: 0
|
||||
maxJumpAcrossDistance: 0
|
||||
minRegionArea: 2
|
||||
manualCellSize: 0
|
||||
cellSize: 0.16666667
|
||||
manualTileSize: 0
|
||||
tileSize: 256
|
||||
buildHeightMesh: 0
|
||||
maxJobWorkers: 0
|
||||
preserveTilesOutsideBounds: 0
|
||||
debug:
|
||||
m_Flags: 0
|
||||
m_NavMeshData: {fileID: 0}
|
||||
--- !u!1 &354453078
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 354453079}
|
||||
- component: {fileID: 354453080}
|
||||
m_Layer: 0
|
||||
m_Name: Audio
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &354453079
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 354453078}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1732823841}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!82 &354453080
|
||||
AudioSource:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 354453078}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 4
|
||||
OutputAudioMixerGroup: {fileID: 0}
|
||||
m_audioClip: {fileID: 0}
|
||||
m_PlayOnAwake: 1
|
||||
m_Volume: 1
|
||||
m_Pitch: 1
|
||||
Loop: 0
|
||||
Mute: 0
|
||||
Spatialize: 0
|
||||
SpatializePostEffects: 0
|
||||
Priority: 128
|
||||
DopplerLevel: 1
|
||||
MinDistance: 1
|
||||
MaxDistance: 500
|
||||
Pan2D: 0
|
||||
rolloffMode: 0
|
||||
BypassEffects: 0
|
||||
BypassListenerEffects: 0
|
||||
BypassReverbZones: 0
|
||||
rolloffCustomCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
- serializedVersion: 3
|
||||
time: 1
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
panLevelCustomCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
spreadCustomCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 0
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
reverbZoneMixCustomCurve:
|
||||
serializedVersion: 2
|
||||
m_Curve:
|
||||
- serializedVersion: 3
|
||||
time: 0
|
||||
value: 1
|
||||
inSlope: 0
|
||||
outSlope: 0
|
||||
tangentMode: 0
|
||||
weightedMode: 0
|
||||
inWeight: 0.33333334
|
||||
outWeight: 0.33333334
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
m_RotationOrder: 4
|
||||
--- !u!1 &658829530
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 658829532}
|
||||
- component: {fileID: 658829531}
|
||||
- component: {fileID: 658829533}
|
||||
m_Layer: 0
|
||||
m_Name: Directional Light
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!108 &658829531
|
||||
Light:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 658829530}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 10
|
||||
m_Type: 1
|
||||
m_Shape: 0
|
||||
m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
|
||||
m_Intensity: 1
|
||||
m_Range: 10
|
||||
m_SpotAngle: 30
|
||||
m_InnerSpotAngle: 21.80208
|
||||
m_CookieSize: 10
|
||||
m_Shadows:
|
||||
m_Type: 2
|
||||
m_Resolution: -1
|
||||
m_CustomResolution: -1
|
||||
m_Strength: 1
|
||||
m_Bias: 0.05
|
||||
m_NormalBias: 0.4
|
||||
m_NearPlane: 0.2
|
||||
m_CullingMatrixOverride:
|
||||
e00: 1
|
||||
e01: 0
|
||||
e02: 0
|
||||
e03: 0
|
||||
e10: 0
|
||||
e11: 1
|
||||
e12: 0
|
||||
e13: 0
|
||||
e20: 0
|
||||
e21: 0
|
||||
e22: 1
|
||||
e23: 0
|
||||
e30: 0
|
||||
e31: 0
|
||||
e32: 0
|
||||
e33: 1
|
||||
m_UseCullingMatrixOverride: 0
|
||||
m_Cookie: {fileID: 0}
|
||||
m_DrawHalo: 0
|
||||
m_Flare: {fileID: 0}
|
||||
m_RenderMode: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingLayerMask: 1
|
||||
m_Lightmapping: 4
|
||||
m_LightShadowCasterMode: 0
|
||||
m_AreaSize: {x: 1, y: 1}
|
||||
m_BounceIntensity: 1
|
||||
m_ColorTemperature: 6570
|
||||
m_UseColorTemperature: 0
|
||||
m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_UseBoundingSphereOverride: 0
|
||||
m_UseViewFrustumForShadowCasterCull: 1
|
||||
m_ShadowRadius: 0
|
||||
m_ShadowAngle: 0
|
||||
--- !u!4 &658829532
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 658829530}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
|
||||
m_LocalPosition: {x: 0, y: 3, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!114 &658829533
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 658829530}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 474bcb49853aa07438625e644c072ee6, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Version: 3
|
||||
m_UsePipelineSettings: 1
|
||||
m_AdditionalLightsShadowResolutionTier: 2
|
||||
m_LightLayerMask: 1
|
||||
m_RenderingLayers: 1
|
||||
m_CustomShadowLayers: 0
|
||||
m_ShadowLayerMask: 1
|
||||
m_ShadowRenderingLayers: 1
|
||||
m_LightCookieSize: {x: 1, y: 1}
|
||||
m_LightCookieOffset: {x: 0, y: 0}
|
||||
m_SoftShadowQuality: 0
|
||||
--- !u!1 &948603383
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 948603387}
|
||||
- component: {fileID: 948603386}
|
||||
- component: {fileID: 948603385}
|
||||
- component: {fileID: 948603384}
|
||||
m_Layer: 0
|
||||
m_Name: Main Camera
|
||||
m_TagString: MainCamera
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &948603384
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 948603383}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_RenderShadows: 1
|
||||
m_RequiresDepthTextureOption: 2
|
||||
m_RequiresOpaqueTextureOption: 2
|
||||
m_CameraType: 0
|
||||
m_Cameras: []
|
||||
m_RendererIndex: -1
|
||||
m_VolumeLayerMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 1
|
||||
m_VolumeTrigger: {fileID: 0}
|
||||
m_VolumeFrameworkUpdateModeOption: 2
|
||||
m_RenderPostProcessing: 0
|
||||
m_Antialiasing: 0
|
||||
m_AntialiasingQuality: 2
|
||||
m_StopNaN: 0
|
||||
m_Dithering: 0
|
||||
m_ClearDepth: 1
|
||||
m_AllowXRRendering: 1
|
||||
m_AllowHDROutput: 1
|
||||
m_UseScreenCoordOverride: 0
|
||||
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_RequiresDepthTexture: 0
|
||||
m_RequiresColorTexture: 0
|
||||
m_Version: 2
|
||||
m_TaaSettings:
|
||||
m_Quality: 3
|
||||
m_FrameInfluence: 0.1
|
||||
m_JitterScale: 1
|
||||
m_MipBias: 0
|
||||
m_VarianceClampScale: 0.9
|
||||
m_ContrastAdaptiveSharpening: 0
|
||||
--- !u!81 &948603385
|
||||
AudioListener:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 948603383}
|
||||
m_Enabled: 1
|
||||
--- !u!20 &948603386
|
||||
Camera:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 948603383}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 2
|
||||
m_ClearFlags: 1
|
||||
m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
|
||||
m_projectionMatrixMode: 1
|
||||
m_GateFitMode: 2
|
||||
m_FOVAxisMode: 0
|
||||
m_Iso: 200
|
||||
m_ShutterSpeed: 0.005
|
||||
m_Aperture: 16
|
||||
m_FocusDistance: 10
|
||||
m_FocalLength: 50
|
||||
m_BladeCount: 5
|
||||
m_Curvature: {x: 2, y: 11}
|
||||
m_BarrelClipping: 0.25
|
||||
m_Anamorphism: 0
|
||||
m_SensorSize: {x: 36, y: 24}
|
||||
m_LensShift: {x: 0, y: 0}
|
||||
m_NormalizedViewPortRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
near clip plane: 0.3
|
||||
far clip plane: 1000
|
||||
field of view: 60
|
||||
orthographic: 0
|
||||
orthographic size: 5
|
||||
m_Depth: 0
|
||||
m_CullingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
m_RenderingPath: -1
|
||||
m_TargetTexture: {fileID: 0}
|
||||
m_TargetDisplay: 0
|
||||
m_TargetEye: 3
|
||||
m_HDR: 1
|
||||
m_AllowMSAA: 1
|
||||
m_AllowDynamicResolution: 0
|
||||
m_ForceIntoRT: 0
|
||||
m_OcclusionCulling: 1
|
||||
m_StereoConvergence: 10
|
||||
m_StereoSeparation: 0.022
|
||||
--- !u!4 &948603387
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 948603383}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &962199775
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 962199776}
|
||||
- component: {fileID: 962199777}
|
||||
m_Layer: 0
|
||||
m_Name: HybridClREntrance
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &962199776
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 962199775}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: -0.6535072, y: -0.08580719, z: 1.3690248}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &962199777
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 962199775}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 3397e2d382924ffea94a9ecbd16ab745, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
moduleName: com.sxkjg.main
|
||||
m_moduleRootTrans: {fileID: 0}
|
||||
--- !u!1 &1212764680
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1212764681}
|
||||
- component: {fileID: 1212764682}
|
||||
m_Layer: 0
|
||||
m_Name: Caption
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1212764681
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1212764680}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1732823841}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!212 &1212764682
|
||||
SpriteRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1212764680}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 0
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 10754, guid: 0000000000000000f000000000000000, type: 0}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 0
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_FlipX: 0
|
||||
m_FlipY: 0
|
||||
m_DrawMode: 0
|
||||
m_Size: {x: 1, y: 1}
|
||||
m_AdaptiveModeThreshold: 0.5
|
||||
m_SpriteTileMode: 0
|
||||
m_WasSpriteAssigned: 0
|
||||
m_MaskInteraction: 0
|
||||
m_SpriteSortPoint: 0
|
||||
--- !u!1 &1732823839
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1732823841}
|
||||
- component: {fileID: 1732823840}
|
||||
m_Layer: 0
|
||||
m_Name: ScriptPlayer
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!114 &1732823840
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1732823839}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 543f5e9055e16f045806981c5725e910, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
packageID: com.zjkjg.daopian
|
||||
scriptName: graph.sg
|
||||
--- !u!4 &1732823841
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1732823839}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1212764681}
|
||||
- {fileID: 354453079}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1660057539 &9223372036854775807
|
||||
SceneRoots:
|
||||
m_ObjectHideFlags: 0
|
||||
m_Roots:
|
||||
- {fileID: 658829532}
|
||||
- {fileID: 948603387}
|
||||
- {fileID: 962199776}
|
||||
- {fileID: 1732823841}
|
||||
7
Assets/10.StoryEditor/Sample~/Sample.unity.meta
Normal file
7
Assets/10.StoryEditor/Sample~/Sample.unity.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dbefcb0c8b734346a6bf5cab23d09db
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/10.StoryEditor/Sample~/Script.meta
Normal file
8
Assets/10.StoryEditor/Sample~/Script.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7861aace72bef144da5e5349d49d214d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
20
Assets/10.StoryEditor/Sample~/Script/ResourceLoader.cs
Normal file
20
Assets/10.StoryEditor/Sample~/Script/ResourceLoader.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using rokid.armaz.module;
|
||||
using Stary.Evo.StoryEditor;
|
||||
using UnityEngine;
|
||||
|
||||
public class ResourceLoader : IResource
|
||||
{
|
||||
public UniTask<T> Load<T>(ResourcePathData pathData) where T : Object
|
||||
{
|
||||
return AMP.ResourceLoader.LoadAssetAsync<T>(pathData.packageID, pathData.path);
|
||||
}
|
||||
|
||||
public UniTask<ResourcePathData> Save<T>(T asset, string packageID) where T : Object
|
||||
{
|
||||
ResourcePathData pathData = new();
|
||||
pathData.packageID = packageID;
|
||||
pathData.AddPath(asset.name);
|
||||
return UniTask.FromResult(pathData);
|
||||
}
|
||||
}
|
||||
11
Assets/10.StoryEditor/Sample~/Script/ResourceLoader.cs.meta
Normal file
11
Assets/10.StoryEditor/Sample~/Script/ResourceLoader.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43ce2e09f0a37f64e8dac4036d588160
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
166
Assets/10.StoryEditor/Sample~/Script/TestScriptPlayer.cs
Normal file
166
Assets/10.StoryEditor/Sample~/Script/TestScriptPlayer.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using Array = System.Array;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Stary.Evo.StoryEditor;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
public class TestScriptPlayer : MonoBehaviour
|
||||
{
|
||||
[LabelText("指定包体ID"), SerializeField, ValueDropdown(nameof(GetPackages))]
|
||||
private string packageID;
|
||||
[LabelText("指定剧本名称"), SerializeField, ValueDropdown(nameof(GetScripts))]
|
||||
private string scriptName;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
var caption = GetComponentInChildren<SpriteRenderer>();
|
||||
caption.color = Color.clear;
|
||||
var audioClip = GetComponentInChildren<AudioSource>();
|
||||
audioClip.volume = 0;
|
||||
|
||||
ScriptPlayer.Init(new ResourceLoader(), audioClip, caption);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
ScriptPlayer.Release();
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void Play()
|
||||
{
|
||||
_ = ScriptPlayer.Play(packageID, scriptName);
|
||||
}
|
||||
|
||||
[Button]
|
||||
public void Stop()
|
||||
{
|
||||
_ = ScriptPlayer.Stop();
|
||||
}
|
||||
|
||||
#region 非主要内容,忽略即可
|
||||
|
||||
private Dictionary<string, string> _paths = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有Module Package
|
||||
/// </summary>
|
||||
private string[] GetPackages()
|
||||
{
|
||||
// 查找Modules目录
|
||||
List<string> modules = new();
|
||||
GetDirectoryPaths(Application.dataPath, modules, "Modules");
|
||||
if (modules.Count == 0)
|
||||
{
|
||||
Debug.LogError("未找到任何Modules目录");
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
// 查找package
|
||||
List<string> packages = new();
|
||||
GetDirectoryPaths(modules[0], packages, "com.");
|
||||
if (packages.Count == 0)
|
||||
{
|
||||
return Array.Empty<string>();
|
||||
}
|
||||
|
||||
// 记录Package地址并返回选项
|
||||
_paths.Clear();
|
||||
packages.ForEach(path => _paths.Add(Path.GetFileName(path), path));
|
||||
return _paths.Keys.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取Package中所有Script
|
||||
/// </summary>
|
||||
private string[] GetScripts()
|
||||
{
|
||||
// 未选Package
|
||||
if (string.IsNullOrEmpty(packageID) || !_paths.ContainsKey(packageID))
|
||||
return Array.Empty<string>();
|
||||
|
||||
List<string> scripts = new();
|
||||
GetFilePaths(_paths[packageID], scripts, tail: ".sg.json");
|
||||
return scripts.Count == 0 ? Array.Empty<string>() : scripts.Select(path => Path.GetFileName(path).Replace(".json", "")).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取符合条件的目录
|
||||
/// </summary>
|
||||
/// <param name="root">查找起始目录</param>
|
||||
/// <param name="result">查找结果</param>
|
||||
/// <param name="title">筛选条件(title)</param>
|
||||
/// <param name="tail">筛选条件(tail)</param>
|
||||
private static void GetDirectoryPaths(string root, List<string> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取符合条件的文件路径
|
||||
/// </summary>
|
||||
/// <param name="root">查找起始目录</param>
|
||||
/// <param name="result">查找结果</param>
|
||||
/// <param name="title">筛选条件(title)</param>
|
||||
/// <param name="tail">筛选条件(tail)</param>
|
||||
private static void GetFilePaths(string root, List<string> result, string title = null, string tail = null)
|
||||
{
|
||||
foreach (var file in Directory.GetFiles(root))
|
||||
{
|
||||
// ReSharper disable once ReplaceWithSingleAssignment.True
|
||||
var check = true;
|
||||
|
||||
// 匹配文件头
|
||||
if (!string.IsNullOrEmpty(title))
|
||||
{
|
||||
if(!Path.GetFileName(file).StartsWith(title))
|
||||
check = false;
|
||||
}
|
||||
|
||||
// 匹配文件尾
|
||||
if (!string.IsNullOrEmpty(tail))
|
||||
{
|
||||
if (!Path.GetFileName(file).EndsWith(tail))
|
||||
{
|
||||
check = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (check)
|
||||
{
|
||||
result.Add(file.Replace('\\', '/'));
|
||||
}
|
||||
}
|
||||
|
||||
// 继续往下找
|
||||
foreach (var dir in Directory.GetDirectories(root))
|
||||
{
|
||||
GetFilePaths(dir, result, title, tail);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 543f5e9055e16f045806981c5725e910
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
24
Assets/10.StoryEditor/package.json
Normal file
24
Assets/10.StoryEditor/package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "com.staryevo.storyeditor",
|
||||
"version": "1.0.0",
|
||||
"displayName": "10.StoryEditor",
|
||||
"description": "可视化剧本编辑器\n1.通过可视化图表编辑剧本内容\n2.将剧本导出为json\n3.解析剧本并执行",
|
||||
"unity": "2021.3",
|
||||
"unityRelease": "44f1c1",
|
||||
"keywords": [
|
||||
"unity",
|
||||
"story",
|
||||
"visual",
|
||||
"editor",
|
||||
"graph"
|
||||
],
|
||||
"author": {
|
||||
"name": "staryEvo",
|
||||
"url": "https://www.unity3d.com"
|
||||
},
|
||||
"dependencies": {
|
||||
"com.github.siccity.xnode": "1.8.0",
|
||||
"com.cysharp.unitask": "2.5.10",
|
||||
"com.unity.nuget.newtonsoft-json": "3.2.1"
|
||||
}
|
||||
}
|
||||
7
Assets/10.StoryEditor/package.json.meta
Normal file
7
Assets/10.StoryEditor/package.json.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3e21fe172f8611499ecc724ca256dca
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user