使用嵌套的ScriptableObject及ReorderableList創建習題持久化數據

使用嵌套的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();
        }

參考

  1. Unity編輯器拓展之二:ReorderableList可重新排序的列表框(複雜使用)

  2. Unity 編輯器擴展總結 七:數組或list集合的顯示方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章