unity仿写The Beginner‘s Guide对话系统

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

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 = "";
}

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值