2026-04-24 16:57:44 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using Kill.Managers;
|
|
|
|
|
|
using Kill.UI.Pages;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
|
|
|
|
|
|
|
public class SafetylearningTestPage : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
public Transform questionContainer;
|
|
|
|
|
|
public GameObject questionPrefab;
|
|
|
|
|
|
public Button nextButton;
|
|
|
|
|
|
public Button submitButton;
|
|
|
|
|
|
public GameObject answerPrefab;
|
|
|
|
|
|
public List<GameObject> sonObjs;
|
|
|
|
|
|
|
|
|
|
|
|
// 题库(所有未答对2次的题目)
|
|
|
|
|
|
private List<QuestionInfo> questionBank = new List<QuestionInfo>();
|
|
|
|
|
|
// 答题队列(当前答题顺序)
|
|
|
|
|
|
public List<QuestionInfo> questionQueue = new List<QuestionInfo>();
|
|
|
|
|
|
// 答题记录(记录每道题答对次数)
|
|
|
|
|
|
private Dictionary<int, int> correctCountMap = new Dictionary<int, int>();
|
|
|
|
|
|
|
|
|
|
|
|
private int currentQuestionIndex = 0;
|
|
|
|
|
|
private int totalAnsweredCount = 0;
|
|
|
|
|
|
private SafetylearningQuestion currentQuestion;
|
|
|
|
|
|
private GameObject currentAnswerObj;
|
|
|
|
|
|
|
|
|
|
|
|
public void Init(List<QuestionInfo> questions)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 初始化题库
|
|
|
|
|
|
questionBank = new List<QuestionInfo>(questions);
|
|
|
|
|
|
correctCountMap.Clear();
|
|
|
|
|
|
totalAnsweredCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化答题队列(按顺序)
|
|
|
|
|
|
InitializeQueue();
|
|
|
|
|
|
|
|
|
|
|
|
// 显示第一题
|
|
|
|
|
|
ShowQuestion(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化答题队列(按顺序)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void InitializeQueue()
|
|
|
|
|
|
{
|
|
|
|
|
|
questionQueue.Clear();
|
|
|
|
|
|
// 按顺序将题库中所有题目加入队列
|
|
|
|
|
|
foreach (var question in questionBank)
|
|
|
|
|
|
{
|
|
|
|
|
|
questionQueue.Add(question);
|
|
|
|
|
|
}
|
|
|
|
|
|
currentQuestionIndex = 0;
|
|
|
|
|
|
Debug.Log($"初始化答题队列,共 {questionQueue.Count} 道题");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 重新生成队列(答错时调用)
|
|
|
|
|
|
/// 清空队列,将题库中所有题目打乱顺序生成新队列
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void RegenerateQueue()
|
|
|
|
|
|
{
|
|
|
|
|
|
questionQueue.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
// 将题库中所有题目加入队列
|
|
|
|
|
|
foreach (var question in questionBank)
|
|
|
|
|
|
{
|
|
|
|
|
|
questionQueue.Add(question);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 打乱队列
|
|
|
|
|
|
ShuffleList(questionQueue);
|
|
|
|
|
|
|
|
|
|
|
|
currentQuestionIndex = 0;
|
|
|
|
|
|
Debug.Log($"重新生成答题队列,共 {questionQueue.Count} 道题");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从题库中移除题目(答对2次时调用)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void RemoveFromBank(int questionId)
|
|
|
|
|
|
{
|
|
|
|
|
|
var question = questionBank.Find(q => q.id == questionId);
|
|
|
|
|
|
if (question != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
questionBank.Remove(question);
|
|
|
|
|
|
Debug.Log($"题目 {questionId} 已从题库中移除");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Fisher-Yates 洗牌算法
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShuffleList<T>(List<T> list)
|
|
|
|
|
|
{
|
|
|
|
|
|
System.Random rng = new System.Random();
|
|
|
|
|
|
int n = list.Count;
|
|
|
|
|
|
while (n > 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
n--;
|
|
|
|
|
|
int k = rng.Next(n + 1);
|
|
|
|
|
|
T value = list[k];
|
|
|
|
|
|
list[k] = list[n];
|
|
|
|
|
|
list[n] = value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 显示指定题目
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShowQuestion(int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 检查队列是否为空,为空则通过
|
|
|
|
|
|
if (questionQueue.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log("答题队列已空,通过!");
|
|
|
|
|
|
// 显示完成页面
|
|
|
|
|
|
SafetylearningPageCtrl.Instance?.ShowFinishPage();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 清除旧题目和解析
|
|
|
|
|
|
ClearCurrentQuestion();
|
|
|
|
|
|
|
|
|
|
|
|
currentQuestionIndex = index;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果超出范围,从第一题开始
|
|
|
|
|
|
if (currentQuestionIndex >= questionQueue.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentQuestionIndex = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建新题目
|
|
|
|
|
|
GameObject questionObj = Instantiate(questionPrefab, questionContainer);
|
|
|
|
|
|
sonObjs.Add(questionObj);
|
|
|
|
|
|
currentQuestion = questionObj.GetComponent<SafetylearningQuestion>();
|
|
|
|
|
|
|
|
|
|
|
|
// 使用总答题序号作为题号(累加)
|
|
|
|
|
|
totalAnsweredCount++;
|
|
|
|
|
|
currentQuestion.Init(totalAnsweredCount, questionQueue[currentQuestionIndex]);
|
|
|
|
|
|
|
|
|
|
|
|
// 订阅选项选择事件
|
|
|
|
|
|
currentQuestion.OnOptionSelected += OnOptionSelected;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始状态:隐藏提交和下一题按钮
|
|
|
|
|
|
submitButton.gameObject.SetActive(false);
|
|
|
|
|
|
nextButton.gameObject.SetActive(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 绑定按钮事件
|
|
|
|
|
|
submitButton.onClick.RemoveAllListeners();
|
|
|
|
|
|
submitButton.onClick.AddListener(OnSubmitClick);
|
|
|
|
|
|
|
|
|
|
|
|
nextButton.onClick.RemoveAllListeners();
|
|
|
|
|
|
nextButton.onClick.AddListener(OnNextClick);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 选项被选择
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnOptionSelected()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 有选择时显示提交按钮
|
|
|
|
|
|
if (currentQuestion != null && currentQuestion.HasSelectedAnswer())
|
|
|
|
|
|
{
|
|
|
|
|
|
submitButton.gameObject.SetActive(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 提交答案
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnSubmitClick()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (currentQuestion == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 禁用选项交互
|
|
|
|
|
|
currentQuestion.DisableAllOptions();
|
|
|
|
|
|
submitButton.gameObject.SetActive(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 显示答案判断
|
|
|
|
|
|
currentQuestion.CheckAnswer();
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否正确
|
|
|
|
|
|
string userAnswer = currentQuestion.GetUserAnswer();
|
|
|
|
|
|
string correctAnswer = questionQueue[currentQuestionIndex].correctAnswer;
|
|
|
|
|
|
bool isCorrect = userAnswer == correctAnswer;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取当前题目ID
|
|
|
|
|
|
int currentQuestionId = questionQueue[currentQuestionIndex].id;
|
|
|
|
|
|
|
|
|
|
|
|
if (isCorrect)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 更新答对次数
|
|
|
|
|
|
if (!correctCountMap.ContainsKey(currentQuestionId))
|
|
|
|
|
|
{
|
|
|
|
|
|
correctCountMap[currentQuestionId] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
correctCountMap[currentQuestionId]++;
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否答对2次
|
|
|
|
|
|
if (correctCountMap[currentQuestionId] >= 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 从题库中移除
|
|
|
|
|
|
RemoveFromBank(currentQuestionId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从队列中移除当前题目
|
|
|
|
|
|
questionQueue.RemoveAt(currentQuestionIndex);
|
|
|
|
|
|
// 索引保持不变,因为移除后后面的题目会前移
|
|
|
|
|
|
|
|
|
|
|
|
// 2秒后自动下一题
|
|
|
|
|
|
StartCoroutine(DelayNextQuestion());
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-05-18 08:42:33 +08:00
|
|
|
|
CreateAnswerExplanation();
|
2026-04-24 16:57:44 +08:00
|
|
|
|
// 答错:清空队列,重新生成
|
|
|
|
|
|
RegenerateQueue();
|
|
|
|
|
|
|
|
|
|
|
|
// 显示解析和下一题按钮
|
2026-05-18 08:42:33 +08:00
|
|
|
|
|
2026-04-24 16:57:44 +08:00
|
|
|
|
nextButton.gameObject.SetActive(true);
|
|
|
|
|
|
|
|
|
|
|
|
// 重置索引,确保点击下一题从第一题开始
|
|
|
|
|
|
currentQuestionIndex = -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 延迟下一题
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private IEnumerator DelayNextQuestion()
|
|
|
|
|
|
{
|
2026-05-18 08:42:33 +08:00
|
|
|
|
yield return new WaitForSeconds(1f);
|
2026-04-24 16:57:44 +08:00
|
|
|
|
ShowQuestion(currentQuestionIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建答案解析
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void CreateAnswerExplanation()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (answerPrefab == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
currentAnswerObj = Instantiate(answerPrefab, questionContainer);
|
|
|
|
|
|
sonObjs.Add(currentAnswerObj);
|
|
|
|
|
|
currentAnswerObj.GetComponent<SafetylearningAnswer>().Init(
|
|
|
|
|
|
questionQueue[currentQuestionIndex].options,
|
|
|
|
|
|
questionQueue[currentQuestionIndex].videoUrl
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 下一题
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void OnNextClick()
|
|
|
|
|
|
{
|
|
|
|
|
|
currentQuestionIndex++;
|
|
|
|
|
|
ShowQuestion(currentQuestionIndex);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 清除当前题目和解析
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ClearCurrentQuestion()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 取消订阅事件
|
|
|
|
|
|
if (currentQuestion != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentQuestion.OnOptionSelected -= OnOptionSelected;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 销毁所有子对象
|
|
|
|
|
|
foreach (var obj in sonObjs)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (obj != null)
|
|
|
|
|
|
Destroy(obj);
|
|
|
|
|
|
}
|
|
|
|
|
|
sonObjs.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
currentQuestion = null;
|
|
|
|
|
|
currentAnswerObj = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
submitButton?.onClick.RemoveAllListeners();
|
|
|
|
|
|
nextButton?.onClick.RemoveAllListeners();
|
|
|
|
|
|
|
|
|
|
|
|
if (currentQuestion != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentQuestion.OnOptionSelected -= OnOptionSelected;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|