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; } } }
使用步骤
- 创建空对象命名为
DialogueManager,挂载上述单例脚本,并赋值对话框UI、文本组件和音效源 - 移除每个对话对象上的
dialogueBox、dialogueText、printSFX引用,仅保留lines数组的内容设置 - 确保玩家对象带有
Player标签,且所有对话对象的碰撞体已勾选“Is Trigger”
内容的提问来源于stack exchange,提问作者Ivan Martynov
相关产品推荐
相关产品推荐

