【a】可视化剧本编辑器 10.StoryEditor
This commit is contained in:
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:
|
||||
Reference in New Issue
Block a user