2026-01-06 14:24:23 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Threading;
|
|
|
|
|
|
using Cysharp.Threading.Tasks;
|
|
|
|
|
|
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;
|
2026-01-06 15:59:37 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 刷新帧率
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private const float UpdateInterval = 0.02f;
|
2026-01-06 14:24:23 +08:00
|
|
|
|
|
|
|
|
|
|
/// <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);
|
2026-01-06 15:59:37 +08:00
|
|
|
|
|
2026-01-06 14:24:23 +08:00
|
|
|
|
// 淡入
|
2026-01-06 15:59:37 +08:00
|
|
|
|
await SetCaptionColor(1,ct);
|
2026-01-06 14:24:23 +08:00
|
|
|
|
|
|
|
|
|
|
// 等音频播放完
|
|
|
|
|
|
if (_audio)
|
|
|
|
|
|
{
|
|
|
|
|
|
_audio.Play();
|
|
|
|
|
|
await UniTask.Delay(TimeSpan.FromSeconds(_audio.clip.length), cancellationToken:ct);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 淡出
|
2026-01-06 15:59:37 +08:00
|
|
|
|
await SetCaptionColor(0, ct);
|
2026-01-06 14:24:23 +08:00
|
|
|
|
await UniTask.Delay(TimeSpan.FromSeconds(AnimationTime), cancellationToken:ct);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 停止剧本
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static async UniTask Stop()
|
|
|
|
|
|
{
|
|
|
|
|
|
_graph.Stop();
|
|
|
|
|
|
|
2026-01-06 15:59:37 +08:00
|
|
|
|
await SetCaptionColor(0, CancellationToken.None);
|
2026-01-06 14:24:23 +08:00
|
|
|
|
|
|
|
|
|
|
ReleaseGraph();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static void ReleaseGraph() => _graph = null;
|
2026-01-06 15:59:37 +08:00
|
|
|
|
|
|
|
|
|
|
private static async UniTask DoAnim(Action animAction, CancellationToken ct)
|
|
|
|
|
|
{
|
|
|
|
|
|
float time = 0;
|
|
|
|
|
|
while (time < AnimationTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
animAction?.Invoke();
|
|
|
|
|
|
await UniTask.Delay(TimeSpan.FromSeconds(UpdateInterval), cancellationToken: ct);
|
|
|
|
|
|
time+=UpdateInterval;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static async UniTask SetCaptionColor(int alpha, CancellationToken ct)
|
|
|
|
|
|
{
|
|
|
|
|
|
var isAdd = alpha > _caption.color.a;
|
|
|
|
|
|
|
|
|
|
|
|
var speed = Mathf.Abs(_caption.color.a - alpha) / AnimationTime * UpdateInterval;
|
|
|
|
|
|
if (speed == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
await DoAnim(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
_caption.color = new(1, 1, 1, _caption.color.a + (isAdd ? speed : -speed));
|
|
|
|
|
|
}, ct);
|
|
|
|
|
|
}
|
2026-01-06 14:24:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|