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

Unity日语假名答题游戏题库优化方案咨询

Unity日语假名答题游戏优化方案需求

我是新手开发者,正在开发一款日语平假名、片假名答题游戏,玩法为展示日文假名,玩家从4个发音选项中选择正确答案。目前所有题目与答案都在Inspector中配置,两类题库各71题,每题含71个选项(超1万条字符串),新增字符时卡顿严重,尝试拆分题目与答案但不知如何操作,现寻求优化方案。参考过Brackey的答题教程、YouTube教程及ChatGPT,核心代码如下:

核心代码

GameManager类

public class GameManager : MonoBehaviour
{
    public Animator playerAnimator;
    public Animator enemyAnimator;

    public GameObject healthBar;
    public GameObject enemyHealthBar;

    public Question[] katakanaQuestions;
    public Question[] hiraganaQuestions;

    [SerializeField] private float timeBetweenQuestions;

    private static List<Question> unansweredQuestions;

    private Question currentQuestion;

    [SerializeField] private TMP_Text factText;
    [SerializeField] private Button[] answerButtons;
  
    private int correctAnswerIndex;

    private bool isAnsweringQuestion = false;

    void Start()
    {
        LoadSelectedCategory();
        SetCurrentQuestion();
    }

    private void LoadSelectedCategory()
    {
        bool isHiraganaSelected = PlayerPrefs.GetInt("IsHiraganaSelected", 1) == 1;

        Question[] selectedQuestions = isHiraganaSelected ? hiraganaQuestions : katakanaQuestions;
        
        unansweredQuestions = selectedQuestions.ToList();
    }

    void SetCurrentQuestion()
    {
        int randomQuestionIndex = Random.Range(0, unansweredQuestions.Count);
        currentQuestion = unansweredQuestions[randomQuestionIndex];

        factText.text = currentQuestion.question;

        
        List<string> answerChoices = new List<string>();    // 创建包含正确答案和三个随机错误答案的选项列表

        answerChoices.Add(currentQuestion.answers[currentQuestion.correctAnswerIndex]);  // 添加正确答案
        List<string> incorrectAnswers = currentQuestion.answers.ToList();
        
        incorrectAnswers.RemoveAt(currentQuestion.correctAnswerIndex);       // 移除正确答案
        for (int i = 0; i < 3; i++)      // 添加剩余三个随机错误答案
        {
            int randomIndex = Random.Range(0, incorrectAnswers.Count);
            answerChoices.Add(incorrectAnswers[randomIndex]);
            incorrectAnswers.RemoveAt(randomIndex);
        }

        // 打乱顺序
        int n = answerChoices.Count;
        while (n > 1)
        {
            n--;
            int k = Random.Range(0, n + 1);
            string temp = answerChoices[k];
            answerChoices[k] = answerChoices[n];
            answerChoices[n] = temp;
        }

        // 设置按钮文本
        for (int i = 0; i < answerButtons.Length; i++)
        {
            answerButtons[i].GetComponentInChildren<TMP_Text>().text = answerChoices[i];
        }

        // 正确按钮索引
        correctAnswerIndex = answerChoices.IndexOf(currentQuestion.answers[currentQuestion.correctAnswerIndex]);

    }

    public void CheckAnswer(int answerIndex)
    {
        if (isAnsweringQuestion)
        {
            Debug.Log("Wait for the next question!");
            return;
        }

        isAnsweringQuestion = true; // 玩家正在答题

        StartCoroutine(CheckAnswerCoroutine(answerIndex));
    }

    private IEnumerator CheckAnswerCoroutine(int answerIndex)
    {
        //yield return new WaitForSeconds(0.3f);

        // 存储按钮原始颜色
        Color[] originalColors = new Color[answerButtons.Length];
        for (int i = 0; i < answerButtons.Length; i++)
        {
            originalColors[i] = answerButtons[i].GetComponent<Image>().color;
        }

        for (int i = 0; i < answerButtons.Length; i++)
        {
            if (i == correctAnswerIndex)
            {
                // 将正确答案按钮改为特定绿色(0.1f, 0.8f, 0.1f)
                answerButtons[i].GetComponent<Image>().color = new Color(0.1f, 0.8f, 0.1f);
            }
            else if (i == answerIndex)
            {
                // 将选中的错误答案按钮改为特定红色(0.8f, 0.1f, 0.1f)
                answerButtons[i].GetComponent<Image>().color = new Color(0.8f, 0.1f, 0.1f);
            }
        }

        yield return new WaitForSeconds(1f); // 等待一段时间展示按钮颜色

        // 重置按钮颜色为原始状态
        for (int i = 0; i < answerButtons.Length; i++)
        {
            answerButtons[i].GetComponent<Image>().color = originalColors[i];
        }

        if (answerIndex == correctAnswerIndex)
        {
            Debug.Log("Correct!");

            Attack();
            enemyHealthBar.GetComponent<EnemyHealthBar>().TakeDamage(1);
        }
        else
        {
            Debug.Log("Wrong answer");

            GetHit();
            healthBar.GetComponent<HealthBar>().TakeDamage(1);
        }

        yield return new WaitForSeconds(timeBetweenQuestions); // 等待下一题

        isAnsweringQuestion = false;

        SetCurrentQuestion();
    }
}

序列化数据类

[System.Serializable]
public class Question
{
    public string question;
    public int correctAnswerIndex;
    public string[] answers;
}

[System.Serializable]
public class KatakanaQuestion
{
    public string question;
    public string[] answers;
    public int correctAnswerIndex;
}

优化方案

1. 拆分题库与全局答案池

当前每题重复存储71个选项,数据冗余严重。改为:

  • 单独维护两类假名的全局答案池:比如hiraganaPronunciations和katakanaPronunciations,各存储71条发音字符串,仅保存一次。
  • 修改Question类,只保留核心数据,删除重复的answers数组:
    [System.Serializable]
    public class Question
    {
        public string kana; // 要展示的平假名/片假名
        public string correctPronunciation; // 对应的正确发音
    }
    
  • 生成选项时,从全局答案池中随机选取3个错误选项+正确答案,避免重复存储大量字符串。

2. 用ScriptableObject管理题库

将题库从Inspector直接配置改为外部文件存储,减少Editor卡顿:

  • 创建KanaQuestionSet ScriptableObject,整合题库和答案池:
    [CreateAssetMenu(fileName = "KanaQuestionSet", menuName = "Game/Kana Question Set")]
    public class KanaQuestionSet : ScriptableObject
    {
        public List<Question> questions;
        public List<string> allPronunciations; // 该类假名的所有发音选项
    }
    
  • 在Project窗口创建两个实例:HiraganaSet和KatakanaSet,分别配置对应的题库和答案池。
  • GameManager中引用这两个ScriptableObject,而非直接的数组。ScriptableObject的数据存储在外部文件,不会在Inspector中实时渲染大量字符串,大幅降低卡顿。

3. 优化选项生成逻辑

当前每次生成选项时都要复制整个答案数组,改为更高效的写法:

void SetCurrentQuestion()
{
    // 随机选择当前题目
    int randomQuestionIndex = Random.Range(0, unansweredQuestions.Count);
    currentQuestion = unansweredQuestions[randomQuestionIndex];
    factText.text = currentQuestion.kana;

    // 获取当前类别的全局答案池
    List<string> allPronunciations = isHiraganaSelected ? hiraganaSet.allPronunciations : katakanaSet.allPronunciations;
    string correctAnswer = currentQuestion.correctPronunciation;

    // 筛选错误选项并随机选3个
    List<string> incorrectOptions = allPronunciations.Where(p => p != correctAnswer).ToList();
    List<string> randomIncorrect = incorrectOptions.OrderBy(_ => Random.value).Take(3).ToList();

    // 组合选项并打乱顺序
    List<string> answerChoices = new List<string> { correctAnswer };
    answerChoices.AddRange(randomIncorrect);
    answerChoices = answerChoices.OrderBy(_ => Random.value).ToList();

    // 设置按钮文本
    for (int i = 0; i < answerButtons.Length; i++)
    {
        answerButtons[i].GetComponentInChildren<TMP_Text>().text = answerChoices[i];
    }

    // 记录正确答案索引
    correctAnswerIndex = answerChoices.IndexOf(correctAnswer);
}

4. 减少Inspector实时渲染压力

如果坚持用Inspector配置数组,可将Question数组标记为HideInInspector,通过编辑器扩展工具批量导入数据,或者在Edit Mode下禁用不必要的数据刷新逻辑。

5. 数据预加载与缓存

在Start方法中预加载选中类别的题库和答案池,避免运行时重复读取;将全局答案池缓存为HashSet或数组,提升错误选项的筛选效率。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 18:54:51