版权声明:本文为博主原创文章,遵循 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
网友评论