
using System.Collections;
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class TMPDialogueSystem : MonoBehaviour
{
[System.Serializable]
public class DialogueNode
{
[TextArea(3, 5)]
public string npcText;
public string[] playerOptions;
public int[] nextNodes;
}
// 配置参数
public DialogueNode[] dialogueNodes;
public TextMeshProUGUI chatText;
public TextMeshProUGUI optionsText;
public int maxMessages = 10;
public float messageDelay = 0.5f;
public float fadeInDuration = 0.3f;
public float slideUpDistance = 10f;
// 运行时变量
private int currentNode = 0;
private bool waitingForChoice = false;
private bool canReceiveInput = true;
private List<string> messageList = new List<string>(); // 改用List更方便控制
private Vector3 originalChatPos;
private float lineHeight;
[Header("文字颜色设置")]
public Color npcTextColor = new Color(0.96f, 0.96f, 0.86f); // NPC颜色 #F5F5DC
public Color playerTextColor = new Color(0.6f, 0.8f, 1f); // 玩家颜色 #99CCFF(淡蓝色)
void Awake()
{
originalChatPos = chatText.rectTransform.localPosition;
lineHeight = chatText.fontSize * 1.3f; // 计算行高
}
void Start()
{
// 清空初始文本
chatText.text = "";
optionsText.text = "";
// 强制刷新UI
Canvas.ForceUpdateCanvases();
LayoutRebuilder.ForceRebuildLayoutImmediate(chatText.rectTransform);
// 开始对话
StartCoroutine(ShowDialogueNode(currentNode));
}
void Update()
{
if (waitingForChoice && canReceiveInput)
{
if (Input.GetKeyDown(KeyCode.Alpha1)) StartCoroutine(SelectOption(0));
else if (Input.GetKeyDown(KeyCode.Alpha2)) StartCoroutine(SelectOption(1));
else if (Input.GetKeyDown(KeyCode.Alpha3)) StartCoroutine(SelectOption(2));
}
else if (Input.GetKeyDown(KeyCode.Space) && canReceiveInput && !waitingForChoice)
{
StartCoroutine(ContinueDialogue());
}
}
IEnumerator ShowDialogueNode(int nodeIndex)
{
if (nodeIndex < 0 || nodeIndex >= dialogueNodes.Length)
{
yield return AddMessage("[对话结束]", false);
yield break;
}
var node = dialogueNodes[nodeIndex];
// NPC消息
yield return AddMessage("NPC: " + node.npcText, false);
// 玩家选项
if (node.playerOptions.Length > 0)
{
waitingForChoice = true;
ShowOptions(node.playerOptions);
}
else
{
yield return new WaitForSeconds(messageDelay);
waitingForChoice = false;
currentNode++;
StartCoroutine(ShowDialogueNode(currentNode));
}
}
IEnumerator AddMessage(string message, bool isPlayer)
{
canReceiveInput = false;
// 1. 如果即将超出限制,移除最旧的消息
if (messageList.Count >= maxMessages)
{
messageList.RemoveAt(0);
UpdateChatText(); // 立即更新文本
yield return new WaitForEndOfFrame(); // 等待一帧让UI更新
}
// 2. 添加新消息
messageList.Add(message);
// 3. 创建动画文本
var animText = CreateAnimationText(message, isPlayer);
Vector3 targetPos = CalculateTargetPosition();
// 4. 执行动画
float elapsed = 0;
while (elapsed < fadeInDuration)
{
float t = elapsed / fadeInDuration;
animText.alpha = t;
animText.rectTransform.localPosition = Vector3.Lerp(
targetPos + new Vector3(0, slideUpDistance, 0),
targetPos,
t
);
elapsed += Time.deltaTime;
yield return null;
}
// 5. 更新主文本并清理
UpdateChatText();
Destroy(animText.gameObject);
canReceiveInput = true;
}
TextMeshProUGUI CreateAnimationText(string msg, bool isPlayer)
{
GameObject obj = new GameObject("AnimText");
obj.transform.SetParent(chatText.transform.parent);
var textComp = obj.AddComponent<TextMeshProUGUI>();
textComp.text = msg;
textComp.font = chatText.font;
textComp.fontSize = chatText.fontSize;
textComp.alignment = chatText.alignment;
textComp.rectTransform.sizeDelta = chatText.rectTransform.sizeDelta;
textComp.rectTransform.localPosition = CalculateTargetPosition() + new Vector3(0, slideUpDistance, 0);
textComp.alpha = 0;
textComp.color = isPlayer ? playerTextColor : npcTextColor;
// 如果需要更平滑的渐变,可以初始设为透明
textComp.color = new Color(textComp.color.r, textComp.color.g, textComp.color.b, 0);
return textComp;
}
Vector3 CalculateTargetPosition()
{
int lineIndex = Mathf.Min(messageList.Count, maxMessages) - 1;
return originalChatPos - new Vector3(0, lineIndex * lineHeight, 0);
}
void UpdateChatText()
{
// 使用富文本标签保留颜色
StringBuilder coloredText = new StringBuilder();
foreach (string msg in messageList)
{
bool isPlayer = msg.StartsWith("你:");
Color color = isPlayer ? playerTextColor : npcTextColor;
// 将颜色转换为Hex格式(例如 #FF0000)
string colorHex = ColorUtility.ToHtmlStringRGB(color);
coloredText.AppendLine($"<color=#{colorHex}>{msg}</color>");
}
chatText.text = coloredText.ToString();
Canvas.ForceUpdateCanvases();
// 保持滚动到底部
var scrollRect = chatText.GetComponentInParent<ScrollRect>();
if (scrollRect != null)
{
scrollRect.verticalNormalizedPosition = 0;
LayoutRebuilder.ForceRebuildLayoutImmediate(scrollRect.content);
}
}
IEnumerator SelectOption(int optionIndex)
{
if (currentNode < 0 || currentNode >= dialogueNodes.Length) yield break;
canReceiveInput = false;
var node = dialogueNodes[currentNode];
if (optionIndex >= 0 && optionIndex < node.playerOptions.Length)
{
// 添加玩家选择
yield return AddMessage("你: " + node.playerOptions[optionIndex], true);
waitingForChoice = false;
HideOptions();
// 跳转到下一个节点
currentNode = node.nextNodes[optionIndex];
yield return new WaitForSeconds(messageDelay * 0.5f);
StartCoroutine(ShowDialogueNode(currentNode));
}
canReceiveInput = true;
}
IEnumerator ContinueDialogue()
{
canReceiveInput = false;
yield return new WaitForSeconds(messageDelay);
currentNode++;
StartCoroutine(ShowDialogueNode(currentNode));
canReceiveInput = true;
}
void ShowOptions(string[] options)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < options.Length; i++)
{
sb.AppendLine($"{i + 1}. {options[i]}");
}
optionsText.text = sb.ToString();
}
void HideOptions() => optionsText.text = "";
}

1317

被折叠的 条评论
为什么被折叠?



