ScriptableObject是什么
是Unity提供的一个数据配置存储的基类。可以用来保存大量数据的数据容器,
是一个可以自定义数据的资源文件,一般用来当关卡配置,或者其他的配置一类的文件,一般用于只用不改的公共数据。
可以发现,这个能做的和单例类也能做,单例类也是共享一份数据,
这个和单例管理类都能结合json那些去持久化保存,不过如果不进行结合的话,
单例类没法在编辑器开发和运行中保存,但是scriptableobject可以的,而且单例类要写出个单例,然后设置跨场景不销毁,还要哪边都要去通过GetInstance去访问单例的对象,远没有直观的scriptableobject方便,所以通用管理配置类最好用scriptableobject来做
主要作用
- 1、 数据复用(多个对象用同一个数据,节省性能,避免了每个类都去申请空间,浪费)
- 2、配置文件(配置游戏中的数据)
- 3、编辑模式下的数据持久化
数据复用
创建的方法
1、
这种比较简便,在project文件内,点击加号就能看到,这种方法需要继承ScripteableObject类
2、
在顶部菜单栏创建,自主传入类名,指定生成位置
使用方法
1、直接声明一个资源类,然后在inspector,直接将asset拖上去 2、放到能加载到的文件夹内,直接加载 3、通过单例模式去加载,这样不用去动态加载和手动拖取 3、数据只会在编辑模式才会被更改,也可以在编辑模式下,运行时候用代码更改,但是不能再打包出去后更改
void Start()
{
#region 知识点一 ScriptableObject数据文件的使用
//1.通过Inspector中的public变量进行关联
//1-1.创建一个数据文件
//1-2.在继承MonoBehaviour类中申明数据容器类型的成员
// 在Inspector窗口进行关联
//data.PrintInfo();
//2.通过资源加载的信息关联
//加载数据文件资源
//注意:Resources、AB包、Addressables都支持加载继承ScriptableObject的数据文件
data = Resources.Load<MyData>("MyDataTest");
data.PrintInfo();
//注意:如果多个对象关联同一个数据容器文件,他们共享的是一个对象
// 因为是引用对象,所以在其中任何地方修改后,其它地方也会发生改变
#endregion
#region 知识点二 ScriptableObject的生命周期函数
//ScriptableObject和MonoBehavior很类似
//它也存在生命周期函数
//但是生命周期函数的数量更少
//主要做了解,一般我们使用较少
//Awake 数据文件创建时调用
//OnDestroy ScriptableObject 对象将被销毁时调用
//OnDisable ScriptableObject 对象销毁时、即将重新加载脚本程序集时 调用
//OnEnable ScriptableObject 创建或者加载对象时调用
//OnValidate 编辑器才会调用的函数,Unity在加载脚本或者Inspector窗口中更改值时调用
#endregion
#region 知识点三 ScriptableObject好处的体现
//1.编辑器中的数据持久化
//通过代码修改数据对象中内容,会影响数据文件
//相当于达到了编辑器中数据持久化的目的
//(该数据持久化 只是在编辑模式下的持久,发布运行时并不会保存数据)
data.i = 9999;
data.f = 5.5f;
data.b = false;
//2.复用数据
//如果多个对象关联同一个数据文件
//相当于他们复用了一组数据,内存上更加节约空间
#endregion
#region 总结
//其实创建出来的数据资源文件,你可以把它理解成一种记录数据的资源
//它的使用方式,和我们以前使用Unity当中的其它资源规则是一样的
//比如:预设体、音频文件、视频文件、动画控制器文件、材质球等等
//只不过通过继承ScriptableObject类生成的数据资源文件,它主要是和数据相关的
#endregion
}
非持久化数据
public MyData data;
// Start is called before the first frame update
void Start()
{
#region 知识点一 ScriptableObject的非持久化数据指的是什么
//指的是不管在编辑器模式还是在发布后都 不会持久化的数据
//我们可以根据自己的需求随时创建对应数据对象进行使用
//就好像直接new一个数据结构类对象
#endregion
#region 知识点二 如何利用ScriptableObject生成非持久化的数据
//利用ScriptableObject中的静态方法 CreateInstance<>()
//该方法可以在运行时创建出指定继承ScriptableObject的对象
//该对象只存在于内存当中,可以被GC
//调用一次就创建一次
//通过这种方式创建出来的数据对象 它里面的默认值 不会受到脚本中设置的影响
//data = ScriptableObject.CreateInstance("MyData") as MyData;
data = ScriptableObject.CreateInstance<MyData>();
data.PrintInfo();
#endregion
#region 知识点三 ScriptableObject的非持久化数据存在的意义
//只是希望在运行时能有一组唯一的数据可以使用
//但是这个数据又不太希望保存为数据资源文件浪费硬盘空间
//那么ScriptableObject的非持久化数据就有了存在的意义
//它的特点是
//只在运行时使用,在编辑器模式下也不会保存在本地
#endregion
}
非持久化数据变成持久化
原理就是将类保存到其他持久化方式中,类如json xml、playerprefs、2进制等等,其实无意义,
void Start()
{
#region 知识点一 回顾通过ScriptableObject创建非持久化数据
MyData data = ScriptableObject.CreateInstance<MyData>();
#endregion
#region 知识点二 回顾数据持久化
//硬盘<=>内存
//使用数据时从硬盘中读取
//数据改变后保存到硬盘上
//游戏退出程序关闭后,数据信息会被存储到硬盘上,达到持久化的目的
//我们讲授过的数据持久化相关知识
//PlayerPrefs
//XML
//Json
//2进制
//ScriptableObject并不适合用来做数据持久化功能
//但是我们可以利用我们学过的数据持久化方案 让其持久化
#endregion
#region 知识点三 利用Json结合ScriptableObject存储数据
data.PrintInfo();
//data.i = 9999;
//data.f = 6.6f;
//data.b = true;
//将数据对象 序列化为 json字符串
//string str = JsonUtility.ToJson(data);
//print(str);
把数据序列化后的结果 存入指定路径当中
//File.WriteAllText(Application.persistentDataPath + "/testJson.json", str);
//print(Application.persistentDataPath);
#endregion
#region 知识点四 利用Json结合ScriptableObject读取数据
//从本地读取 Json字符串
string str = File.ReadAllText(Application.persistentDataPath + "/testJson.json");
//根据json字符串反序列化出数据 将内容覆盖到数据对象中
JsonUtility.FromJsonOverwrite(str, data);
data.PrintInfo();
#endregion
#region 总结
//对于ScriptableObject的数据
//由于它在游戏发布运行过程中无法被持久化
//我们可以利用 PlayerPrefs、XML、Json、2进制等等方式
//让其可以达到被真正持久化的目的
//但是我个人并不建议大家利用ScriptableObject来做数据持久化
//有点画蛇添足的意思了
#endregion
}
复用数据
例如 发射子弹,每个子弹上都有速度、攻击力等等(不变的,固定的),那么创建n个子弹就要开辟n份数据,这有点浪费。这样可以用通用的,然后只去改通用的,这些子弹就会都变化了。要是用单例去做,还要哪边都要去通过GetInstance去访问单例的对象,然后去写代码去更改,这里直接去更改数据更方便
public class Bullet : MonoBehaviour
{
public BulletInfo info;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
this.transform.Translate(Vector3.forward * info.speed * Time.deltaTime);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu()]
public class BulletInfo : ScriptableObject
{
public float speed;
public int atk;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson6 : MonoBehaviour
{
public BulletInfo info;
// Start is called before the first frame update
void Start()
{
#region 知识点一 使用预设体对象可能存在的内存浪费问题
//对于只用不变的数据
//以面向对象的思想去声明对象类是可能存在内存浪费的问题的
//我们以子弹对象为例
#endregion
#region 知识点二 举例说明 利用ScriptableObject数据对象 更加节约内存
#endregion
#region 总结
//对于不同对象,使用相同数据时
//我们可以使用ScriptableObject来节约内存
#endregion
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
info.speed += 1;
}
}
单例模式的应用
用单例模式可以避免很多代码,例如,以前要手动拖取或者动态加载,浪费事,
//继承自这个类的子类,去创建子类的ScriptableObject
public class SingleScriptableObject<T> :ScriptableObject where T:ScriptableObject
{
private static T instance;
public static T Instance
{
get
{
//如果为空 首先应该去资源路劲下加载 对应的 数据资源文件
if (instance == null)
{
//我们定两个规则
//1.所有的 数据资源文件都放在 Resources文件夹下的ScriptableObject中
//2.需要复用的 唯一的数据资源文件名 我们定一个规则:和类名是一样的
instance = Resources.Load<T>("ScriptableObject/" + typeof(T).Name);
}
//如果没有这个文件 为了安全起见 我们可以在这直接创建一个数据
if(instance==null)
{
instance = CreateInstance<T>();
}
//甚至可以在这里 从json当中读取数据,但是我不建议用ScriptableObject来做数据持久化
return instance;
}
}
}
//这个TestData,去创建出一个名为TestData的asset,然后可以直接在其他类里点出来TestData.GetInstance().i
[CreateAssetMenu]
public class TestData : SingleScriptableObject<TestData>
{
public int i;
public bool b;
}
void Start()
{
#region 知识点一 为什么要单例模式化的获取数据
//对于只用不变并且要复用的数据
//比如配置文件中的数据
//我们往往需要在很多地方获取他们
//如果我们直接通过在脚本中 public关联 或者 动态加载
//如果在多处使用,会存在很多重复代码,效率较低
//如果我们将此类数据通过单例模式化的去获取
//可以提升效率,减少代码量
#endregion
#region 知识点二 实现单例模式化获取数据
//知识点
//面向对象、单例模式、泛型等等
//我们可以实现一个ScriptableObject数据单例模式基类
//让我们只需要让子类继承该基类
//就可以直接获取到数据
//而不再需要去通过 public关联 和 资源动态加载
print(RoleInfo.Instance.roleList[0].id);
print(RoleInfo.Instance.roleList[1].tips);
print(TestData.Instance.i);
print(TestData.Instance.b);
#endregion
#region 总结
//这种基类比较适合 配置数据 的管理和获取
//当我们的数据是 只用不变,并且是唯一的时候,可以使用该方式提高我们的开发效率
//在此基础上你也可以根据自己的需求进行变形
//比如添加数据持久化的功能,将数据从json中读取,并提供保存数据的方法
//但是不建议大家用ScriptableObject来制作数据持久化功能
//除非你有这方面的特殊需求
#endregion
}
// Update is called once per frame
void Update()
{
}
|