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

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);
    }
}

关键修改说明

  1. 每个NPC拥有独立的NPCQuizState组件,存储自身的Quiz完成状态,避免全局变量冲突
  2. 触发器启动Quiz前先检查NPC的状态,确保未完成时才触发
  3. Manager在交互时保存当前NPC的状态引用,答对后仅更新该NPC的状态,不影响其他NPC

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:35:28