Add subtitle UI for voice playback

This commit is contained in:
2026-05-16 21:33:00 +01:00
parent 58050abded
commit 3174079e37
81 changed files with 8657 additions and 1231 deletions

View File

@@ -0,0 +1,212 @@
using System;
using System.Threading;
using BriarQueen.Framework.Coordinators.Events;
using BriarQueen.Framework.Events.UI;
using BriarQueen.Framework.Services.Subtitles;
using Cysharp.Threading.Tasks;
using PrimeTween;
using UnityEngine;
using VContainer;
namespace BriarQueen.UI.HUD
{
public class SubtitleUI : MonoBehaviour
{
[Header("UI")]
[SerializeField]
private InteractTextUI _interactTextUI;
[SerializeField]
private CanvasGroup _canvasGroup;
[Header("Tweens")]
[SerializeField]
private TweenSettings _tweenSettings = new()
{
duration = 0.2f,
ease = Ease.InOutSine,
useUnscaledTime = true
};
private EventCoordinator _eventCoordinator;
private SubtitleService _subtitleService;
private CancellationTokenSource _cancellationTokenSource;
private Sequence _sequence;
[Inject]
public void Construct(EventCoordinator eventCoordinator, SubtitleService subtitleService)
{
_eventCoordinator = eventCoordinator;
_subtitleService = subtitleService;
}
private void Awake()
{
ApplyImmediate(string.Empty, false);
}
private void OnEnable()
{
_eventCoordinator?.Subscribe<SubtitleDisplayChangedEvent>(OnSubtitleDisplayChanged);
SyncFromService();
}
private void OnDisable()
{
_eventCoordinator?.Unsubscribe<SubtitleDisplayChangedEvent>(OnSubtitleDisplayChanged);
StopTween();
}
private void OnDestroy()
{
StopTween();
}
private void SyncFromService()
{
if (_subtitleService != null && _subtitleService.IsVisible)
{
ApplyImmediate(_subtitleService.CurrentText, true);
return;
}
ApplyImmediate(string.Empty, false);
}
private void OnSubtitleDisplayChanged(SubtitleDisplayChangedEvent eventData)
{
if (!eventData.Visible || string.IsNullOrWhiteSpace(eventData.Text))
{
HideSubtitle().Forget();
return;
}
ShowSubtitle(eventData.Text).Forget();
}
private async UniTaskVoid ShowSubtitle(string text)
{
if (_canvasGroup == null || _interactTextUI == null)
return;
StopTween();
_interactTextUI.SetText(text);
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
_cancellationTokenSource = new CancellationTokenSource();
var tween = new TweenSettings<float>
{
startValue = _canvasGroup.alpha,
endValue = 1f,
settings = _tweenSettings
};
_sequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_canvasGroup, tween));
try
{
await _sequence.ToUniTask(cancellationToken: _cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
DisposeTweenState();
}
_canvasGroup.alpha = 1f;
}
private async UniTaskVoid HideSubtitle()
{
if (_canvasGroup == null || _interactTextUI == null)
return;
StopTween();
_cancellationTokenSource = new CancellationTokenSource();
var tween = new TweenSettings<float>
{
startValue = _canvasGroup.alpha,
endValue = 0f,
settings = _tweenSettings
};
_sequence = Sequence.Create(useUnscaledTime: true)
.Group(Tween.Alpha(_canvasGroup, tween));
try
{
await _sequence.ToUniTask(cancellationToken: _cancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
DisposeTweenState();
}
ApplyImmediate(string.Empty, false);
}
private void ApplyImmediate(string text, bool visible)
{
if (_interactTextUI != null)
{
if (visible)
_interactTextUI.SetText(text);
else
_interactTextUI.ClearText();
}
if (_canvasGroup == null)
return;
_canvasGroup.alpha = visible ? 1f : 0f;
_canvasGroup.blocksRaycasts = false;
_canvasGroup.interactable = false;
}
private void StopTween()
{
if (_sequence.isAlive)
_sequence.Stop();
_sequence = default;
if (_cancellationTokenSource == null)
return;
try
{
_cancellationTokenSource.Cancel();
}
catch
{
}
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
}
private void DisposeTweenState()
{
_sequence = default;
if (_cancellationTokenSource == null)
return;
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
}
}
}