Unity中使用ScriptableObject允许我们创建配置文件并作为Unity中的Asset资产使用,无需像Json、XML等文件需要通过IO读取并反序列化成为我们需要的数据结构,其弊端是不可以像Json等配置文件可以在外部进行修改,在此不对其做详细介绍,下面使用ScriptableObject创作一个用于配置问答系统数据的资产,先看效果:
配置文件中包含五种题型:判断、单选、多选、填空、论述,选项可以删减并配置正确答案,首先定义一个描述题型的枚举QuestionType:
namespaceSK.Framework{ /// <summary>/// 题型/// </summary>publicenumQuestionType { /// <summary>/// 判断题/// </summary>JUDGE, /// <summary>/// 单选题/// </summary>SINGLE_CHOICE, /// <summary>/// 多选题/// </summary>MULTIPLE_CHOICE, /// <summary>/// 填空题/// </summary>COMPLETION, /// <summary>/// 论述题/// </summary>ESSAY, } }
创建基类QuestionBase,定义公共字段,包括题号、问题、题型、题解:
namespaceSK.Framework{ /// <summary>/// 问题基类/// </summary>publicclassQuestionBase { /// <summary>/// 题号/// </summary>publicintSequence; /// <summary>/// 问题/// </summary>publicstringQuestion; /// <summary>/// 题型/// </summary>publicQuestionTypeType; /// <summary>/// 题解/// </summary>publicstringAnalysis; } }
选项因题型不同而异,判断题只包含两个选项:正确与错误,答案为一个bool类型字段。
usingSystem; namespaceSK.Framework{ /// <summary>/// 判断题/// </summary> [Serializable] publicclassJudgeQuestion : QuestionBase { /// <summary>/// 积极选项/// </summary>publicstringPositive="正确"; /// <summary>/// 消极选项/// </summary>publicstringNegative="错误"; /// <summary>/// 答案/// </summary>publicboolAnswer; } }
填空题需要一个string类型的列表存储所有答案:
usingSystem; usingSystem.Collections.Generic; namespaceSK.Framework{ /// <summary>/// 填空题/// </summary> [Serializable] publicclassCompletionQuestion : QuestionBase { /// <summary>/// 答案/// </summary>publicList<string>Answers=newList<string>(0); } }
论述题只需要一个string类型字段描述答案:
usingSystem; namespaceSK.Framework{ /// <summary>/// 论述题/// </summary> [Serializable] publicclassEssayQuestion : QuestionBase { /// <summary>/// 答案/// </summary>publicstringAnswer; } }
单选和多选题的选项分为三种不同类型:文字描述类型选项、图片类型选项、文字描述+图片类型选项,所以首先定义一个选项类型的枚举:
namespaceSK.Framework{ /// <summary>/// 选项类型/// </summary>publicenumChoiceType { /// <summary>/// 文本/// </summary>Text, /// <summary>/// 图片/// </summary>Pic, /// <summary>/// 文本+图片/// </summary>TextAndPic } }
单选题答案为一个int类型字段:
usingSystem; usingSystem.Collections.Generic; namespaceSK.Framework{ /// <summary>/// 单项选择题/// </summary> [Serializable] publicclassSingleChoiceQuestion : QuestionBase { /// <summary>/// 选项类型/// </summary>publicChoiceTypechoiceType; /// <summary>/// 选项/// </summary>publicList<QuestionChoice>Choices=newList<QuestionChoice>(0); /// <summary>/// 答案/// </summary>publicintAnswer; } }
多选题答案为一个int类型列表:
usingSystem; usingSystem.Collections.Generic; namespaceSK.Framework{ /// <summary>/// 多项选择题/// </summary> [Serializable] publicclassMultipleChoiceQuestion : QuestionBase { /// <summary>/// 选项类型/// </summary>publicChoiceTypechoiceType; /// <summary>/// 选项/// </summary>publicList<QuestionChoice>Choices=newList<QuestionChoice>(0); /// <summary>/// 答案/// </summary>publicList<int>Answers=newList<int>(0); } }
需要注意各个问题类都需要添加Serializable特性,以确保其可序列化。单选和多选题的答案均为int类型字段,为了使int值对应A、B、C......Z字符,定义一个Alphabet字母表类, 例如Alphabet.Values[0]返回的便是字符A
namespaceSK.Framework{ /// <summary>/// 英文字母表/// </summary>publicstaticclassAlphabet { publicstaticchar[] Values=newchar[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类进行了编辑器拓展:
usingUnityEngine; usingSystem.Collections.Generic; #ifUNITY_EDITORusingUnityEditor; #endifnamespaceSK.Framework{ [CreateAssetMenu(fileName="New Questions Profile", menuName="Question Profile")] publicsealedclassQuestionsProfile : ScriptableObject { /// <summary>/// 判断题列表/// </summary>publicList<JudgeQuestion>Judges=newList<JudgeQuestion>(0); /// <summary>/// 单向选择题列表/// </summary>publicList<SingleChoiceQuestion>SingleChoices=newList<SingleChoiceQuestion>(0); /// <summary>/// 多项选择题列表/// </summary>publicList<MultipleChoiceQuestion>MultipleChoices=newList<MultipleChoiceQuestion>(0); /// <summary>/// 填空题列表/// </summary>publicList<CompletionQuestion>Completions=newList<CompletionQuestion>(0); /// <summary>/// 论述题列表/// </summary>publicList<EssayQuestion>Essays=newList<EssayQuestion>(); publicTGet<T>(QuestionTypetype, intsequence) whereT : QuestionBase { switch (type) { caseQuestionType.JUDGE: returnJudges.Find(m=>m.Sequence==sequence) asT; caseQuestionType.SINGLE_CHOICE: returnSingleChoices.Find(m=>m.Sequence==sequence) asT; caseQuestionType.MULTIPLE_CHOICE: returnMultipleChoices.Find(m=>m.Sequence==sequence) asT; caseQuestionType.COMPLETION: returnCompletions.Find(m=>m.Sequence==sequence) asT; caseQuestionType.ESSAY: returnEssays.Find(m=>m.Sequence==sequence) asT; default: returnnull; } } } #ifUNITY_EDITOR [CustomEditor(typeof(QuestionsProfile))] publicsealedclassQuestionsProfileInspector : Editor { privateQuestionsProfileprofile; privateQuestionTypecurrentType; privateColorbtnNormalColor=newColor(0.5f, 0.5f, 0.5f, 1f); privatereadonlyGUIContentdeleteContent=newGUIContent("-", "delete"); privateVector2scroll=Vector2.zero; privateDictionary<JudgeQuestion, bool>judgeFoldoutMap; privateDictionary<SingleChoiceQuestion, bool>singleChoicesFoldoutMap; privateDictionary<MultipleChoiceQuestion, bool>multipleChoicesFoldoutMap; privateDictionary<CompletionQuestion, bool>completionFoldoutMap; privateDictionary<EssayQuestion, bool>essayFoldoutMap; privatevoidOnEnable() { profile=targetasQuestionsProfile; judgeFoldoutMap=newDictionary<JudgeQuestion, bool>(); singleChoicesFoldoutMap=newDictionary<SingleChoiceQuestion, bool>(); multipleChoicesFoldoutMap=newDictionary<MultipleChoiceQuestion, bool>(); completionFoldoutMap=newDictionary<CompletionQuestion, bool>(); essayFoldoutMap=newDictionary<EssayQuestion, bool>(); } publicoverridevoidOnInspectorGUI() { OnTypeGUI(); OnMenuGUI(); OnDetailGUI(); if (GUI.changed) { EditorUtility.SetDirty(profile); serializedObject.ApplyModifiedProperties(); } } privatevoidOnTypeGUI() { 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(); } privatevoidOnMenuGUI() { GUILayout.BeginHorizontal(); if (GUILayout.Button("展开", EditorStyles.miniButtonLeft)) { switch (currentType) { caseQuestionType.JUDGE: profile.Judges.ForEach(m=>judgeFoldoutMap[m] =true); break; caseQuestionType.SINGLE_CHOICE: profile.SingleChoices.ForEach(m=>singleChoicesFoldoutMap[m] =true); break; caseQuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.ForEach(m=>multipleChoicesFoldoutMap[m] =true); break; caseQuestionType.COMPLETION: profile.Completions.ForEach(m=>completionFoldoutMap[m] =true); break; caseQuestionType.ESSAY: profile.Essays.ForEach(m=>essayFoldoutMap[m] =true); break; default: break; } } if (GUILayout.Button("收缩", EditorStyles.miniButtonMid)) { switch (currentType) { caseQuestionType.JUDGE: profile.Judges.ForEach(m=>judgeFoldoutMap[m] =false); break; caseQuestionType.SINGLE_CHOICE: profile.SingleChoices.ForEach(m=>singleChoicesFoldoutMap[m] =false); break; caseQuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.ForEach(m=>multipleChoicesFoldoutMap[m] =false); break; caseQuestionType.COMPLETION: profile.Completions.ForEach(m=>completionFoldoutMap[m] =false); break; caseQuestionType.ESSAY: profile.Essays.ForEach(m=>essayFoldoutMap[m] =false); break; default: break; } } if (GUILayout.Button("添加", EditorStyles.miniButtonMid)) { Undo.RecordObject(profile, "Add New"); switch (currentType) { caseQuestionType.JUDGE: profile.Judges.Add(newJudgeQuestion()); break; caseQuestionType.SINGLE_CHOICE: profile.SingleChoices.Add(newSingleChoiceQuestion()); break; caseQuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.Add(newMultipleChoiceQuestion()); break; caseQuestionType.COMPLETION: profile.Completions.Add(newCompletionQuestion()); break; caseQuestionType.ESSAY: profile.Essays.Add(newEssayQuestion()); 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) { caseQuestionType.JUDGE: profile.Judges.Clear(); judgeFoldoutMap.Clear(); break; caseQuestionType.SINGLE_CHOICE: profile.SingleChoices.Clear(); singleChoicesFoldoutMap.Clear(); break; caseQuestionType.MULTIPLE_CHOICE: profile.MultipleChoices.Clear(); multipleChoicesFoldoutMap.Clear(); break; caseQuestionType.COMPLETION: profile.Completions.Clear(); completionFoldoutMap.Clear(); break; caseQuestionType.ESSAY: profile.Essays.Clear(); essayFoldoutMap.Clear(); break; default: break; } } } GUILayout.EndHorizontal(); } privatevoidOnDetailGUI() { scroll=GUILayout.BeginScrollView(scroll); switch (currentType) { #region判断题caseQuestionType.JUDGE: for (inti=0; i<profile.Judges.Count; i++) { varcurrent=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)); varnewValue=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)); varnewQ=EditorGUILayout.TextArea(current.Question, newGUIStyle(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)); varnewP=EditorGUILayout.TextField(current.Positive); if (newP!=current.Positive) { Undo.RecordObject(profile, "Positive"); current.Positive=newP; } varnewAnswer=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)); varnewN=EditorGUILayout.TextField(current.Negative); if (newN!=current.Positive) { Undo.RecordObject(profile, "Negative"); current.Negative=newN; } varnewAns=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)); varnewA=EditorGUILayout.TextArea(current.Analysis, newGUIStyle(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单选题caseQuestionType.SINGLE_CHOICE: for (inti=0; i<profile.SingleChoices.Count; i++) { varcurrent=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)); varnewS=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)); varnewQ=EditorGUILayout.TextArea(current.Question, newGUIStyle(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(newQuestionChoice("选项描述", null)); } GUILayout.FlexibleSpace(); current.choiceType= (ChoiceType)EditorGUILayout.EnumPopup(current.choiceType, GUILayout.Width(80f)); GUILayout.Label("(类型)"); GUILayout.EndHorizontal(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(); for (intk=0; k<current.Choices.Count; k++) { GUILayout.BeginHorizontal(); GUILayout.Space(50); GUILayout.Label($"{Alphabet.Values[k]}.", GUILayout.Width(20)); switch (current.choiceType) { caseChoiceType.Text: current.Choices[k].text=GUILayout.TextField(current.Choices[k].text); break; caseChoiceType.Pic: current.Choices[k].pic=EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false) asSprite; break; caseChoiceType.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)) asSprite; break; } varnewValue=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)); varnewA=EditorGUILayout.TextArea(current.Analysis, newGUIStyle(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多选题caseQuestionType.MULTIPLE_CHOICE: for (inti=0; i<profile.MultipleChoices.Count; i++) { varcurrent=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)); varnewS=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)); varnewQ=EditorGUILayout.TextArea(current.Question, newGUIStyle(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(newQuestionChoice("选项描述", null)); } GUILayout.FlexibleSpace(); current.choiceType= (ChoiceType)EditorGUILayout.EnumPopup(current.choiceType, GUILayout.Width(80f)); GUILayout.Label("(类型)"); GUILayout.EndHorizontal(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(); for (intk=0; k<current.Choices.Count; k++) { GUILayout.BeginHorizontal(); GUILayout.Space(50); GUILayout.Label($"{Alphabet.Values[k]}.", GUILayout.Width(20)); switch (current.choiceType) { caseChoiceType.Text: current.Choices[k].text=GUILayout.TextField(current.Choices[k].text); break; caseChoiceType.Pic: current.Choices[k].pic=EditorGUILayout.ObjectField(current.Choices[k].pic, typeof(Sprite), false) asSprite; break; caseChoiceType.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)) asSprite; break; } varnewValue=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); elsecurrent.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)); varnewA=EditorGUILayout.TextArea(current.Analysis, newGUIStyle(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填空题caseQuestionType.COMPLETION: for (inti=0; i<profile.Completions.Count; i++) { varcurrent=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)); varnewS=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)); varnewQ=EditorGUILayout.TextArea(current.Question, newGUIStyle(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 (intn=0; n<current.Answers.Count; n++) { GUILayout.BeginHorizontal(); GUILayout.Space(50); GUILayout.Label($"({n + 1}).", GUILayout.Width(30)); varnewC=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)); varnewA=EditorGUILayout.TextArea(current.Analysis, newGUIStyle(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论述题caseQuestionType.ESSAY: for (inti=0; i<profile.Essays.Count; i++) { varcurrent=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)); varnewS=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)); varnewQ=EditorGUILayout.TextArea(current.Question, newGUIStyle(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)); varnewA=EditorGUILayout.TextArea(current.Answer, newGUIStyle(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)); varnewV=EditorGUILayout.TextArea(current.Analysis, newGUIStyle(GUI.skin.textArea) { stretchWidth=false }, GUILayout.ExpandWidth(true)); if (newV!=current.Analysis) { Undo.RecordObject(profile, "Essay Analysis"); current.Analysis=newV; } GUILayout.EndHorizontal(); } } break; #endregiondefault: GUILayout.Label("Unknown Question Type."); break; } GUILayout.EndScrollView(); } } #endif}