Unity C#游戏开发:多NPC共用Quiz管理器的答题独立判断问题
Unity Quiz系统:实现单个NPC答题状态独立的解决方案
问题描述
使用Unity和C#开发游戏Quiz系统,5个NPC共享同一触发器与NPCQuizManager脚本。需要实现单个NPC的Quiz是否已正确回答的判断,且不影响其他NPC的Quiz。但使用全局bool变量时出现问题:答对NPC1的Quiz后,NPC2的Quiz也被错误标记为已完成。
解决方案核心思路
问题根源在于单例Manager中的全局状态被所有NPC共用,因此需要给每个NPC添加独立的状态记录组件,让Manager仅处理当前交互NPC的状态更新。
步骤1:创建NPC独立状态脚本
给每个NPC挂载该脚本,用于记录自身的Quiz完成状态:
using UnityEngine; public class NPCQuizState : MonoBehaviour { // 标记该NPC的Quiz是否已被正确回答 public bool hasCompletedQuiz = false; }
步骤2:修改NPC触发器逻辑
在每个NPC的触发器脚本中,触发前先检查自身的Quiz状态,确保未完成时才调用Manager:
using UnityEngine; public class NPCQuizTrigger : MonoBehaviour { [SerializeField] private TextAsset inkQuizData; [SerializeField] private int correctChoiceIndex; private NPCQuizState _npcQuizState; private void Awake() { _npcQuizState = GetComponent<NPCQuizState>(); } private void OnTriggerEnter(Collider other) { // 仅当玩家触发、NPC未完成Quiz、且当前无对话时启动Quiz if (other.CompareTag("Player") && !_npcQuizState.hasCompletedQuiz && !NPCQuizManager.GetInstance().dialogIsActive) { NPCQuizManager.GetInstance().StartDialogWithNPC(inkQuizData, correctChoiceIndex, _npcQuizState); } } }
步骤3:修改NPCQuizManager逻辑
更新Manager,使其接收当前交互NPC的状态引用,并在答对时标记该NPC的状态:
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using Ink.Runtime; using UnityEngine.EventSystems; using Unity.Netcode; using Unity.Netcode.Transports.UTP; public class NPCQuizManager : MonoBehaviour { [Header("Dialog UI")] [SerializeField] private GameObject dialogPanel; [SerializeField] private TextMeshProUGUI dialogText; [Header("Choices UI")] [SerializeField] private GameObject[] choices; private TextMeshProUGUI[] choiceTexts; private Story currentStory; public bool dialogIsActive{get; private set;} private static NPCQuizManager instance; private int rightChoice; // 新增:保存当前交互的NPC状态引用 private NPCQuizState _currentInteractingNPCState; HealthManager hpLoss; public GameObject player; private void Awake(){ if(instance != null){ Debug.LogWarning("Es gibt mehrere Dialog Manager in der Scene!"); } instance = this; } public static NPCQuizManager GetInstance(){ if(instance == null){ instance = new NPCQuizManager(); } return instance; } private void Start(){ dialogIsActive = false; dialogPanel.SetActive(false); choiceTexts = new TextMeshProUGUI[choices.Length]; int index = 0; foreach(GameObject choice in choices){ choiceTexts[index] = choice.GetComponentInChildren<TextMeshProUGUI>(); index++; } } private void Update(){ if(dialogIsActive){ if(Input.GetKeyDown(KeyCode.E)){ ContinueStory(); } } } // 替换原StartDialog方法,新增NPC状态参数 public void StartDialogWithNPC(TextAsset inkJSON, int correctChoice, NPCQuizState npcState){ rightChoice = correctChoice; currentStory = new Story(inkJSON.text); _currentInteractingNPCState = npcState; dialogIsActive = true; dialogPanel.SetActive(dialogIsActive); ContinueStory(); } public void ExitDialogeMode(){ dialogIsActive = false; dialogPanel.SetActive(false); dialogText.text = ""; _currentInteractingNPCState = null; // 清空当前交互的NPC状态引用 } private void ContinueStory(){ if(currentStory.canContinue){ dialogText.text = currentStory.Continue(); DisplayChoices(); } else{ ExitDialogeMode(); } } private void DisplayChoices(){ List<Choice> currentChoices = currentStory.currentChoices; if(currentChoices.Count > choices.Length){ Debug.LogWarning("Es gibt mehr Choices als UI Elemente! Es werden nur: " + currentChoices.Count + " angezeigt!"); } int index = 0; foreach(Choice choice in currentChoices){ choices[index].SetActive(true); choiceTexts[index].text = choice.text; index++; } for(int i = index; i < choices.Length; i++){ choices[i].SetActive(false); } StartCoroutine(SelectFirstChoice()); } private IEnumerator SelectFirstChoice(){ EventSystem.current.SetSelectedGameObject(null); yield return new WaitForEndOfFrame(); EventSystem.current.SetSelectedGameObject(choices[0].gameObject); } public void chooseChoice(int choiceIndex){ currentStory.ChooseChoiceIndex(choiceIndex); if(choiceIndex == rightChoice){ AddECTS(); // 标记当前交互NPC的Quiz已完成 if(_currentInteractingNPCState != null){ _currentInteractingNPCState.hasCompletedQuiz = true; } } else{ looseHP(); } ContinueStory(); } public void AddECTS(){ player = GameObject.FindGameObjectWithTag("Player"); player.GetComponent<ECTSCounter>().erhoeheWert(5); } public void looseHP(){ hpLoss = GameObject.Find("Healthbar").GetComponent<HealthManager>(); hpLoss.TakeDamage(2); } }
关键修改说明
- 每个NPC拥有独立的
NPCQuizState组件,存储自身的Quiz完成状态,避免全局变量冲突 - 触发器启动Quiz前先检查NPC的状态,确保未完成时才触发
- Manager在交互时保存当前NPC的状态引用,答对后仅更新该NPC的状态,不影响其他NPC
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

