You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity游戏对话系统脚本问题:多对象时对话框不显示

Unity对话系统多对象异常问题

我正在为Unity游戏编写对话系统脚本,参考网络教程后自主完成开发。单个对象测试时功能正常,但添加第二个对象后,对话框无法显示,仅能听到字符输出音效。

以下是我的代码:

using UnityEngine;
using TMPro;
using System.Collections;

public class DialogueSystem : MonoBehaviour
{
    [SerializeField] GameObject dialogueBox;
    [SerializeField] TextMeshProUGUI dialogueText;
    [SerializeField] string[] lines;
    [SerializeField] AudioSource printSFX;
    [SerializeField] float printTime = 0.1f;
    private int index;
    public bool playerNearby;

    // Start is called before the first frame update
    void Start()
    {
        dialogueText.text = string.Empty;
    }

    // Update is called once per frame
    void Update()
    {

        if (Input.GetKeyDown(KeyCode.E) && playerNearby)
        {
            if(dialogueBox.activeInHierarchy)
                CloseDialogue();
            else
                StartDialogue();
        } 
        if(dialogueText.text == lines[index] && Input.GetKeyDown(KeyCode.Z))
            NextLine();
        if(!playerNearby)
            CloseDialogue();
    }

    private void StartDialogue()
    {
        dialogueBox.SetActive(true);
        index = 0;
        StartCoroutine(PrintLine());
    }

    IEnumerator PrintLine()
    {
        foreach(char letter in lines[index].ToCharArray())
        {
            dialogueText.text += letter;
            printSFX.Play();
            yield return new WaitForSeconds(printTime);
        }
    }

    private void NextLine()
    {
        if (index < lines.Length-1) 
        {
            index++;
            dialogueText.text = string.Empty;
            StartCoroutine(PrintLine());
        } else
        {
            CloseDialogue();
        }
    }

    public void CloseDialogue()
    {
        dialogueText.text = string.Empty;
        index = 0;
        dialogueBox.SetActive(false) ;
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            playerNearby = true;
        }
    }

    public void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            playerNearby = false;
        }
    }
}

问题核心原因

每个对话对象都独立持有dialogueBox和dialogueText引用,当多个对象同时存在时,不同对象的逻辑会互相干扰:

  • 玩家靠近第二个对象时,第一个对象的playerNearby会变为false,触发其CloseDialogue方法,直接关闭对话框
  • 多个对象的Update方法同时执行,导致对话框的显示/关闭状态被频繁覆盖

修复方案:全局单例管理对话框

将对话框UI设为全局唯一实例,所有对话对象共享同一套对话框逻辑,避免多实例冲突。

1. 创建全局对话管理器(单例)

using UnityEngine;
using TMPro;
using System.Collections;

public class DialogueManager : MonoBehaviour
{
    public static DialogueManager Instance;

    [SerializeField] GameObject dialogueBox;
    [SerializeField] TextMeshProUGUI dialogueText;
    [SerializeField] AudioSource printSFX;
    [SerializeField] float printTime = 0.1f;

    private string[] currentLines;
    private int currentIndex;
    private bool isPrinting;

    private void Awake()
    {
        if (Instance == null)
            Instance = this;
        else
            Destroy(gameObject);
    }

    public void StartDialogue(string[] lines)
    {
        currentLines = lines;
        currentIndex = 0;
        dialogueBox.SetActive(true);
        dialogueText.text = string.Empty;
        StartCoroutine(PrintLine());
    }

    public void NextLine()
    {
        if (isPrinting) return;
        
        if (currentIndex < currentLines.Length - 1)
        {
            currentIndex++;
            dialogueText.text = string.Empty;
            StartCoroutine(PrintLine());
        }
        else
        {
            CloseDialogue();
        }
    }

    public void CloseDialogue()
    {
        dialogueText.text = string.Empty;
        currentIndex = 0;
        dialogueBox.SetActive(false);
        StopAllCoroutines();
        isPrinting = false;
    }

    IEnumerator PrintLine()
    {
        isPrinting = true;
        foreach (char letter in currentLines[currentIndex].ToCharArray())
        {
            dialogueText.text += letter;
            printSFX.Play();
            yield return new WaitForSeconds(printTime);
        }
        isPrinting = false;
    }

    public bool IsDialogueActive()
    {
        return dialogueBox.activeInHierarchy;
    }
}

2. 修改原对话对象脚本

移除独立的UI引用,改为调用全局管理器的方法:

using UnityEngine;

public class DialogueSystem : MonoBehaviour
{
    [SerializeField] string[] lines;
    private bool playerNearby;
    private bool isTalking;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.E) && playerNearby)
        {
            if (DialogueManager.Instance.IsDialogueActive() && isTalking)
                DialogueManager.Instance.CloseDialogue();
            else
            {
                DialogueManager.Instance.StartDialogue(lines);
                isTalking = true;
            }
        }

        if (Input.GetKeyDown(KeyCode.Z) && isTalking)
            DialogueManager.Instance.NextLine();

        if (!playerNearby && isTalking)
        {
            DialogueManager.Instance.CloseDialogue();
            isTalking = false;
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            playerNearby = true;
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.CompareTag("Player"))
        {
            playerNearby = false;
        }
    }
}

使用步骤

  1. 创建空对象命名为DialogueManager,挂载上述单例脚本,并赋值对话框UI、文本组件和音效源
  2. 移除每个对话对象上的dialogueBox、dialogueText、printSFX引用,仅保留lines数组的内容设置
  3. 确保玩家对象带有Player标签,且所有对话对象的碰撞体已勾选“Is Trigger”

内容的提问来源于stack exchange,提问作者Ivan Martynov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 00:56:22