using System.Collections;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace BaseGames.Dialogue
{
///
/// 对话框 UI 组件(架构 14_NarrativeModule §5)。
/// 挂载在 Canvas_Overlay 下的 DialogueBox 子对象。
/// 负责打字机效果、头像显示、继续提示。
///
public class DialogueUI : MonoBehaviour
{
[SerializeField] private GameObject _rootPanel;
[SerializeField] private TMP_Text _speakerNameText;
[SerializeField] private TMP_Text _dialogueText;
[SerializeField] private GameObject _speakerNamePanel; // 无名称时隐藏整个名称框
[SerializeField] private GameObject _continuePrompt; // "▼" 图标,打字完成后显示
[SerializeField] private Image _speakerPortrait; // 角色头像框
private Coroutine _typingCoroutine;
private DialogueLine _currentLine;
private const float DefaultTypewriterDelay = 0.03f;
/// 当前是否仍在执行打字机效果。
public bool IsTyping { get; private set; }
// ── 公开 API ──────────────────────────────────────────────────────
/// 显示一行对话并开始打字机效果。
public void ShowLine(DialogueLine line)
{
_currentLine = line;
_rootPanel.SetActive(true);
_continuePrompt.SetActive(false);
// 说话人名称
bool hasSpeaker = !string.IsNullOrEmpty(line.speakerNameKey);
if (_speakerNamePanel != null) _speakerNamePanel.SetActive(hasSpeaker);
if (hasSpeaker && _speakerNameText != null)
_speakerNameText.text = line.speakerNameKey;
// 头像
if (_speakerPortrait != null)
{
_speakerPortrait.gameObject.SetActive(line.portraitSprite != null);
if (line.portraitSprite != null) _speakerPortrait.sprite = line.portraitSprite;
}
// 开始打字机协程
if (_typingCoroutine != null) StopCoroutine(_typingCoroutine);
_typingCoroutine = StartCoroutine(TypeLine(line));
}
/// 立即显示全部文字(跳过打字机效果)。
public void SkipTyping()
{
if (!IsTyping) return;
if (_typingCoroutine != null)
{
StopCoroutine(_typingCoroutine);
_typingCoroutine = null;
}
if (_dialogueText != null)
_dialogueText.text = _currentLine.textKey ?? "";
IsTyping = false;
if (_continuePrompt != null) _continuePrompt.SetActive(true);
}
/// 隐藏对话框面板。
public void Hide()
{
if (_rootPanel != null) _rootPanel.SetActive(false);
}
// ── 内部协程 ──────────────────────────────────────────────────────
private IEnumerator TypeLine(DialogueLine line)
{
IsTyping = true;
float delay = line.typewriterDelay > 0f ? line.typewriterDelay : DefaultTypewriterDelay;
string text = line.textKey ?? "";
// 性能:StringBuilder 避免每帧字符串分配(O(n²) → O(n))
var sb = new StringBuilder(text.Length);
if (_dialogueText != null) _dialogueText.text = "";
foreach (char c in text)
{
sb.Append(c);
if (_dialogueText != null) _dialogueText.SetText(sb); // TMP SetText(StringBuilder) 零分配
yield return new WaitForSecondsRealtime(delay);
}
IsTyping = false;
if (_continuePrompt != null) _continuePrompt.SetActive(true);
_typingCoroutine = null;
}
}
}