Unity答题系统

答题系统Demo前言

大四毕设的时候,参考网上大佬的答题系统,移植到了我的毕设项目中去,总感觉那个答题系统不够满意,但是那会技术有限,没有去深入研究完善,而且总有bug,后面答辩后也没管了。工作了,学的东西多了点,琢磨着自己也搞个答题系统玩玩,这个demo用的是加载XML文档的方法创建题目,xml文档中可以设置题目是单选还是多选。有上一题、下一题的功能,每一题都有提交功能,只有选择了答案才能提交,提交了会显示题目的解析。能满足一般的答题系统的需求了。

准备工作

搭建答题系统界面UI

如图所示:
在这里插入图片描述

其中题目选项和解析内容使用滚动视图(scroll view)来规范显示内容。这里还需要在滚动视图的content物体上添加自动布局的组件:设置垂直布局自适应。
在这里插入图片描述
注意:
在使用滚动视图的地方,将content作为父物体的,都要在content物体上添加自动布局组件,并在子元素自适应,不然你会看到生成的题目有可能飞到十万八千里之外,那这样在视图里面就看不到了。

将题库xml文件存放在自己创建的XML文件夹下

xml文档内容格式如下:

在这里插入图片描述

编写脚本实现答题系统相关功能

Panel_Question.cs脚本挂载到主界面Panel_Question上
public class Panel_Question : MonoBehaviour
{
    /// <summary>
    /// 上一题按钮
    /// </summary>
    private Button previousBtn;
    /// <summary>
    /// 提交按钮
    /// </summary>
    private Button submitBtn;
    /// <summary>
    /// 下一题按钮
    /// </summary>
    private Button nextBtn;
    /// <summary>
    /// 当前是第几道题
    /// </summary>
    private Text questionID;
    /// <summary>
    /// 解析内容文本
    /// </summary>
    private Text analysisData;
    /// <summary>
    /// 题目的父节点
    /// </summary>
    private Transform questionRoot;
    private Transform questionBtnRoot;
    /// <summary>
    /// 选项的组
    /// </summary>
    private ToggleGroup questionGroup;
    /// <summary>
    /// 答题界面数据内容
    /// </summary>
    private QuestionPanelData mQuestionPanelData;
    /// <summary>
    /// 每一道题的题目内容
    /// </summary>
    private QuestionData mQuestionData;
    /// <summary>
    /// 题目内容物体
    /// </summary>
    private GameObject mQuestion;
    /// <summary>
    /// 题目计数
    /// </summary>
    private int mQuestionCount;
    /// <summary>
    /// 选项的链表
    /// </summary>
    private List<Options> options=new List<Options>();
    private void Awake()
    {
        Init();
    }
    /// <summary>
    /// 初始化找到相关物体和组件,并给按钮添加监听方法
    /// </summary>
    private void Init()
    {
        previousBtn = transform.Find("BGImage/topic/btn_Previous").GetComponent<Button>();
        submitBtn= transform.Find("BGImage/topic/btn_Submit").GetComponent<Button>();
        nextBtn= transform.Find("BGImage/topic/btn_Next").GetComponent<Button>();
        questionID = transform.Find("BGImage/topic/questionNumber").GetComponent<Text>();
        analysisData= transform.Find("BGImage/analysis/Scroll View/Viewport/Content/Text").GetComponent<Text>();
        questionRoot = transform.Find("BGImage/topic/Scroll View/Viewport/Content").transform;
        questionBtnRoot= transform.Find("BGImage/selectSign/Viewport/Content").transform;
        questionGroup = transform.Find("BGImage/topic/Scroll View/Viewport/Content").GetComponent<ToggleGroup>();

        previousBtn.onClick.AddListener(previousClick);
        submitBtn.onClick.AddListener(submitClick);
        nextBtn.onClick.AddListener(nextClick);
    }
    private void Start()
    {
        StartCoroutine(LoadingQuesiton(DataPath.QuestionData));
    }
    /// <summary>
    /// 点击开始答题事件,将解析好的题目内容初始化显示出来
    /// </summary>
    public void StartAnswer()
    {
        for (int i = 0; i < QuestionPanel.questionPanelData.Count; i++)
        {
            InitQuestionPanel(QuestionPanel.questionPanelData[i]);
        }
    }
    /// <summary>
    /// 加载xml文件的协程
    /// </summary>
    /// <param name="path">文件路径</param>
    /// <returns></returns>
    IEnumerator LoadingQuesiton(string path)
    {
        yield return null;
        using (WWW  www=new WWW (path))
        {
            yield return www;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(www.text);
            new QuestionPanel(doc.FirstChild);
        }
    }
    /// <summary>
    /// 初始化第一道题
    /// </summary>
    /// <param name="questionPanelData"></param>
    public void InitQuestionPanel(QuestionPanelData questionPanelData)
    {
        //this.gameObject.SetActive(true);
        mQuestionCount = 0;
        mQuestionPanelData = questionPanelData;
        CreateQuestion(questionPanelData.questionData[mQuestionCount]);
    }
    /// <summary>
    /// 创建题目
    /// </summary>
    /// <param name="questionData"></param>
    private void CreateQuestion(QuestionData questionData)
    {
        analysisData.text = "";
        mQuestionData = questionData;
        questionID.text = string.Format("第{0}题", mQuestionCount + 1);
        if (mQuestion!=null)
        {
            Destroy(mQuestion);
        }
        //实例化题目预制体
        mQuestion = GameObject.Instantiate(Resources.Load<GameObject>(DataPath.QuestionText));
        mQuestion.transform.SetParent(questionRoot);
        mQuestion.transform.localScale = Vector3.one;
        mQuestion.GetComponent<Text>().text = questionData.problem;

        if (options.Count>0)
        {
            for (int i = 0; i < options.Count; i++)
            {
                Destroy(options[i].gameObject);
            }
        }
        options = new List<Options>();
        //实例化选项预制体
        for (int i = 0; i < questionData.answerData.Count; i++)
        {
            Options option = GameObject.Instantiate(Resources.Load<Options>("Options"));
            option.Init(questionData.answerData[i]);
            option.transform.SetParent(questionRoot);
            //如果是单选则设置为一个toggle组
            if (questionData.isSingleChoice)
            {
                option.thisToggle.group = questionGroup;
            }
            options.Add(option);
        }
    }
    /// <summary>
    /// 上一题点击事件
    /// </summary>
    private void previousClick()
    {
        if (mQuestionCount>0)
        {
            mQuestionCount--;
            CreateQuestion(mQuestionPanelData.questionData[mQuestionCount]);
        }
    }
    /// <summary>
    /// 下一题点击事件
    /// </summary>
    private void nextClick()
    {
        if (mQuestionCount<mQuestionPanelData.questionData.Count-1)
        {
            mQuestionCount++;
            CreateQuestion(mQuestionPanelData.questionData[mQuestionCount]);
        }
    }

    private string rightAnswer;
    /// <summary>
    /// 提交事件
    /// </summary>
    private void submitClick()
    {
        //遍历当前题目的选项,有选择的就可以提交核验答案,并显示解析内容
        foreach (var item in options)
        {
            if (item.thisToggle.isOn)
            {
                rightAnswer = "";
                for (int i = 0; i < mQuestionData.answerData.Count; i++)
                {
                    switch (i)
                    {
                        case 0:
                            if (mQuestionData.answerData[i].Score>0)//xml文档里面score属性对应的数值是用来判断该选项是否为正确选项
                            {
                                rightAnswer += "A";
                            }
                            break;
                        case 1:
                            if (mQuestionData.answerData[i].Score > 0)
                            {
                                rightAnswer += "B";
                            }
                            break;
                        case 2:
                            if (mQuestionData.answerData[i].Score > 0)
                            {
                                rightAnswer += "C";
                            }
                            break;
                        case 3:
                            if (mQuestionData.answerData[i].Score > 0)
                            {
                                rightAnswer += "D";
                            }
                            break;
                    }
                }
                analysisData.text = String.Format("正确选项是:{0}\n解析:{1}", rightAnswer, mQuestionData.Analysis);
            }
        }
    }
}
/// <summary>
/// 答题界面数据类
/// </summary>
public class QuestionPanel
{
    public static  List<QuestionPanelData> questionPanelData;
    public QuestionPanel(XmlNode node)
    {
        questionPanelData = new List<QuestionPanelData>();
        for (int i = 0; i < node.ChildNodes.Count; i++)
        {
            questionPanelData.Add(new QuestionPanelData(node.ChildNodes[i]));
        }
    }
}
/// <summary>
/// 答题界面数据类
/// </summary>
public class QuestionPanelData
{
    public  List<QuestionData> questionData;
    public QuestionPanelData(XmlNode node)
    {
        questionData = new List<QuestionData>();
        for (int i = 0; i < node.ChildNodes.Count; i++)
        {
            questionData.Add(new QuestionData(node.ChildNodes[i]));
        }
    }
}
/// <summary>
/// 题目数据类
/// </summary>
public class QuestionData
{
    /// <summary>
    /// 是否为单选,true为单选,false为多选
    /// </summary>
    public bool isSingleChoice;
    /// <summary>
    /// 解析内容
    /// </summary>
    public string Analysis;
    /// <summary>
    /// 题目内容
    /// </summary>
    public string problem;
    public List<AnswerData> answerData;
    public QuestionData(XmlNode node)
    {
        isSingleChoice = bool.Parse(node.Attributes["SelectType"].InnerText);
        Analysis = node["Analysis"].InnerText;
        problem = node["Problem"].InnerText;
        answerData = new List<AnswerData>();
        XmlNodeList nodelist = node["Answer"].ChildNodes;
        for (int i = 0; i < nodelist.Count; i++)
        {
            answerData.Add(new AnswerData(nodelist[i]));
        }
    }
}
/// <summary>
/// 答案数据类
/// </summary>
public class AnswerData
{
    /// <summary>
    /// 选项的内容
    /// </summary>
    public string option;
    /// <summary>
    /// 选项对应的分数
    /// </summary>
    public int Score;
    public AnswerData(XmlNode node)
    {
        option = node.Attributes["option"].InnerText;
        Score = int.Parse(node.InnerText);
    }

}

DataPath.cs全局静态类:
在这里插入图片描述

Options.cs脚本挂载的选项预制体上,管理每一个选项的数据
public class Options : MonoBehaviour
{
    /// <summary>
    /// 当前选项组件
    /// </summary>
    public Toggle thisToggle;
    /// <summary>
    /// 选项的内容文本
    /// </summary>
    public Text optionText;
    /// <summary>
    /// 选项对应的分数
    /// </summary>
    public int score;
    /// <summary>
    /// 选项的状态
    /// </summary>
    public bool IsSelect=false;

    public void Init(AnswerData answerData)
    {
        optionText.text = answerData.option;
        score = answerData.Score;
        thisToggle.onValueChanged.AddListener((isSelect) => { IsSelect = isSelect; });
    }
}

答题系统demo演示

单选:
在这里插入图片描述
多选:
在这里插入图片描述
选错:
在这里插入图片描述
这个答题系统满足了基本的单选多选需求,后期可以考虑完善其他功能…

额外的知识

demo用到的Application.dataPath指的是程序的数据文件所在的文件夹路径,这里是因为在PC平台使用www类加载资源,所以加载路径要使用file://协议实现加载,否则会加载失败。平台不一样,路径也不一样。
执行以下代码:
在这里插入图片描述

① Yield return null,在此表示暂缓一帧,在下一帧才执行后面的代码,等同于yield return +任意数字,yield return www表示等待资源加载完成,再执行后面的代码,等同于www.isDone.
② using语句在此处的作用是,使用了www类的实例,结束的时候自动调用Dispose处理实例对象。即将其释放掉。
③ Unity里加载xml文件的一般流程是:引入System.Xml命名空间,写加载解析的函数,函数里先创建xml文档的对象:XmlDocument doc=new XmlDocument();,再通过调用XmlNodeList、XmlNode、XmlElement等类的属性和方法去遍历查找并读取相关的节点数据。
④ Xml类常用的一些类和方法:
在这里插入图片描述
在这里插入图片描述

其中Load()方法是指可以从 一个指定的文件中解析内容,而LoadXml()则是加载有xml格式字符串到DOM对象中。

  • 32
    点赞
  • 238
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 57
    评论
以下是一个简单的Unity答题系统开发的步骤: 1.创建题目和答案的数据结构,可以使用XML文件或者其他数据存储方式。 2.创建一个答题界面,包括题目、选项、倒计时等元素。 3.创建一个计时器,用于倒计时并在时间结束后关闭答题界面。 4.创建一个按钮组件,用于选择题目。 5.创建一个按钮点击事件,用于检查答案是否正确并更新得分。 6.创建一个结束界面,显示得分和其他统计信息。 7.将所有组件和事件连接起来,以实现完整的答题系统。 以下是一个简单的Unity答题系统的代码示例: ```csharp using System.Collections;using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Question { public string question; public List<string> options; public int answer; } public class QuizManager : MonoBehaviour { public Text questionText; public List<Button> optionButtons; public Text scoreText; public Text timeText; public GameObject quizPanel; public GameObject resultPanel; private List<Question> questions; private int currentQuestionIndex; private int score; private float timeLeft; private bool answered; void Start() { LoadQuestions(); StartQuiz(); } void Update() { if (timeLeft > 0) { timeLeft -= Time.deltaTime; timeText.text = Mathf.RoundToInt(timeLeft).ToString(); } else if (!answered) { EndQuiz(); } } void LoadQuestions() { // Load questions from XML file or other data source // ... } void StartQuiz() { currentQuestionIndex = 0; score = 0; timeLeft = 60; answered = false; quizPanel.SetActive(true); resultPanel.SetActive(false); ShowQuestion(); } void EndQuiz() { quizPanel.SetActive(false); resultPanel.SetActive(true); scoreText.text = "Score: " + score.ToString(); } void ShowQuestion() { Question question = questions[currentQuestionIndex]; questionText.text = question.question; for (int i = 0; i < optionButtons.Count; i++) { optionButtons[i].GetComponentInChildren<Text>().text = question.options[i]; } } public void AnswerQuestion(int answer) { if (!answered) { Question question = questions[currentQuestionIndex]; if (answer == question.answer) { score++; } currentQuestionIndex++; if (currentQuestionIndex < questions.Count) { ShowQuestion(); } else { answered = true; EndQuiz(); } } } public void SelectQuestion(int index) { if (!answered) { currentQuestionIndex = index; ShowQuestion(); } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 57
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周周的Unity小屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值