using System;
using DG.Tweening;
using TMPro;
using UnityEngine;
namespace Ichni.Story
{
///
/// 由 按需实例化的 Helper 专属对话气泡。
/// 每个实例只显示一条文本:淡入、停留、淡出后自动销毁,因此场景中不需要常驻气泡,
/// 也不会与通用 MessageBox 队列产生耦合。
///
[RequireComponent(typeof(CanvasGroup))]
public class StoryHelperTalk : MonoBehaviour
{
[SerializeField, Tooltip("显示 Helper 名称的文本。")]
private TMP_Text speakerText;
[SerializeField, Tooltip("显示随机 Helper 对话的文本。")]
private TMP_Text contentText;
[Header("Animation")]
[SerializeField, Min(0.01f), Tooltip("淡入与缩放入场时间。")]
private float showDuration = 0.18f;
[SerializeField, Min(0f), Tooltip("气泡完整显示后的停留时间。")]
private float visibleDuration = 4.5f;
[SerializeField, Min(0.01f), Tooltip("淡出与缩放退场时间。")]
private float hideDuration = 0.16f;
[SerializeField, Range(0.5f, 1f), Tooltip("入场与退场时相对原始大小的缩放比例。")]
private float initialScaleMultiplier = 0.92f;
/// 气泡结束显示并准备销毁时触发,供创建者清理当前实例引用。
public event Action Closed;
private CanvasGroup _canvasGroup;
private RectTransform _rectTransform;
private Sequence _sequence;
private Vector3 _baseScale;
private bool _isClosed;
private void Awake()
{
_canvasGroup = GetComponent();
_rectTransform = transform as RectTransform;
_baseScale = _rectTransform != null ? _rectTransform.localScale : Vector3.one;
}
///
/// 写入文本并播放完整的出现、停留与收回流程。
/// 所有 DOTween Sequence 均先 Pause,再手动调用 Play,符合项目约定。
///
public void Show(string speaker, string content)
{
if (speakerText == null || contentText == null || _rectTransform == null)
{
Debug.LogWarning("[StoryHelperTalk] Prefab 未配置 Speaker Text、Content Text 或 RectTransform,已取消显示。", this);
CloseImmediately();
return;
}
speakerText.text = speaker;
contentText.text = content;
_canvasGroup.alpha = 0f;
_rectTransform.localScale = _baseScale * initialScaleMultiplier;
_sequence?.Kill();
_sequence = DOTween.Sequence()
.Pause()
.SetUpdate(true)
.Append(_canvasGroup.DOFade(1f, showDuration))
.Join(_rectTransform.DOScale(_baseScale, showDuration).SetEase(Ease.OutBack))
.AppendInterval(visibleDuration)
.Append(_canvasGroup.DOFade(0f, hideDuration))
.Join(_rectTransform.DOScale(_baseScale * initialScaleMultiplier, hideDuration).SetEase(Ease.InQuad))
.OnComplete(CloseImmediately);
_sequence.Play();
}
///
/// 立即关闭并销毁该运行时实例。再次点击 Helper 时会调用它,使旧文本不会与新文本叠加。
///
public void CloseImmediately()
{
if (_isClosed)
return;
_isClosed = true;
_sequence?.Kill();
_sequence = null;
Closed?.Invoke(this);
Destroy(gameObject);
}
private void OnDestroy()
{
_sequence?.Kill();
}
}
}