Unity实现可移植答题模块的简单实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_17367039/article/details/102936483
Unity实现可移植答题模块的简单实现
前言
在最近的几天里,领导交代开发一个答题功能的模块,需要很方便地移植到其他项目中。因为我之前开发过答题功能的项目,所以对这个需求也不虚,但是开发这种类似于插件的功能,我还是没有搞过的。于是我翻了翻之前的项目,看了看代码,思考了一下,感觉还是可以搞出来的,于是研究了两天,将这个可以方便移植的答题模块搞了出来,先看下效果图。
效果图1:

效果图2:

效果图3:

效果图4:

步骤一
在场景中创建一个Scroll View组件,在其子物体Content里添加两个组件,Vertical Layout Group和Content Size Fitter,如下图所示:

步骤二
在Content下创建TopicSelect物体,在下面添加几种UI组件,将其做成Prafab,结构如图所示:

步骤三
添加TopicSelectScr脚本:代码如下图所示:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
//using SUIFW;
/// <summary>
/// 一道选择题
/// </summary>
public class TopicSelectScr : MonoBehaviour
{
/// <summary>
/// 自身的考试题 需要赋值的数据
/// </summary>
public ExaminationDate examination;
/// <summary>
/// 是否选择正确,通过属性判断成绩
/// </summary>
public bool isCorrect;
//查找组件
public Text IdTxt;
public Text SubjectTxt;
public Toggle Toggle_A;
public Text Toggle_A_Label;
public Toggle Toggle_B;
public Text Toggle_B_Label;
public Toggle Toggle_C;
public Text Toggle_C_Label;
public Toggle Toggle_D;
public Text Toggle_D_Label;
public int topicId;
public Text answerText;

GameObject rootGo;

public GameObject[] toggleBackGround;

private void Awake()
{
    Toggle_A.isOn = false;
    Toggle_B.isOn = false;
    Toggle_C.isOn = false;
    Toggle_D.isOn = false;

}

/// <summary>
/// 赋值完数据之后,需要更新UI的显示,并提供题号的id
/// </summary>
/// <param name="id"></param>
public void OnChangeUIShow(int id,int FontSize=28)
{
    topicId = id;
    IdTxt.text = id.ToString();
    SubjectTxt.text = examination.Topic;
    Toggle_A_Label.text = examination.Select[0];
    Toggle_A_Label.fontSize = FontSize;
    Toggle_B_Label.text = examination.Select[1];
    Toggle_B_Label.fontSize = FontSize;
    Toggle_C_Label.text = examination.Select[2];
    Toggle_C_Label.fontSize = FontSize;
    Toggle_D_Label.text = examination.Select[3];
    Toggle_D_Label.fontSize = FontSize;
    answerText.text = examination.Analysis;
    Toggle_A.onValueChanged.AddListener(TogSelectA);
    Toggle_B.onValueChanged.AddListener(TogSelectB);
    Toggle_C.onValueChanged.AddListener(TogSelectA);
    Toggle_D.onValueChanged.AddListener(TogSelectB);
}
/// <summary>
/// 选择1
/// </summary>
/// <param name="ison"></param>
void TogSelectA(bool ison)
{
    int id = 0;//该选择的判断标识
    int res;
    if (ison)
    {
        if (int.TryParse(examination.Answer, out res))//将json的正确答案对比
        {
            if (id == res)//判断否正确
            {
                isCorrect = true;
            }
            else
            {
                isCorrect = false;
            }
        }
    }
    else
    {
        isCorrect = false;
    }
}
/// <summary>
/// 选择2
/// </summary>
/// <param name="ison"></param>
void TogSelectB(bool ison)
{
    int id = 1;
    int res;
    if (ison)
    {
        if (int.TryParse(examination.Answer, out res))
        {
            if (id == res)
            {
                isCorrect = true;
            }
            else
            {
                isCorrect = false;
            }
        }
    }
    else
    {
        isCorrect = false;
    }
}
/// <summary>
/// 选择3
/// </summary>
/// <param name="ison"></param>
void TogSelectC(bool ison)
{
    int id = 2;
    int res;
    if (ison)
    {
        if (int.TryParse(examination.Answer, out res))
        {
            if (id == res)
            {
                isCorrect = true;
            }
            else
            {
                isCorrect = false;
            }
        }
    }
    else
    {
        isCorrect = false;
    }
}
/// <summary>
/// 选择4
/// </summary>
/// <param name="ison"></param>
void TogSelectD(bool ison)
{
    int id = 3;
    int res;
    if (ison)
    {
        if (int.TryParse(examination.Answer, out res))
        {
            if (id == res)
            {
                isCorrect = true;
            }
            else
            {
                isCorrect = false;
            }
        }
    }
    else
    {
        isCorrect = false;
    }
}

/// <summary>
/// 隐藏勾选对象
/// </summary>
internal void ToHideToggle()
{
    for (int i = 0;i<toggleBackGround.Length;i++)
    {
        toggleBackGround[i].SetActive(false);
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
步骤四
将TopicSelectScr脚本挂载到TopicSelect物体上,将里面组件拖到脚本中,制作完预制体后在场景中将其删除,物体上的组件如下图所示:

步骤五
在场景中建立StreamingAssets文件夹,在其中创建一个JsonTest.json文件,用文本编辑器打开,里面数据如图所示:

步骤六
开始解析json功能的开发,首先创建ExaminationDate.CS脚本,脚本代码如下所示:

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

[Serializable]
public class ExaminationDate
{
//题目
public string Topic;
//答案选择
public string[] Select;
//正确的id
public string Answer;
//答案解析
public string Analysis;

public ExaminationDate()
{
    Select = new string[4];
}

public override string ToString()
{
    return Topic + " " + Select[0] + " " + Select[1] + " " + Select[2] + " " + Select[3] + " " + Answer + Analysis;
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
步骤七
json解析以及题目展示、判断核心代码如下所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
using System.Text.RegularExpressions;
using System;
using System.IO;
using LitJson;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class ExamTestScr : MonoBehaviour
{
//解析芯片之后得到所有的考题信息
List<ExaminationDate> examList = new List<ExaminationDate>();
//错误题的集合信息
public List<ExaminationDate> WrongList = new List<ExaminationDate>();
//考题的预设体
public TopicSelectScr topicSelectFormPro;
public RectTransform contentParent;//考试题预设体的父物体
float t_Height = 0;//获取单个预设体UI的高度
//临时保存考题的实例化集合
List<TopicSelectScr> topicSelectS = new List<TopicSelectScr>();
//错题的标记id
public List<TopicSelectScr> topicMarkS = new List<TopicSelectScr>();
int TopicNum = 0;
//核对几道正确的题
public float index = 0;
//错题的标记id 临时保存
public List<TopicSelectScr> TopicMarks = new List<TopicSelectScr>();
//public static string Examination = "http://www.techvew.com/Data/Examination/Examination.json";
public string Examination;
//每题的分数
int thisScore = 0;
//展示分数的Panel
public GameObject showScorePanel;
//展示分数的Text组件
public Text showScoreText;
//文本管理类
ConfigTest configTest;
//场景名称
string scenceName = "";
//计算分数按钮
public GameObject countButton;
//进入实验按钮
public GameObject intoGameButton;
//答题父物体
public GameObject content;

// Use this for initialization
void Start ()
{
    Examination = Application.streamingAssetsPath + "/JsonTest.json";
    BeginString();
    StartCoroutine(ToAnalysisExamOKInfo());
}

/// <summary>
/// 加载物体
/// </summary>
void BeginString()
{
    configTest = GameObject.Find("configTest").GetComponent<ConfigTest>();
    StartCoroutine(ToBeginString());
}
/// <summary>
/// 初始化字符串
/// </summary>
/// <returns></returns>
IEnumerator ToBeginString()
{
    yield return new WaitForSeconds(0.1f);
    TopicNum = int.Parse(configTest.dic["题数"]["TiNumber"]);
    scenceName = configTest.dic["场景名称"]["ScenceName"];
    thisScore = int.Parse(configTest.dic["每题分数"]["thisScore"]);
}

/// <summary>
/// 延时执行解析题目
/// </summary>
/// <returns></returns>
IEnumerator ToAnalysisExamOKInfo()
{
    yield return new WaitForSeconds(0.2f);
    AnalysisExamOKInfo();
}

/// <summary>
/// 解析考试题
/// </summary>
void AnalysisExamOKInfo()
{
    StartCoroutine(OnWwwDoenLoad(Examination));
}
/// <summary>
/// 实例化一定数量的考试题
/// </summary>
/// <param name="TopicNum">考题的数量</param>
public void InfoExamTopic(float TopicNum)
{
    //集合数据清除
    if (topicSelectS.Count > 0)//如果有原始的题号
    {
        for (int i = 0;i < topicSelectS.Count;i++)
        {
            Destroy(topicSelectS[i].gameObject);
        }
    }
    if (examList.Count <= 0)
    {
        Debug.Log("解析失败,数据个数为0");
        return;
    }
    //动态设置父物体的高度
    contentParent.sizeDelta = new Vector2(contentParent.rect.width,TopicNum * t_Height);
    //实例化一定数量的考题
    if (TopicNum > 0)//给定的数值必须大于0
    {
        int index = UnityEngine.Random.Range(0,examList.Count - (int)TopicNum);
        topicSelectS.Clear();
        for (int i = 0;i < TopicNum;i++)
        {
            TopicSelectScr Select = Instantiate(topicSelectFormPro);
            topicSelectS.Add(Select);//添加到集合中,方便审核成绩
            Select.transform.SetParent(contentParent);
            Select.gameObject.transform.localScale = Vector3.one;
            Select.examination = examList[index++];
            Select.OnChangeUIShow(i + 1,30);
        }
    }
}
/// <summary>
/// 接在网络路径的json 解析
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
IEnumerator OnWwwDoenLoad(string url)
{
    WWW www = new WWW(url);
    yield return www;
    if (!string.IsNullOrEmpty(www.text))
    {
        string ExamContent = www.text;
        //Debug.Log(ExamContent);
        examList = JsonMapper.ToObject<List<ExaminationDate>>(ExamContent);
        //print("A" + examList.Count);
    }
    //解析数据之后
    InfoExamTopic(TopicNum);
}
/// <summary>
/// 提交按钮,统计成绩
/// </summary>
public void SubmitInfo()
{
    index = 0;//重置正确的数量
    topicMarkS.Clear();//清空错题id
    //计算正确的题号
    for (int i = 0;i < topicSelectS.Count;i++)
    {
        if (topicSelectS[i].isCorrect == true)//得到正确的数量
        {
            index++;
            topicSelectS[i].gameObject.SetActive(false);
        }
        else
        {
            topicMarkS.Add(topicSelectS[i]);//捕获错误的题号id
            topicSelectS[i].answerText.gameObject.SetActive(true);
            topicSelectS[i].ToHideToggle();
        }
    }
    StartCoroutine(ToCountScore());
}
/// <summary>
/// 计算分数的协程
/// </summary>
/// <returns></returns>
IEnumerator ToCountScore()
{
    yield return new WaitForSeconds(0.1f);
    thisScore = (int)index * 10;
    showScorePanel.SetActive(true);
    //Debug.Log("您的分数为:" + thisScore.ToString());
    showScoreText.text = "您的分数为:" + thisScore.ToString();
}

/// <summary>
/// 开始试验
/// </summary>
public void ToBeginGame()
{
    SceneManager.LoadScene(scenceName);
}

/// <summary>
/// 显示错题
/// </summary>
public void ToShowWrong()
{
    showScorePanel.SetActive(false);
    countButton.SetActive(false);
    intoGameButton.SetActive(true);
    content.transform.localPosition = new Vector3(0f,0f); 
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
步骤八
将此脚本挂载到daTiPanel上,如下图所示:

步骤九
为了便于一直,我在这个工程中添加了本地配置文件的功能,通过修改本地配置文件的参数可以修改生成题的数量和每道题的分数,在StreamingsAssets文件夹下添加Config.txt文件,里面参数设置如下所示:

步骤十
添加ConfigTest脚本,挂载到场景的物体上,代码如下图所示:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

/// <summary>
/// 配置文件读取
/// </summary>
public class ConfigTest : MonoBehaviour
{
private string configPath;

public Dictionary<string, Dictionary<string, string>> dic;

private void Awake()
{
    //读取配置文件(StreamingAssets)路径
    configPath = Path.Combine(Application.streamingAssetsPath,"Config.txt");
    if (dic == null)
    {
        dic = new Dictionary<string, Dictionary<string, string>>();
        LoadConfig();
    }
}

/// <summary>
/// 读取所有的数据
/// </summary>
void LoadConfig()
{
    string[] lines = null;
    if (File.Exists(configPath))
    {
        lines = File.ReadAllLines(configPath);
        BuildDic(lines);
    }
}
/// <summary>
/// 处理所有的数据
/// </summary>
/// <param name="lines"></param>
private void BuildDic(string[] lines)
{
    string mainKey = null;//主键
    string subKey = null;//子键
    string subValue = null;//值
    foreach (var item in lines)
    {
        string line = null;
        line = item.Trim();//去除空白行
        if (!string.IsNullOrEmpty(line))
        {
            if (line.StartsWith("["))//取主键
            {
                mainKey = line.Substring(1, line.IndexOf("]") - 1);
                if (dic.ContainsKey(mainKey))
                {
                    return;
                }
                dic.Add(mainKey, new Dictionary<string, string>());
            }
            else//取主键
            {
                var configValue = line.Split(new char[] { '='},StringSplitOptions.RemoveEmptyEntries);
                subKey = configValue[0].Trim();
                subValue = configValue[1].Trim();
                subValue = subValue.StartsWith("\"") ? subValue.Substring(1) : subValue;
                dic[mainKey].Add(subKey,subValue);
            }
        }
    }
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
步骤十一
在ExamTestScr.cs中脚本调用配置文件脚本里的方法,如下图所示:

总结
通过以上的步骤,基本实现了可移植答题模块功能的实现,具体想增加什么功能还需要在以后的项目中慢慢添加吧!
————————————————
版权声明:本文为CSDN博主「波波斯维奇」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_17367039/article/details/102936483

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容