使用嵌套的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. [PHP] composer, PHP Fatal error: Allowed memory size of xx bytes exhausted

    终端执行 composer 命令时经常会遇到内存不够的情况. 视情况升级一下 composer,使用 composer self-update. 默认 php 的内存限制是 128M,临时取消 php ...

  2. Win10下小米路由器4A百兆版刷Openwrt固件【图片详细版】

    将原来的小米路由器换成了华为,早就听闻刷软路由可以实现去广告,解锁灰色歌单等诸多骚操作.就来榨取这个小米4A的剩余价值来着的. 注意 1. 必须使用路由模式,中继模式是打不开telnet的 更新固件 ...

  3. kubenetes1.26中安装kubesphere3.4版本

    一.安装前环境准备 # kubesphere官网:https://kubesphere.io/zh/docs/v3.4/introduction/what-is-kubesphere/ # 1.kub ...

  4. git将本地项目关联远程仓库并上传到新分支

    混合项目开发,项目交接的时候没做好,新入职接手老项目的时候一脸懵逼,进入开发阶段时,越搞越不对,越搞越不对,总感觉 本地跑的项目和己方测试环境以及客户的测试环境和目标环境不一致,结果发现着手的两套代码 ...

  5. Wordpress给每一个分类栏目定制不同的广告位

    给分类栏目添加广告位,等同于添加自定义字段. 如果需要依据不同的栏目给广告位添加不同的tag来源,需要在模板页面中获取栏目的分类别名,读取不同的广告. 图1 如图1所示添加新的图片输入框 1. 实现的 ...

  6. leaflet 用自定义pane实现图层顺序调整

    在 Leaflet 中,map panes 隐式地将图层组合在一起,而开发者并不知道这一点.这种分组允许 Web 浏览器以比单独处理图层更有效的方式同时处理多个图层. Map panes 使用 z-i ...

  7. LLM实战:LLM微调加速神器-Unsloth + LLama3

    1. 背景 五一结束后,本qiang~又投入了LLM的技术海洋中,本期将给大家带来LLM微调神器:Unsloth. 正如Unsloth官方的对外宣贯:Easily finetune & tra ...

  8. MySQL所有的主从同步架构搭建方式

    目录 一.前言 二.关于MySQL主从同步 三.部署规划 3.1 服务器规划 3.2 数据库目录规划 四.准备工具 五.四台机器上使用通用二进制包安装MySQL(以node7为例) 5.1 上传MyS ...

  9. docker安装MySQL8.0.35主从复制(实战保姆级)

    很久没有记录了,今天有时间就记录一下最近安装遇到的问题 liunx安装docker这个是前提,就不多过述 1 准备两台服务器 10.104.13.139 10.104.13.140 2 确保liunx ...

  10. nginx的11个阶段

    nginx处理请求的11个阶段 阶段 模块 第一阶段 POST_READ realip 第二阶段 SERVER_REWRITE rewrite 第三阶段 FIND_CONFIG 第四阶段 REWRIT ...