使用嵌套的ScriptableObject及ReorderableList創建習題持久化數據
效果展示
題集持久化數據:存儲題目,可以直接在inspector面板上創建對應的問題子項
問題持久化數據 :
源碼
ScriptableObject類
題集的持久化數據類
[CreateAssetMenu(fileName = "Config/Question/QuestionData",menuName = "題集數據")]
[Serializable]
public class ChoiceQuestionData: ScriptableObject
{
[SerializeField]
public List<ChoiceQuestionItem> Questions=new List<ChoiceQuestionItem>();
public ChoiceQuestionItem AddQuestion() {
ChoiceQuestionItem node = ScriptableObject.CreateInstance(typeof(ChoiceQuestionItem)) as ChoiceQuestionItem ;
Questions.Add(node);
return node;
}
public void Clear() {
Questions.Clear();
}
}
問題的持久化數據
[Serializable]
[CreateAssetMenu(fileName = "Config/Question/QuestionItem",menuName = "問題")]
public class ChoiceQuestionItem : ScriptableObject
{
[TextArea]
public string Question;
public List<string> Choices;
public List<int> RightOptions;
}
[Serializable]
[CreateAssetMenu(fileName = "Config/Question/QuestionItem",menuName = "問題")]
public class ChoiceQuestionItem : ScriptableObject
{
[TextArea]
public string Question;
public List<string> Choices;
public List<int> RightOptions;
}
題集的Editor腳本
[CustomEditor(typeof(ChoiceQuestionData))]
public class ChoiceQuestionDataEditor: UnityEditor.Editor
{
private ChoiceQuestionData _choiceQuestionData;
private ReorderableList _questionReorderableList;
private SerializedProperty _questionProperty;
private int _index;
private string _questionName;
public void OnEnable()
{
_choiceQuestionData = (ChoiceQuestionData)this.target;
_questionProperty = serializedObject.FindProperty("Questions");
_questionReorderableList = new ReorderableList(serializedObject, _questionProperty, true,true,true,true);
//添加新元素時的回調函數,自定義新元素的值
_questionReorderableList.onAddCallback = list =>
{
if (list.serializedProperty != null)
{
var item = AddItem();
list.serializedProperty.arraySize++;
list.index = list.serializedProperty.arraySize - 1;
list.serializedProperty.GetArrayElementAtIndex(list.index)
.objectReferenceValue = item;
}
else
{
ReorderableList.defaultBehaviours.DoAddButton(list);
}
};
//刪除元素時的回調函數
_questionReorderableList.onRemoveCallback = list =>
{
RemoveItem(list.index);
ReorderableList.defaultBehaviours.DoRemoveButton(list);
};
}
public override void OnInspectorGUI()
{
//更新序列化對象的表示形式。
serializedObject.Update();
//繪製列表頭部,如標題等
_questionReorderableList.drawHeaderCallback = DrawHeaderCallback;
//繪製列表內部元素,缺少時面板不會顯示具體元素的可賦值項,只會以Element+序號代替
_questionReorderableList.drawElementCallback = DrawElement;
//ReorderableList自動繪製
_questionReorderableList.DoLayoutList();
#region 添加問題
EditorGUILayout.BeginHorizontal();
_questionName = EditorGUILayout.TextField("問題名稱", _questionName);
if (GUILayout.Button("添加問題", GUILayout.Height(15))) {
AddItem();
}
EditorGUILayout.EndHorizontal();
#endregion
#region 刪除問題
EditorGUILayout.BeginHorizontal();
_index = EditorGUILayout.IntField("索引", _index);
if (GUILayout.Button("刪除", GUILayout.Height(15)))
{
if (_index > -1f && _index < _choiceQuestionData.Questions.Count)
{
AssetDatabase.RemoveObjectFromAsset(_choiceQuestionData.Questions[_index]);
_choiceQuestionData.Questions.RemoveAt(_index);
AssetDatabase.SaveAssets();
}
}
EditorGUILayout.EndHorizontal();
#endregion
#region 清空問題
if (GUILayout.Button("清空", GUILayout.Height(20),GUILayout.Width(100))) {
var items = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_choiceQuestionData))
.Where(x=> x is ChoiceQuestionItem);
foreach (var item in items)
{
AssetDatabase.RemoveObjectFromAsset(item);
}
this._choiceQuestionData.Clear();
AssetDatabase.SaveAssets();
}
#endregion
//應用序列化對象的更改。
serializedObject.ApplyModifiedProperties();
}
/// <summary>
/// 繪製列表頭部
/// </summary>
/// <param name="rect"></param>
void DrawHeaderCallback(Rect rect)
{
GUI.Label(rect, "問題:");
Rect subChildRect = new Rect(rect.x + 80, rect.y, 150, rect.height);
GUI.Label(subChildRect, "【選項合集】");
}
/// <summary>
/// 繪製列表內部元素
/// </summary>
void DrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty element = _questionProperty.GetArrayElementAtIndex(index);
EditorGUI.PropertyField(rect, element);
}
ChoiceQuestionItem AddItem()
{
var question=this._choiceQuestionData.AddQuestion();
question.name = _questionName??"QuestionItem";
//嵌套ChoiceQuestionItem的ScriptableObject到ChoiceQuestionData的ScriptableObject對象,方便歸一到一個上層文件
AssetDatabase.AddObjectToAsset(question, target);
AssetDatabase.SaveAssets();
return question;
}
void RemoveItem(int index)
{
AssetDatabase.RemoveObjectFromAsset(this._choiceQuestionData.Questions[index]);
this._choiceQuestionData.Questions.RemoveAt(index);
AssetDatabase.SaveAssets();
}