unity制作答题系统

一:前言

首先要感谢周周的Unity小屋大佬,原文链接如下

Unity答题系统_unity 答题系统-CSDN博客

在此基础上增加了一些功能,使其比较完善

二:功能说明

通过xml文件写入问题答案和解析,点击开始答题进入答题界面,在规定时间内答题,倒计时结束,自动关闭答题界面,显示结束界面和得分,倒计时未结束,可以答题,答过的题不可二次修改,点击下方的小点按钮,选择对应的题目,题目选择完毕后,下方按钮也有对应标识,不可再次点击。

程序运行如下:

三:UI界面介绍

1.开始界面

2.答题界面

答题界面与周周的Unity小屋布置的框架一样,注意预制体,xml文件的位置等。添加了得分,倒计时等。

3.结束界面

4.提示界面

四:代码书写

  1. 选项预制体代码Options


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

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

这个脚本挂载上option预制体上,吧对应的内容拖到对应位置即可。

  1. Panel_Question、ButtonItem、DataPath


using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.UI;

public class Panel_Question : MonoBehaviour
{

    [Header("root界面:开始界面的root 答题界面的root 结束界面的root  提示界面的root")]
    [SerializeField] GameObject startRoot;
    [SerializeField] GameObject answerRoot;
    [SerializeField] GameObject endRoot;
    [SerializeField] GameObject hintRoot;

    [Header("按钮:开始答题 上一题,提交,下一题")]
    [SerializeField] Button startBtn;
    [SerializeField] Button previousBtn;
    [SerializeField] Button submitBtn;
    [SerializeField] Button nextBtn;
    [SerializeField] Button backToMainBtn;

    [Header("按钮:确认提交  取消提交")]
    [SerializeField] Button affirmSubBtn;
    [SerializeField] Button cancelSubBtn;

    [Header("文本:题目序号 解析内容 得分 倒计时  最后得分")]
    [SerializeField] Text questionID;
    [SerializeField] Text analysisData;
    [SerializeField] Text scoreTxt;
    [SerializeField] Text countDownTxt;
    [SerializeField] Text endTxt;

    [Header("内容scroll的content 单选scroll的content 选项scroll的content")]
    [SerializeField] Transform contentScrollContent;
    [SerializeField] Transform questionBtnRoot;
    [SerializeField] Transform selectContent;
    [SerializeField] Transform scrollView;

    [SerializeField] ToggleGroup questionGroup;

    // 答题界面数据内容
    private QuestionPanelData mQuestionPanelData;
    // 每一道题的题目内容
    private QuestionData mQuestionData;
    // 题目内容物体
    private GameObject mQuestion;
    // 选项的链表
    private List<Options> options = new List<Options>();

    //倒计时的总时间
    public float secound ;

    [SerializeField] GameObject prefab;

    static Panel_Question instance;

    public static Panel_Question GetInstance()
    {
        return instance;
    }

    private void Awake()
    {
        Init();
        instance = this;
    }

    /// <summary>
    /// 按钮监听
    /// </summary>
    private void Init()
    {
        startBtn.onClick.AddListener(StartAnswer);
        previousBtn.onClick.AddListener(previousClick);
        submitBtn.onClick.AddListener(submitClick);
        nextBtn.onClick.AddListener(nextClick);
        backToMainBtn.onClick.AddListener(BackToMain);
        affirmSubBtn.onClick.AddListener(AffirmSub);
        cancelSubBtn.onClick.AddListener(CancelSub);
    }

    private void Start()
    {
        //读取xml文件
        StartCoroutine(LoadingQuesiton(DataPath.QuestionData));

        scoreTxt.text = "得分: 0";
        countDownTxt.text = "答题时间还剩:" + secound;
        //界面的显示与隐藏
        startRoot.SetActive(true);
        answerRoot.SetActive(false);
        endRoot.SetActive(false);
        hintRoot.SetActive(false);
        
    }

    private void Update()
    {
        if (endRoot.activeSelf)
        {
            //如果倒计时结束前答题完成,关闭倒计时的协程,时间重新赋值
            StopCoroutine(TimeChange());
            secound = 0;
        }
    }

    #region 读取xml文件
    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);
        }
    }
    #endregion

    #region 初始化第一道题
    public void InitQuestionPanel(QuestionPanelData questionPanelData)
    {
        //this.gameObject.SetActive(true);
        mQuestionPanelData = questionPanelData;
        CreateQuestion(questionPanelData.questionData[index]);
    }
    #endregion

    #region 创建题目
    bool isFirst = false;
    public void CreateQuestion(QuestionData questionData)
    {
        //数据赋值
        analysisData.text = "";
        mQuestionData = questionData;
        questionID.text = string.Format("第{0}题(共" + mQuestionPanelData.questionData.Count + "题)", index + 1);
        if (mQuestion != null)
        {
            Destroy(mQuestion);
        }

        //实例化题目预制体
        mQuestion = Instantiate(Resources.Load<GameObject>(DataPath.QuestionText));
        mQuestion.transform.SetParent(contentScrollContent);
        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>();

        //实例化按钮选项组序列
        if (!isFirst)
            for (int i = 0; i < mQuestionPanelData.questionData.Count; i++)
            {
               Instantiate(prefab, scrollView);
                isFirst = true;
            }
        
        //当前题目的按钮序列的标识变大
        for (int i = 0; i < mQuestionPanelData.questionData.Count; i++)
        {
            if (i!= index)
                scrollView.GetChild(i).gameObject.GetComponent<RectTransform>().localScale = new Vector2(1, 1);
            else
                scrollView.GetChild(i).gameObject.GetComponent<RectTransform>().localScale = new Vector2(1.5f, 1.5f);

        }

        //实例化选项预制体
        for (int i = 0; i < questionData.answerData.Count; i++)
        {
            Options option = Instantiate(Resources.Load<Options>("Options"));
            option.Init(questionData.answerData[i]);
            option.transform.SetParent(selectContent);
            //如果是单选则设置为一个toggle组
            if (questionData.isSingleChoice)
            {
                option.thisToggle.group = questionGroup;
            }
            options.Add(option);
        }
    }
    #endregion

    #region 开始答题 上一题 下一题  提交按钮事件

    // 开始答题
    public void StartAnswer()
    {
        for (int i = 0; i < QuestionPanel.questionPanelData.Count; i++)
        {
            InitQuestionPanel(QuestionPanel.questionPanelData[i]);
        }
        startRoot.SetActive(false);
        answerRoot.SetActive(true);
        secound = 30;
        //开启倒计时
        StartCoroutine(TimeChange());
    }

    // 上一题点击事件
    private void previousClick()
    {
        if (index > 0)
        {
            index--;
            CreateQuestion(mQuestionPanelData.questionData[index]);
        }

        if (scrollView.GetChild(index).GetComponent<Button>().interactable == false)
            foreach (var item in options)
                item.thisToggle.interactable = false;

    }

    // 下一题点击事件
    private void nextClick()
    {
        if (index < mQuestionPanelData.questionData.Count - 1)
        {
            index++;
            CreateQuestion(mQuestionPanelData.questionData[index]);
            isFirstClick = false;
        }

        if (scrollView.GetChild(index).GetComponent<Button>().interactable == false)
            foreach (var item in options)
                item.thisToggle.interactable = false;
    }

    int score = 0;
    bool isFirstClick = false;      //是否是第一次答对,只有点击第一次提交的时候加分,再点提交不加分
    // 题目提交事件
    private void submitClick()
    {
        //遍历当前题目的选项,有选择的就可以提交核验答案,并显示解析内容
        foreach (var item in options)
        {
            if (item.thisToggle.isOn)
            {
                //回答正确加一分
                if (item.score > 0)
                {
                    analysisData.text = "回答正确";
                    if (!isFirstClick)
                    {
                        score += 1;
                        isFirstClick = true;
                    }
                    scoreTxt.text = "得分:" + score.ToString();
                }
                else
                    analysisData.text = "回答错误,解析:" + mQuestionData.Analysis;
            }
            //选择一个选项之后不能在选择其他选项
            item.thisToggle.interactable = false;
        }

        //如果当前题目是最后一题,提交之后,出现提示界面
        if (index + 1 == mQuestionPanelData.questionData.Count)
        {
            startRoot.SetActive(false);
            answerRoot.SetActive(true);
            endRoot.SetActive(false);
            hintRoot.SetActive(true);
        }

        //提交后,该题目不可再选择修改
        scrollView.GetChild(index).GetComponent<Image>().color = Color.green;
        scrollView.GetChild(index).GetComponent<Button>().interactable = false;

    }

    //返回主界面
    void BackToMain()
    {
        startRoot.SetActive(true);
        answerRoot.SetActive(false);
        endRoot.SetActive(false);
        score = 0;
        scoreTxt.text = "得分:" + score.ToString();

        for (int i = 0; i < scrollView.childCount; i++)
        {
            scrollView.GetChild(i).GetComponent<Image>().color = Color.white;
            scrollView.GetChild(i).GetComponent<Button>().interactable = true;
            Debug.Log(456789);
        }

        index = 0;
    }

    int index = 0;
    //缩列按钮事件
    public void ThisBtn(ButtonItem item)
    {
        for (int i = 0; i < scrollView.childCount; i++)
        {
            CreateQuestion(mQuestionPanelData.questionData[item.transform.GetSiblingIndex()]);
            index = item.transform.GetSiblingIndex();
        }
    }

    //确认提交 显示结束界面
    void AffirmSub()
    {
        startRoot.SetActive(false);
        answerRoot.SetActive(false);
        endRoot.SetActive(true);
        hintRoot.SetActive(false);
        endTxt.text = "答题结束\n 您的得分是:" + score.ToString();
    }

    //取消提交 返回原来的界面
    void CancelSub()
    {
        startRoot.SetActive(false);
        answerRoot.SetActive(true);
        endRoot.SetActive(false);
        hintRoot.SetActive(false);
    }
    #endregion

    #region 倒计时的协程
    IEnumerator TimeChange()
    {
        while (secound > 0)
        {
            yield return new WaitForSeconds(1);
            countDownTxt.text = "答题时间还剩:"+secound.ToString()+"秒";
            secound--;
        }
        if (secound<=0)
        {
            //界面的显示与隐藏
            startRoot.SetActive(false);
            answerRoot.SetActive(false);
            endRoot.SetActive(true);
            endTxt.text = "答题结束\n 您的得分是:" + score.ToString();
        }
    }
    #endregion

}

/// <summary>
/// 答题panel数据类
/// </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
{
    // 是否为单选,true为单选,false为多选
    public bool isSingleChoice;
    // 解析内容
    public string Analysis;
    // 题目内容
    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
{
    // 选项的内容
    public string option;
    // 选项对应的分数
    public int Score;
    public AnswerData(XmlNode node)
    {
        option = node.Attributes["option"].InnerText;
        Score = int.Parse(node.InnerText);
    }
}

这个代码挂在canvas上,其中代码中的命名与场景中的命名一致,将对应物体添加到对应位置上,其中ButtonItem是挂载上image上的,image是一个预制体,内容如下。

ButtonItem代码如下:


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class ButtonItem : MonoBehaviour
{
    Button Btn;


    // Start is called before the first frame update
    void Start()
    {
        Btn = GetComponent<Button>();
        Btn.onClick.AddListener(ThisBtn);
    }

    public void ThisBtn()
    {
        Panel_Question.GetInstance().ThisBtn(this);
    }

}

其中,用www读取xml文件时,会报错,是xml格式有问题,我在读取时报错是XmlException: Data at the root level is invalid. Line 1, position 1.通过代码重新生成一个xml文件,再在其中添加想写的内容即可。我生成XmlTest的代码如下。有需要的可以参考


using UnityEngine;
using System.IO;
using System.Xml;
public class XmlTest : MonoBehaviour
{
    // Use this for initialization
    void Start()
    {
        CreateXml();
    }
    /// 
    /// Creates the xml.
    /// 
    private void CreateXml()
    {
        //设置保存路径
        string path =  Application.dataPath + "/XML/" + "ConfigFile.xml";
        //判断文件是否存在
        if (File.Exists(path) == false)
        {
            //创建一个xml文件
            XmlDocument xml = new XmlDocument();
            //创建最上层节点
            XmlElement root = xml.CreateElement("Root");
            //创建子节点
            XmlElement element = xml.CreateElement("Question");
            element.SetAttribute("SelectType", "True");

            //创建子节点的第一个子节点,设置属性并添加内容
            XmlElement Child1 = xml.CreateElement("Problem");
            Child1.InnerText = "这里输入您的题目";

            //创建子节点的第二个子节点,设置属性并添加内容
            XmlElement Child2 = xml.CreateElement("Answer");

            //创建三级子节点
            XmlElement item1 = xml.CreateElement("Item");
            item1.SetAttribute("option", "A.答案一");
            item1.InnerText = "0";

            XmlElement item2 = xml.CreateElement("Item");
            item2.SetAttribute("option", "B.答案一");
            item2.InnerText = "0";

            XmlElement item3 = xml.CreateElement("Item");
            item3.SetAttribute("option", "C.答案一");
            item3.InnerText = "1";

            XmlElement item4 = xml.CreateElement("Item");
            item4.SetAttribute("option", "D.答案一");
            item4.InnerText = "0";

            //二级子节点
            XmlElement Child3 = xml.CreateElement("Analysis");
            Child3.InnerText = "这里输入解析";

            //创建子节点
            XmlElement element2 = xml.CreateElement("Question");
            element2.SetAttribute("SelectType", "True");

            //创建子节点的第一个子节点,设置属性并添加内容
            XmlElement Child2_1 = xml.CreateElement("Problem");
            Child2_1.InnerText = "这里输入您的题目gvfrebr";

            //创建子节点的第二个子节点,设置属性并添加内容
            XmlElement Child2_2= xml.CreateElement("Answer");

            //创建三级子节点
            XmlElement item2_1 = xml.CreateElement("Item");
            item2_1.SetAttribute("option", "A.答案一");
            item2_1.InnerText = "0";

            XmlElement item2_2 = xml.CreateElement("Item");
            item2_2.SetAttribute("option", "B.答案44");
            item2_2.InnerText = "0";

            XmlElement item2_3 = xml.CreateElement("Item");
            item2_3.SetAttribute("option", "C.答案3");
            item2_3.InnerText = "1";

            XmlElement item2_4 = xml.CreateElement("Item");
            item2_4.SetAttribute("option", "D.答案一");
            item2_4.InnerText = "0";

            //二级子节点
            XmlElement Child2_3 = xml.CreateElement("Analysis");
            Child2_3.InnerText = "这里输入解析";

            //把节点一层一层的添加至xml中,注意他们之间的先后顺序,这是生成XML文件的顺序
            element2.AppendChild(Child2_1);
            Child2_2.AppendChild(item2_1);
            Child2_2.AppendChild(item2_2);
            Child2_2.AppendChild(item2_3);
            Child2_2.AppendChild(item2_4);
            element2.AppendChild(Child2_2);
            element2.AppendChild(Child2_3);
            root.AppendChild(element2);

            element.AppendChild(Child1);
            Child2.AppendChild(item1);
            Child2.AppendChild(item2);
            Child2.AppendChild(item3);
            Child2.AppendChild(item4);
            element.AppendChild(Child2);
            element.AppendChild(Child3);
            root.AppendChild(element);
            xml.AppendChild(root);
            //保存XML文档
            xml.Save(path);
            Debug.Log("Xml 创建成功!");
        }
    }
}

也可以只生成两个root根节点,然后内容自己再xml中写,就不用通过代码添加了。

通过xml文件读取的数据的路径通过一个代码封装静态变量,全局使用,代码如下:DataPath


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 全局静态类,用来定义静态字段,方便调用
/// </summary>
public class DataPath
{
    public static string QuestionData = "file://" + Application.dataPath + "/XML/" + "ConfigFile.xml";
    public static string QuestionText = "QuestionText";
}

  • 19
    点赞
  • 159
    收藏
    觉得还不错? 一键收藏
  • 40
    评论
### 回答1: Unity2D 是一款非常流行的游戏引擎,可以用于开发各种类型的游戏,包括答题游戏。许多开发者需要一个答题系统源码来参考和学习,这里就向大家介绍一下 Unity2D 答题系统源码。 Unity2D 答题系统源码主要包括两个部分:界面设计和功能实现。界面设计包括题目展示、选项显示、计时器、得分等,这些都需要通过 Unity2D 引擎实现。功能实现涉及到游戏逻辑、数据管理、UI 交互等方面,需要开发者根据自己的需求进行设计和编写。 在使用 Unity2D 答题系统源码时,需要注意以下几点: 1. 熟悉 Unity2D 引擎,掌握常用的 2D 游戏开发技术。 2. 根据游戏规则和要求进行功能设计和实现,包括题目难度、题型、分数计算等。 3. 使用 C# 编程语言进行代码编写,包括游戏逻辑、UI 交互、数据管理等。 4. 界面设计需要考虑用户体验,如何让用户更方便、更快速地进行答题操作。 5. 确保代码的可扩展性和可维护性,方便后续的修改和维护工作。 总体来说,Unity2D 答题系统源码可以让开发者更快速地实现答题游戏功能,并且可以根据自己的需要进行修改和扩展。但是需要开发者具备一定的编程和设计技能,才能更好地使用 Unity2D 引擎进行游戏开发。 ### 回答2: Unity2D答题系统源码是一个基于Unity2D引擎开发的答题游戏源码。该源码可以帮助初学者了解和掌握Unity2D的开发技巧,同时也可以作为答题游戏的基础代码,方便开发者快速进行二次开发。 该源码包含了题目的制作、难度等级的设置、计分系统、倒计时等核心功能。玩家可以通过点击正确的答案来获得积分,同时还可以利用道具来增加自己的分数和答题时间。 在该源码的开发过程中,我们需要掌握Unity2D引擎的基本知识和编程技巧,比如物体的移动、碰撞检测、动画控制等。同时还需要了解答题游戏的设计原理和玩家习惯,才能开发出适合玩家的游戏。 总之,Unity2D答题系统源码是一个重要的学习资料,它可以帮助我们深入理解Unity2D引擎的开发技巧,同时也可以作为开发答题游戏的基础代码,提高游戏的开发效率和质量。 ### 回答3: Unity2D答题系统源码是一种基于Unity2D游戏引擎答题系统源代码。它主要提供一组可定制的答题功能,包括题目类型、难度等。这个系统可以用于各种类型的测试,比如知识测试、考试、调查、竞赛等。 这个答题系统源码提供了一个完整的框架,可以方便地修改和扩展。它包含了一个用户管理系统答题系统和结果分析系统。每个用户可以注册并登录,然后选择他们想参加的测试。用户可以选择一个或多个题目类型和难度,然后开始答题。测试结束后,用户可以看到自己的得分,并可以查看自己的答案和正确答案。管理员可以在后台管理用户和测试,查看测试结果和分析报告。 这个系统的优点是,它构建在Unity2D游戏引擎的基础之上,可扩展性强,可以适应不同的需求。它还具有用户友好的界面,可以吸引用户并提供良好的参与体验。此外,这个系统源码是免费提供的,任何人都可以使用和修改它,让它更好地满足自己的需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值