ScriptableObject 是 Unity 提供的一个数据配置存储基类,可以用来保存大量数据的数据模板,类似于可以自定义的数据资源文件模板
它是一个类似 MonoBehavior 的基类,需要继承来进行使用
主要作用:
-
数据复用(多个对象用同一个数据)
-
配置文件(配置游戏中的数据)
-
编辑模式下的数据持久化
优点:
-
直接在 Inspector 窗口编辑配置数据,利用它来做配置文件
-
处理重复数据,减少数据拷贝时造成的内存占用,利用它来做公共数据
-
更方便的处理数据带来的多态行为
扫描二维码关注公众号,回复: 15191604 查看本文章
一、创建数据模板
创建 ScriptableObject 数据模板,需要进行的操作为:
- 创建 C# 脚本,继承 ScriptableObject 类
- 在该类中声明成员(变量、方法等)
public class MyData : ScriptableObject
{
// 可以声明任何类型的成员变量
// 如果希望在 Inspector 窗口中能够编辑它,需要声明为 public 或添加 SerializeField 特性
public int i;
public float f;
public bool b;
public GameObject obj;
public Material m;
public AudioClip audioClip;
public VideoClip videoClip;
public void PrintInfo() {
Debug.Log(i);
Debug.Log(f);
Debug.Log(b);
}
}
声明后,我们边可以在 Inspector 窗口中看到变化
我们可以在其中进行设置,但是这些设置都是默认数据,并没有真正使用他们
![image-20230604144929256](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041449323.png#pic_center)
这些关联信息都是通过脚本文件对应的 Unity 配置文件 meta 进行记录的,打开该脚本同目录下的 .meta 文件可看到:
![image-20230604145315888](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041453924.png#pic_center)
目前该数据只是一个数据容器模板,只是规定了需要创建的数据格式,并没有创建数据文件
二、创建数据文件
具体的方法有两种:
-
为类添加 CreateAssetMenu 特性,通过菜单创建资源
-
[CreateAssetMenu(fileName, menuName, order)]
fileName:创建出来的默认数据文件名
menuName:在 Asset / Create 菜单中显示的名字
order:在 Asset / Create 菜单中的顺序,通过数字指定排列优先级
[CreateAssetMenu(fileName = "MyData", menuName = "ScriptableObject/我的数据", order = 0)] public class MyData : ScriptableObject { ... }
在 Project 窗口中右键即可找到对应目录创建数据文件:
创建出来的文件是实实在在的独立数据文件 .asset,可以在 Inspector 面板中编辑其数据。
-
-
利用 ScriptableObject 的静态方法创建数据对象,然后将数据对象保存在工程目录下
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class ScriptableObjectTool
{
[MenuItem("ScriptableObject/CreateMyData")]
public static void CreateMyData()
{
// 书写创建数据资源文件的代码
MyData asset = ScriptableObject.CreateInstance<MyData>();
// 通过编辑器API 根据数据创建一个数据资源文件
AssetDatabase.CreateAsset(asset, "Assets/Resources/MyDataTest.asset");
// 保存创建的资源
AssetDatabase.SaveAssets();
// 刷新界面
AssetDatabase.Refresh();
}
}
在菜单窗口中即可创建,效果和方法 1 一样。
但是由于引用了 UnityEditor 命名空间,需要将该脚本放在 Editor 文件夹下,否则项目打包会失败。
![image-20230604150744685](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041507718.png#pic_center)
方法 1 在项目使用中更常用,方法 2 在 Unity 编辑器开发中更常见。
三、使用数据文件
(一)直接关联使用
在继承 MonoBehaviour 类中申明数据容器类型的成员,在 Inspector 窗口进行关联
public class Lesson2 : MonoBehaviour
{
public MyData data;
}
![image-20230604151604644](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041516679.png#pic_center)
(二)加载数据使用
使用代码加载数据文件资源
Resources、AB包、Addressables 都支持加载继承 ScriptableObject 的数据文件
以 Resources 为例,将数据文件放入 Resources 文件夹下,之后使用代码加载。
![image-20230604151857695](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041518715.png#pic_center)
public class Lesson2 : MonoBehaviour
{
public MyData data;
private void Start() {
data = Resources.Load<MyData>("ScriptableObject/MyDataTest");
data.PrintInfo();
}
}
(三)生命周期函数
ScriptableObject 和 MonoBehavior 类似,也存在生命周期函数
但是生命周期函数的数量更少,主要做了解,一般使用较少
- Awake:数据文件创建时调用
- OnDestroy:ScriptableObject 对象将被销毁时调用
- OnDisable:ScriptableObject 对象销毁时、即将重新加载脚本程序集时调用
- OnEnable:ScriptableObject 创建或者加载对象时调用
- OnValidate:编辑器才会调用的函数,Unity 在加载脚本或者 Inspector 窗口中更改值时调用
public class MyData : ScriptableObject
{
// 可以声明任何类型的成员变量
// 如果希望在 Inspector 窗口中能够编辑它,需要声明为 public 或添加 SerializeField 特性
public int i;
public float f;
public bool b;
public GameObject obj;
public Material m;
public AudioClip audioClip;
public VideoClip videoClip;
private void Awake() {
Debug.Log("数据文件创建时会调用");
}
private void OnEnable() {
}
private void OnDisable() {
}
private void OnDestroy() {
}
private void OnValidate() {
Debug.Log("123");
}
public void PrintInfo() {
Debug.Log(i);
Debug.Log(f);
Debug.Log(b);
}
}
![image-20230604152516958](https://zheliku-typora-01.oss-cn-beijing.aliyuncs.com/image/202306041525998.png#pic_center)
用途:
-
编辑器中的数据持久化
通过代码修改数据对象中内容,会影响数据文件,相当于达到了编辑器中数据持久化的目的
(该数据持久化只是在编辑模式下的持久,发布运行时并不会保存数据) -
复用数据
如果多个对象关联同一个数据文件
相当于他们复用了一组数据,内存上更加节约空间
创建出来的数据资源文件,可以理解成一种记录数据的资源
它的使用方式,和 Unity 中其它资源规则是一样的,比如:预设体、音频文件、视频文件、动画控制器文件、材质球等等
只不过通过继承 ScriptableObject 类生成的数据资源文件,它主要是和数据相关的
四、非持久化特性
ScriptableObject 的非持久化指的是不管在编辑器模式还是在发布后,下次运行程序后之前修改的数据都不会存储下来
通过 ScriptableObject 中的静态方法 ScriptableObject.CreateInstance()
创建数据文件来使用,就好像直接 new 一个数据结构类对象,其构造函数在 ScriptableObject 模板中定义。
data = ScriptableObject.CreateInstance("MyData") as MyData;
data = ScriptableObject.CreateInstance<MyData>();
data.PrintInfo();
- 该方法可以在运行时创建出指定继承 ScriptableObject 的对象
- 该对象只存在于内存当中,可以被 GC 回收,调用一次就创建一次
意义:
- 只希望在运行时能有一组唯一的数据可以使用,但又不希望保存为数据资源文件浪费硬盘空间
- 只在运行时使用,在编辑器模式下也不会保存在本地
对于 ScriptableObject 的数据,由于它在游戏发布运行过程中无法被持久化,可以利用 PlayerPrefs、XML、Json、2进制等等方式,让其可以达到被真正持久化的目的。
但是并不建议使用 ScriptableObject 来做数据持久化,有点画蛇添足。