sega 116b9b3116 Рефакторинг Character.cs: приведение к SRP и вынос логики
Инкапсулированы методы появления и ухода персонажа
Инкапсулирована работа с аниматором
Работа с репликами вынесена в ScriptableObject
Логика MessageBox вынесена в отдельный класс
Переработан event bus персонажей
Сложность и призовой модуль вынесены в отдельные классы
Character.cs приведён к Single Responsibility Principle.
2026-08-13 16:19:56 +03:00

122 lines
3.7 KiB
C#

using Cysharp.Threading.Tasks;
using DG.Tweening;
using System.Threading;
using System.Threading.Tasks;
using TMPro;
using UnityEngine;
namespace CharacterSystem
{
public class MessageBox : MonoBehaviour
{
[SerializeField] private Transform _frame;
[SerializeField] private Vector2 _localPos;
[SerializeField] private TextMeshProUGUI nameLabel;
[SerializeField] private TextMeshProUGUI textField;
[SerializeField] private AudioSource _audioSource;
[SerializeField] private AudioClip typeClip;
private bool nowTyping;
private CancellationTokenSource _cts;
private float lastTypeSoundTime;
private float typeClipLength;
public void Initialize(string characterName)
{
nameLabel.text = characterName;
_frame.position = _localPos;
lastTypeSoundTime = 0;
typeClipLength = typeClip != null ? typeClip.length : 0;
}
public async UniTask PlayReplica(Replica replica, bool force = false)
{
if (nowTyping && force == false) return;
Stop();
_cts = new();
nowTyping = true;
await GetShowSequence().WithCancellation(_cts.Token);
await TypeAsync(replica, _cts.Token);
await UniTask.Delay(1000, cancellationToken: _cts.Token);
await GetHideSequence().WithCancellation(_cts.Token);
Stop();
}
private async UniTask TypeAsync(Replica replica, CancellationToken ct)
{
string message = replica.text;
if (replica.Duration == 0 || string.IsNullOrEmpty(message))
{
textField.text = message;
return;
}
if (replica.clip != null)
{
_audioSource.clip = replica.clip;
_audioSource.Play();
SoundManager.instance.MusicFade(fadeDuration: 0.2f,
holdDuration: replica.clip.length - 0.2f,
targetVolumePercent: 0.5f);
}
float elapsed = 0;
textField.text = string.Empty;
while (elapsed < replica.Duration && ct.IsCancellationRequested == false)
{
float percent = Mathf.Clamp01(elapsed / replica.Duration);
int symbolsToShow = (int)(message.Length * percent);
string text = message.Substring(0, symbolsToShow);
textField.text = text;
bool shouldPlayClip = typeClipLength != 0 && Time.time - lastTypeSoundTime > typeClipLength;
if (shouldPlayClip)
{
_audioSource.PlayOneShot(typeClip);
lastTypeSoundTime = Time.time;
}
await UniTask.Yield(ct);
elapsed += Time.deltaTime;
}
if (ct.IsCancellationRequested == false)
{
textField.text = message;
}
}
public void Stop()
{
if (_cts != null)
{
_cts.Cancel();
_cts.Dispose();
_cts = null;
}
nowTyping = false;
textField.text = string.Empty;
}
private Sequence GetShowSequence()
{
Sequence seq = DOTween.Sequence();
seq.Append(_frame.DOScale(1, 0.2f).SetEase(Ease.OutBack));
return seq;
}
private Sequence GetHideSequence()
{
Sequence seq = DOTween.Sequence();
seq.Append(_frame.DOScale(0, 0.2f).SetEase(Ease.InBack));
return seq;
}
}
}