使用嵌套的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集合的显示方式

使用嵌套的ScriptableObject及ReorderableList创建习题持久化数据的更多相关文章

  1. 使用Entity Framework通过code first方式创建数据库和数据表

    开发环境 WIN10 Entity Framework6.0  MVC5.0  开发工具 VS2015  SqlServer2012 1.创建上下文Context继承DbContext,并创建其他的业 ...

  2. 创建ACCESS数据库,并且创建表和数据。重点:关闭ACCESS数据库引用

    /// <summary> /// 创建ACCESS数据库,并且创建表和数据 /// </summary> /// <param name="dictTable ...

  3. sql server2008中怎样用sql语句创建数据库和数据表

    这是简单用代码实现创建数据库和数据表的sql语句,如下: --调用系统数据库-- use master go /***防止你要创建的数据库同名,先把它删除掉****/ if Exists(select ...

  4. 在ArcMap 10.3中创建和编辑数据

    在ArcMap 10.3中创建和编辑数据 .......待补充 新建 创建一个新文件((Points, Polylines, and Polygons/点.线.多边形)

  5. WordPress插件制作教程(五): 创建新的数据表

    上一篇讲解了怎样将数据保存到数据库,今天为大家讲解创建新的数据表,也就是说当我们激活插件的时候,会在该数据库下面创建一个新的数据表出来.原理很简单,激活插件的时候运行创建数据库的代码.看下面代码: & ...

  6. 如何创建一个要素数据类 IField,IFieldEdit,IFields,IFieldsEditI,GeometryDef,IGeometryDefEdit接口

    如何创建一个要素数据类 创建要素类用到了IFeatureWorkspace.CreateFeatureClass方法,在这个方法中有众多的参数,为了满足这些参数,我们要学习和了解下面的接口. IFie ...

  7. 根据dateFormatter创建NSDate类型数据

    根据dateFormatter 2000-01-01 创建NSDate类型数据 NSDateFormatter *dateFormatter = [NSDate shareDateFormatter] ...

  8. Angular4.x 创建组件|绑定数据|绑定属性|数据循环|条件判断|事件|表单处理|双向数据绑定

    Angular4.x 创建组件|绑定数据|绑定属性|数据循环|条件判断|事件|表单处理|双向数据绑定 创建 angular 组件 https://github.com/angular/angular- ...

  9. C# 利用mysql.data 在mysql中创建数据库及数据表

    C# 利用mysql.data 在mysql中创建数据库及数据表 using System; using System.Collections.Generic; using System.Linq; ...

  10. 快速创建显示数字数据的动画——CountUp.js

    由于项目需求,需要写一个数字增/减量的动画特效,最后找到了CountUp.js CountUp.js是一个无依赖,轻量级的JavaScript“类”,可用于快速创建以更有趣的方式显示数字数据的动画. ...

随机推荐

  1. DevOps 能力提升模型

    简介: DevOps 能力反映的是技术研发响应业务变化的能力.随着组织规模的增加和业务复杂性增长,DevOps 能力会变得越来越重要.持续提升 DevOps 的能力成为技术研发的共同挑战. 编者按:本 ...

  2. WPF 已知问题 在 ObservableCollection 的 CollectionChanged 修改集合内容将让 UI 显示错误

    本文记录一个 WPF 已知问题,在 ObservableCollection 的 CollectionChanged 事件里面,绕过 ObservableCollection 的异常判断逻辑,强行修改 ...

  3. kubeadm搭建k8s-1.24.8集群

    一.实验环境准备 k8s集群角色 IP 主机名 安装组件 配置 控制节点 192.168.10.40 master apiserver.controller-manager.scheduler.etc ...

  4. 在线程中使用Spring的Bean的方法、不推荐把“线程”注入到Spring

    一.不推荐把"线程"注入到spring 将线程注入到Spring容器中并不是一个常见的做法,而且通常也不推荐这样做,原因如下: 生命周期管理困难: Spring管理的Bean生命周 ...

  5. 在.Net中操作redis

    在.Net中操作redis 一.环境 .Net 7 redis 7.2.4 二.所需类包 StackExchange.Redis 三.连接redis信息 appsettings.json配置redis ...

  6. 一个开源轻量级的C#代码格式化工具(支持VS和VS Code)

    前言 C#代码格式化工具除了ReSharper和CodeMaid,还有一款由.NET开源.免费(MIT License).轻量级的C#语言代码格式化工具:CSharpier. 工具介绍 CSharpi ...

  7. Python竖版大屏2 | 用pyecharts开发可视化的奇妙探索!

    目录 1.SHINE主题 2.LIGHT主题 3.MACARONS主题 4.INFOGRAPHIC主题 5.WALDEN主题 6.WESTEROS主题 7.WHITE主题 8.WONDERLAND主题 ...

  8. (更新中)gprMax项目代码分解:gprMax.constants.py、gprMax.exceptions

    目录 1. 引言 2. gprMax.constants.py 3. gprMax.exceptions.py 4. 总结 Reference 1. 引言 本文对gprMax项目中的"gpr ...

  9. spring-boot集成Quartz-job存储方式一JDBC

    1.项目jar包依赖引入 <dependency> <groupId>org.springframework.boot</groupId> <artifact ...

  10. C 语言编程 — 高级数据类型 — void 类型

    目录 文章目录 目录 前文列表 void 类型 前文列表 <程序编译流程与 GCC 编译器> <C 语言编程 - 基本语法> <C 语言编程 - 基本数据类型> & ...