Unity中使用ScriptableObject允许我们创建配置文件并作为Unity中的Asset资产使用,无需像Json、XML等文件需要通过IO读取并反序列化成为我们需要的数据结构,其弊端是不可以像Json等配置文件可以在外部进行修改,在此不对其做详细介绍,下面使用ScriptableObject创作一个用于配置问答系统数据的资产,先看效果:
配置文件中包含五种题型:判断、单选、多选、填空、论述,选项可以删减并配置正确答案,首先定义一个描述题型的枚举QuestionType:
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 题型
/// </summary>
public enum QuestionType
{
/// <summary>
/// 判断题
/// </summary>
JUDGE,
/// <summary>
/// 单选题
/// </summary>
SINGLE_CHOICE,
/// <summary>
/// 多选题
/// </summary>
MULTIPLE_CHOICE,
/// <summary>
/// 填空题
/// </summary>
COMPLETION,
/// <summary>
/// 论述题
/// </summary>
ESSAY,
}
}
创建基类QuestionBase,定义公共字段,包括题号、问题、题型、题解:
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 问题基类
/// </summary>
public class QuestionBase
{
/// <summary>
/// 题号
/// </summary>
public int Sequence;
/// <summary>
/// 问题
/// </summary>
public string Question;
/// <summary>
/// 题型
/// </summary>
public QuestionType Type;
/// <summary>
/// 题解
/// </summary>
public string Analysis;
}
}
选项因题型不同而异,判断题只包含两个选项:正确与错误,答案为一个bool类型字段:
代码语言:javascript复制using System;
namespace SK.Framework
{
/// <summary>
/// 判断题
/// </summary>
[Serializable]
public class JudgeQuestion : QuestionBase
{
/// <summary>
/// 积极选项
/// </summary>
public string Positive = "正确";
/// <summary>
/// 消极选项
/// </summary>
public string Negative = "错误";
/// <summary>
/// 答案
/// </summary>
public bool Answer;
}
}
填空题需要一个string类型的列表存储所有答案:
代码语言:javascript复制using System;
using System.Collections.Generic;
namespace SK.Framework
{
/// <summary>
/// 填空题
/// </summary>
[Serializable]
public class CompletionQuestion : QuestionBase
{
/// <summary>
/// 答案
/// </summary>
public List<string> Answers = new List<string>(0);
}
}
论述题只需要一个string类型字段描述答案:
代码语言:javascript复制using System;
namespace SK.Framework
{
/// <summary>
/// 论述题
/// </summary>
[Serializable]
public class EssayQuestion : QuestionBase
{
/// <summary>
/// 答案
/// </summary>
public string Answer;
}
}
单选和多选题的选项分为三种不同类型:文字描述类型选项、图片类型选项、文字描述 图片类型选项,所以首先定义一个选项类型的枚举:
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 选项类型
/// </summary>
public enum ChoiceType
{
/// <summary>
/// 文本
/// </summary>
Text,
/// <summary>
/// 图片
/// </summary>
Pic,
/// <summary>
/// 文本 图片
/// </summary>
TextAndPic
}
}
单选题答案为一个int类型字段:
代码语言:javascript复制using System;
using System.Collections.Generic;
namespace SK.Framework
{
/// <summary>
/// 单项选择题
/// </summary>
[Serializable]
public class SingleChoiceQuestion : QuestionBase
{
/// <summary>
/// 选项类型
/// </summary>
public ChoiceType choiceType;
/// <summary>
/// 选项
/// </summary>
public List<QuestionChoice> Choices = new List<QuestionChoice>(0);
/// <summary>
/// 答案
/// </summary>
public int Answer;
}
}
多选题答案为一个int类型列表:
代码语言:javascript复制using System;
using System.Collections.Generic;
namespace SK.Framework
{
/// <summary>
/// 多项选择题
/// </summary>
[Serializable]
public class MultipleChoiceQuestion : QuestionBase
{
/// <summary>
/// 选项类型
/// </summary>
public ChoiceType choiceType;
/// <summary>
/// 选项
/// </summary>
public List<QuestionChoice> Choices = new List<QuestionChoice>(0);
/// <summary>
/// 答案
/// </summary>
public List<int> Answers = new List<int>(0);
}
}
需要注意各个问题类都需要添加Serializable特性,以确保其可序列化。单选和多选题的答案均为int类型字段,为了使int值对应A、B、C......Z字符,定义一个Alphabet字母表类, 例如Alphabet.Values[0]返回的便是字符A
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 英文字母表
/// </summary>
public static class Alphabet
{
public static char[] Values = new char[26]
{
'A','B','C','D','E','F','G','H','I','G','K','L','M',
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
};
}
}
有了上述类后,创建QuestionsProfile类,继承ScriptableObject,使用CreateAssetMenu为创建该类型资产提供了创建菜单,并创建QuestionsProfileInspector继承Editor类进行了编辑器拓展:
代码语言:javascript复制using UnityEngine;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace SK.Framework
{
[CreateAssetMenu(fileName = "New Questions Profile", menuName = "Question Profile")]
public sealed class QuestionsProfile : ScriptableObject
{
/// <summary>
/// 判断题列表
/// </summary>
public List<JudgeQuestion> Judges = new List<JudgeQuestion>(0);
/// <summary>
/// 单向选择题列表
/// </summary>
public List<SingleChoiceQuestion> SingleChoices = new List<SingleChoiceQuestion>(0);
/// <summary>
/// 多项选择题列表
/// </summary>
public List<MultipleChoiceQuestion> MultipleChoices = new List<MultipleChoiceQuestion>(0);
/// <summary>
/// 填空题列表
/// </summary>
public List<CompletionQuestion> Completions = new List<CompletionQuestion>(0);
/// <summary>
/// 论述题列表
/// </summary>
public List<EssayQuestion> Essays = new List<EssayQuestion>();
public T Get<T>(QuestionType type, int sequence) where T : QuestionBase
{
switch (type)
{
case QuestionType.JUDGE: return Judges.Find(m => m.Sequence == sequence) as T;
case QuestionType.SINGLE_CHOICE: return SingleChoices.Find(m => m.Sequence == sequence) as T;
case QuestionType.MULTIPLE_CHOICE: return MultipleChoices.Find(m => m.Sequence == sequence) as T;
case QuestionType.COMPLETION: return Completions.Find(m => m.Sequence == sequence) as T;
case QuestionType.ESSAY: return Essays.Find(m => m.Sequence == sequence) as T;
default: return null;
}
}
}
#if UNITY_EDITOR
[CustomEditor(typeof(QuestionsProfile))]
public sealed class QuestionsProfileInspector : Editor
{
private QuestionsProfile profile;
private QuestionType currentType;
private Color btnNormalColor = new Color(0.5f, 0.5f, 0.5f, 1f);
private readonly GUIContent deleteContent = new GUIContent("-", "delete");
private Vector2 scroll = Vector2.zero;
private Dictionary<JudgeQuestion, bool> judgeFoldoutMap;
private Dictionary<SingleChoiceQuestion, bool> singleChoicesFoldoutMap;
private Dictionary<MultipleChoiceQuestion, bool> multipleChoicesFoldoutMap;
private Dictionary<CompletionQuestion, bool> completionFoldoutMap;
private Dictionary<EssayQuestion, bool> essayFoldoutMap;
private void OnEnable()
{
profile = target as QuestionsProfile;
judgeFoldoutMap = new Dictionary<JudgeQuestion, bool>();
singleChoicesFoldoutMap = new Dictionary<SingleChoiceQuestion, bool>();
multipleChoicesFoldoutMap = new Dictionary<MultipleChoiceQuestion, bool>();
completionFoldoutMap = new Dictionary<CompletionQuestion, bool>();
essayFoldoutMap = new Dictionary<EssayQuestion, bool>();
}
public override void OnInspectorGUI()
{
OnTypeGUI();
OnMenuGUI();
OnDetailGUI();
if (GUI.changed)
{
EditorUtility.SetDirty(profile);
serializedObject.ApplyModifiedProperties();
}
}
private void OnTypeGUI()
{
EditorGUILayout.BeginHorizontal();
{
GUI.color = currentType == QuestionType.JUDGE ? Color.white : btnNormalColor;
if (GUILayout.Button("判断题", EditorStyles.miniButtonLeft)) currentType = QuestionType.JUDGE;
GUI.color = currentType == QuestionType.SINGLE_CHOICE ? Color.white : btnNormalColor;
if (GUILayout.Button("单选题", EditorStyles.miniButtonMid)) currentType = QuestionType.SINGLE_CHOICE;
GUI.color = currentType == QuestionType.MULTIPLE_CHOICE ? Color.white : btnNormalColor;
if (GUILayout.Button("多选题", EditorStyles.miniButtonMid)) currentType = QuestionType.MULTIPLE_CHOICE;
GUI.color = currentType == QuestionType.COMPLETION ? Color.white : btnNormalColor;
if (GUILayout.Button("填空题", EditorStyles.miniButtonMid)) currentType = QuestionType.COMPLETION;
GUI.color = currentType == QuestionType.ESSAY ? Color.white : btnNormalColor;
if (GUILayout.Button("论述题", EditorStyles.miniButtonRight)) currentType = QuestionType.ESSAY;
GUI.color = Color.white;
}
EditorGUILayout.EndHorizontal();
}
private void OnMenuGUI()
{
GUILayout.BeginHorizontal();
if (GUILayout.Button("展开", EditorStyles.miniButtonLeft))
{
switch (currentType)
{
case QuestionType.JUDGE: profile.Judges.ForEach(m => judgeFoldoutMap[m] = true); break;
case QuestionType.SINGLE_CHOICE: profile.SingleChoices.ForEach(m => singleChoicesFoldoutMap[m] = true); break;
case QuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.ForEach(m => multipleChoicesFoldoutMap[m] = true); break;
case QuestionType.COMPLETION: profile.Completions.ForEach(m => completionFoldoutMap[m] = true); break;
case QuestionType.ESSAY: profile.Essays.ForEach(m => essayFoldoutMap[m] = true); break;
default: break;
}
}
if (GUILayout.Button("收缩", EditorStyles.miniButtonMid))
{
switch (currentType)
{
case QuestionType.JUDGE: profile.Judges.ForEach(m => judgeFoldoutMap[m] = false); break;
case QuestionType.SINGLE_CHOICE: profile.SingleChoices.ForEach(m => singleChoicesFoldoutMap[m] = false); break;
case QuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.ForEach(m => multipleChoicesFoldoutMap[m] = false); break;
case QuestionType.COMPLETION: profile.Completions.ForEach(m => completionFoldoutMap[m] = false); break;
case QuestionType.ESSAY: profile.Essays.ForEach(m => essayFoldoutMap[m] = false); break;
default: break;
}
}
if (GUILayout.Button("添加", EditorStyles.miniButtonMid))
{
Undo.RecordObject(profile, "Add New");
switch (currentType)
{
case QuestionType.JUDGE: profile.Judges.Add(new JudgeQuestion()); break;
case QuestionType.SINGLE_CHOICE: profile.SingleChoices.Add(new SingleChoiceQuestion()); break;
case QuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.Add(new MultipleChoiceQuestion()); break;
case QuestionType.COMPLETION: profile.Completions.Add(new CompletionQuestion()); break;
case QuestionType.ESSAY: profile.Essays.Add(new EssayQuestion()); break;
default: break;
}
}
if (GUILayout.Button("清空", EditorStyles.miniButtonRight))
{
Undo.RecordObject(profile, "Clear");
if (EditorUtility.DisplayDialog("Prompt", "Are you sure clear the questions?", "Yes", "No"))
{
switch (currentType)
{
case QuestionType.JUDGE: profile.Judges.Clear(); judgeFoldoutMap.Clear(); break;
case QuestionType.SINGLE_CHOICE: profile.SingleChoices.Clear(); singleChoicesFoldoutMap.Clear(); break;
case QuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.Clear(); multipleChoicesFoldoutMap.Clear(); break;
case QuestionType.COMPLETION: profile.Completions.Clear(); completionFoldoutMap.Clear(); break;
case QuestionType.ESSAY: profile.Essays.Clear(); essayFoldoutMap.Clear(); break;
default: break;
}
}
}
GUILayout.EndHorizontal();
}
private void OnDetailGUI()
{
scroll = GUILayout.BeginScrollView(scroll);
switch (currentType)
{
#region 判断题
case QuestionType.JUDGE:
for (int i = 0; i < profile.Judges.Count; i )
{
var current = profile.Judges[i];
if (!judgeFoldoutMap.ContainsKey(current)) judgeFoldoutMap.Add(current, true);
GUILayout.BeginHorizontal("IN Title");
judgeFoldoutMap[current] = EditorGUILayout.Foldout(judgeFoldoutMap[current], $"第 {current.Sequence} 题", true);
if (GUILayout.Button("×", GUILayout.Width(20f)))
{
profile.Judges.Remove(current);
judgeFoldoutMap.Remove(current);
break;
}
GUILayout.EndHorizontal();
if (judgeFoldoutMap[current])
{
GUILayout.BeginHorizontal();
GUILayout.Label("题号:", GUILayout.Width(40));
var newValue = EditorGUILayout.IntField(current.Sequence, GUILayout.Width(30));
if (current.Sequence != newValue)
{
Undo.RecordObject(profile, "Judge Sequence");
current.Sequence = newValue;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("问题:", GUILayout.Width(40));
var newQ = EditorGUILayout.TextArea(current.Question, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newQ != current.Question)
{
Undo.RecordObject(profile, "Judge Question");
current.Question = newQ;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("正确:", GUILayout.Width(40));
var newP = EditorGUILayout.TextField(current.Positive);
if (newP != current.Positive)
{
Undo.RecordObject(profile, "Positive");
current.Positive = newP;
}
var newAnswer = EditorGUILayout.Toggle(current.Answer == true, GUILayout.Width(15));
if (newAnswer != (current.Answer == true))
{
Undo.RecordObject(profile, "Judge Answer");
current.Answer = true;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("错误:", GUILayout.Width(40));
var newN = EditorGUILayout.TextField(current.Negative);
if (newN != current.Positive)
{
Undo.RecordObject(profile, "Negative");
current.Negative = newN;
}
var newAns = EditorGUILayout.Toggle(current.Answer == false, GUILayout.Width(15));
if (newAns != (current.Answer == false))
{
Undo.RecordObject(profile, "Judge Answer");
current.Answer = false;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("题解:", GUILayout.Width(40));
var newA = EditorGUILayout.TextArea(current.Analysis, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newA != current.Analysis)
{
Undo.RecordObject(profile, "Judge Analysis");
current.Analysis = newA;
}
GUILayout.EndHorizontal();
}
}
break;
#endregion
#region 单选题
case QuestionType.SINGLE_CHOICE:
for (int i = 0; i < profile.SingleChoices.Count; i )
{
var current = profile.SingleChoices[i];
if (!singleChoicesFoldoutMap.ContainsKey(current)) singleChoicesFoldoutMap.Add(current, true);
GUILayout.BeginHorizontal("IN Title");
singleChoicesFoldoutMap[current] = EditorGUILayout.Foldout(singleChoicesFoldoutMap[current], $"第 {current.Sequence} 题", true);
if (GUILayout.Button("×", GUILayout.Width(20f)))
{
profile.SingleChoices.Remove(current);
singleChoicesFoldoutMap.Remove(current);
break;
}
GUILayout.EndHorizontal();
if (singleChoicesFoldoutMap[current])
{
GUILayout.BeginHorizontal();
GUILayout.Label("题号:", GUILayout.Width(40));
var newS = EditorGUILayout.IntField(current.Sequence, GUILayout.Width(30));
if (current.Sequence != newS)
{
Undo.RecordObject(profile, "SingleChoices Sequence");
current.Sequence = newS;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("问题:", GUILayout.Width(40));
var newQ = EditorGUILayout.TextArea(current.Question, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newQ != current.Question)
{
Undo.RecordObject(profile, "SingleChoices Question");
current.Question = newQ;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("选项:", GUILayout.Width(40));
GUILayout.BeginHorizontal();
if (GUILayout.Button("+", GUILayout.Width(25)))
{
Undo.RecordObject(profile, "SingleChoices Add");
current.Choices.Add(new QuestionChoice("选项描述", null));
}
GUILayout.FlexibleSpace();
current.choiceType = (ChoiceType)EditorGUILayout.EnumPopup(current.choiceType, GUILayout.Width(80f));
GUILayout.Label("(类型)");
GUILayout.EndHorizontal();
GUILayout.EndHorizontal();
GUILayout.BeginVertical();
for (int k = 0; k < current.Choices.Count; k )
{
GUILayout.BeginHorizontal();
GUILayout.Space(50);
GUILayout.Label($"{Alphabet.Values[k]}.", GUILayout.Width(20));
switch (current.choiceType)
{
case ChoiceType.Text:
current.Choices[k].text = GUILayout.TextField(current.Choices[k].text);
break;
case ChoiceType.Pic:
current.Choices[k].pic = EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false) as Sprite;
break;
case ChoiceType.TextAndPic:
current.Choices[k].text = GUILayout.TextField(current.Choices[k].text);
current.Choices[k].pic = EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false, GUILayout.Width(110f)) as Sprite;
break;
}
var newValue = EditorGUILayout.Toggle(current.Answer == k, GUILayout.Width(15));
if (newValue)
{
Undo.RecordObject(profile, "SingleChoices Answer");
current.Answer = k;
}
if (GUILayout.Button(deleteContent, "MiniButton", GUILayout.Width(18)))
{
Undo.RecordObject(profile, "Delete SingleChoice");
current.Choices.RemoveAt(k);
break;
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
GUILayout.BeginHorizontal();
GUILayout.Label("题解:", GUILayout.Width(40));
var newA = EditorGUILayout.TextArea(current.Analysis, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newA != current.Analysis)
{
Undo.RecordObject(profile, "SingleChoices Analysis");
current.Analysis = newA;
}
GUILayout.EndHorizontal();
}
}
break;
#endregion
#region 多选题
case QuestionType.MULTIPLE_CHOICE:
for (int i = 0; i < profile.MultipleChoices.Count; i )
{
var current = profile.MultipleChoices[i];
if (!multipleChoicesFoldoutMap.ContainsKey(current)) multipleChoicesFoldoutMap.Add(current, true);
GUILayout.BeginHorizontal("IN Title");
multipleChoicesFoldoutMap[current] = EditorGUILayout.Foldout(multipleChoicesFoldoutMap[current], $"第 {current.Sequence} 题", true);
if (GUILayout.Button("×", GUILayout.Width(20f)))
{
profile.MultipleChoices.Remove(current);
multipleChoicesFoldoutMap.Remove(current);
break;
}
GUILayout.EndHorizontal();
if (multipleChoicesFoldoutMap[current])
{
GUILayout.BeginHorizontal();
GUILayout.Label("题号:", GUILayout.Width(40));
var newS = EditorGUILayout.IntField(current.Sequence, GUILayout.Width(30));
if (newS != current.Sequence)
{
Undo.RecordObject(profile, "MultipleChoices Sequence");
current.Sequence = newS;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("问题:", GUILayout.Width(40));
var newQ = EditorGUILayout.TextArea(current.Question, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newQ != current.Question)
{
Undo.RecordObject(profile, "MultipleChoices Question");
current.Question = newQ;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("选项:", GUILayout.Width(40));
GUILayout.BeginHorizontal();
if (GUILayout.Button("+", GUILayout.Width(25)))
{
Undo.RecordObject(profile, "SingleChoices Add");
current.Choices.Add(new QuestionChoice("选项描述", null));
}
GUILayout.FlexibleSpace();
current.choiceType = (ChoiceType)EditorGUILayout.EnumPopup(current.choiceType, GUILayout.Width(80f));
GUILayout.Label("(类型)");
GUILayout.EndHorizontal();
GUILayout.EndHorizontal();
GUILayout.BeginVertical();
for (int k = 0; k < current.Choices.Count; k )
{
GUILayout.BeginHorizontal();
GUILayout.Space(50);
GUILayout.Label($"{Alphabet.Values[k]}.", GUILayout.Width(20));
switch (current.choiceType)
{
case ChoiceType.Text:
current.Choices[k].text = GUILayout.TextField(current.Choices[k].text);
break;
case ChoiceType.Pic:
current.Choices[k].pic = EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false) as Sprite;
break;
case ChoiceType.TextAndPic:
current.Choices[k].text = GUILayout.TextField(current.Choices[k].text);
current.Choices[k].pic = EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false, GUILayout.Width(110f)) as Sprite;
break;
}
var newValue = EditorGUILayout.Toggle(current.Answers.Contains(k), GUILayout.Width(15));
if (newValue != current.Answers.Contains(k))
{
Undo.RecordObject(profile, "MultipleChoices Answers");
if (newValue)
current.Answers.Add(k);
else
current.Answers.Remove(k);
}
if (GUILayout.Button(deleteContent, "MiniButton", GUILayout.Width(18)))
{
Undo.RecordObject(profile, "Delete MultipleChoice");
current.Choices.RemoveAt(k);
break;
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
GUILayout.BeginHorizontal();
GUILayout.Label("题解:", GUILayout.Width(40));
var newA = EditorGUILayout.TextArea(current.Analysis, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newA != current.Analysis)
{
Undo.RecordObject(profile, "MultipleChoices Analysis");
current.Analysis = newA;
}
GUILayout.EndHorizontal();
}
}
break;
#endregion
#region 填空题
case QuestionType.COMPLETION:
for (int i = 0; i < profile.Completions.Count; i )
{
var current = profile.Completions[i];
if (!completionFoldoutMap.ContainsKey(current)) completionFoldoutMap.Add(current, true);
GUILayout.BeginHorizontal("IN Title");
completionFoldoutMap[current] = EditorGUILayout.Foldout(completionFoldoutMap[current], $"第 {current.Sequence} 题", true);
if (GUILayout.Button("×", GUILayout.Width(20f)))
{
profile.Completions.Remove(current);
completionFoldoutMap.Remove(current);
break;
}
GUILayout.EndHorizontal();
if (completionFoldoutMap[current])
{
GUILayout.BeginHorizontal();
GUILayout.Label("题号:", GUILayout.Width(40));
var newS = EditorGUILayout.IntField(current.Sequence, GUILayout.Width(30));
if (newS != current.Sequence)
{
Undo.RecordObject(profile, "Completion Sequence");
current.Sequence = newS;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("问题:", GUILayout.Width(40));
var newQ = EditorGUILayout.TextArea(current.Question, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newQ != current.Question)
{
Undo.RecordObject(profile, "Completion Question");
current.Question = newQ;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("答案:", GUILayout.Width(40));
if (GUILayout.Button("+", GUILayout.Width(25)))
{
Undo.RecordObject(profile, "CompletionAnswers Add");
current.Answers.Add(null);
}
GUILayout.EndHorizontal();
GUILayout.BeginVertical();
for (int n = 0; n < current.Answers.Count; n )
{
GUILayout.BeginHorizontal();
GUILayout.Space(50);
GUILayout.Label($"({n 1}).", GUILayout.Width(30));
var newC = EditorGUILayout.TextField(current.Answers[n]);
if (current.Answers[n] != newC)
{
Undo.RecordObject(profile, "CompletionAnswer");
current.Answers[n] = newC;
}
if (GUILayout.Button(deleteContent, "MiniButton", GUILayout.Width(18)))
{
Undo.RecordObject(profile, "CompletionAnswers Remove");
current.Answers.RemoveAt(n);
break;
}
GUILayout.EndHorizontal();
}
GUILayout.EndVertical();
GUILayout.BeginHorizontal();
GUILayout.Label("题解:", GUILayout.Width(40));
var newA = EditorGUILayout.TextArea(current.Analysis, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newA != current.Analysis)
{
Undo.RecordObject(profile, "Completion Analysis");
current.Analysis = newA;
}
GUILayout.EndHorizontal();
}
}
break;
#endregion
#region 论述题
case QuestionType.ESSAY:
for (int i = 0; i < profile.Essays.Count; i )
{
var current = profile.Essays[i];
if (!essayFoldoutMap.ContainsKey(current)) essayFoldoutMap.Add(current, true);
GUILayout.BeginHorizontal("IN Title");
essayFoldoutMap[current] = EditorGUILayout.Foldout(essayFoldoutMap[current], $"第 {current.Sequence} 题", true);
if (GUILayout.Button("×", GUILayout.Width(20f)))
{
profile.Essays.Remove(current);
essayFoldoutMap.Remove(current);
break;
}
GUILayout.EndHorizontal();
if (essayFoldoutMap[current])
{
GUILayout.BeginHorizontal();
GUILayout.Label("题号:", GUILayout.Width(40));
var newS = EditorGUILayout.IntField(current.Sequence, GUILayout.Width(30));
if (newS != current.Sequence)
{
Undo.RecordObject(profile, "Essay Sequence");
current.Sequence = newS;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("问题:", GUILayout.Width(40));
var newQ = EditorGUILayout.TextArea(current.Question, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newQ != current.Question)
{
Undo.RecordObject(profile, "Essay Question");
current.Question = newQ;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("答案:", GUILayout.Width(40));
var newA = EditorGUILayout.TextArea(current.Answer, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newA != current.Answer)
{
Undo.RecordObject(profile, "Essay Answer");
current.Answer = newA;
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
GUILayout.Label("题解:", GUILayout.Width(40));
var newV = EditorGUILayout.TextArea(current.Analysis, new GUIStyle(GUI.skin.textArea) { stretchWidth = false }, GUILayout.ExpandWidth(true));
if (newV != current.Analysis)
{
Undo.RecordObject(profile, "Essay Analysis");
current.Analysis = newV;
}
GUILayout.EndHorizontal();
}
}
break;
#endregion
default:
GUILayout.Label("Unknown Question Type.");
break;
}
GUILayout.EndScrollView();
}
}
#endif
}