Unity ScriptableObject详解

https://blog.csdn.net/qq_36383623/article/details/99649941

一、ScriptableObject类概述、属性及函数

首先提供官方参考文档:API-Reference-ScriptableObject & Scriptable-Manual & SctiptableObject-Candycat

概述: ScriptableObject类直接继承自Object类;它和MonoBehaviour是并列的,都继承自Object(但MonoBehaviour并不是直接继承自Object),会在后面介绍ScriptableObject和MonoBehavior的比较;

脚本化对象就是一个数据容器,可以用来存储大量的数据,它是可序列化的,这个特点也正决定了它的主要用途;一个主要用处就是通过将数据存储在ScriptableObject对象中来减少工程以及游戏运行时因拷贝值所造成的内存占用;

ScriptableObject与预制体:当你有一个预制体,它附加了一些mono脚本,包含了一些数据,每次我们实例化预制体的时候它都会拷贝assets下原预制体的值生成一份自己的拷贝,然后我们可以修改场景内预制体的值而并不影响assets下预制体的值,这是prefab的特性,对于我们从一个prefab模板生成属性不同的游戏对象是很有用的,但是如果prefab里的脚本数据是不需要修改的,它就会造成很大的资源浪费,尤其在数据很多的时候;为了避免这种问题,我们可以在不需要修改prefab里的脚本数据时,考虑使用ScriptableObject来存储这些重复的数据,然后其它所有预制体都可以使用引用的方式来访问这份数据,这就意味着不管场景中实例了多少预制体,在内存中就只需要有一份数据;它所带给我们的启示就是,当预制体中的脚本里有大量重复数据时,我们要想着将数据抽离,单独保存在本地

举一个子弹的例子,这是一个比较典型的例子,因为一个子弹会包含很多属性,而且在场景中需要大量的实例化,我们要先做一个子弹的预制体,为了体现它的属性,我们会写一个派生自MonoBehaviour的脚本Bullet,在里面添加一些属性,然后将其附加到一个子弹游戏对象上,完成了一个子弹预制体的制作;之后我们在场景中实例化新的子弹的时候,这个实例也会有一个Bullet实例,而且最重要的是这些子弹都会有这样一份Bullet而且它们的数据是相同的,这就是之前讲到的因为拷贝值产生新的实例导致了大量的内存占用;这个时候我们就可以把这个派生自MonoBehaviour的脚本里的数据放置在派生自ScriptableObject的脚本,然后就可以在创建Assets下创建一份数据文件,设置完数据后,在Bullet中我们就定义一个指向ScriptableOjbect对象的引用,这样它就由原来的拷贝大量值并存储重复的值变成了拷贝一个引用并存储一个引用

应用场景及方式:当使用编辑器运行游戏的时候,可以将数据保存到ScriptableObject里(当创建一个脚本化对象实例后使用AssetDatabase.CreateAsset()保存该资源),退出之后也不会丢失,因为它是作为Assets下的资源存在的;它是仅在编辑器中才可以保存修改的数据(因为ScriptableObject对象虽然声明在UnityEngine中,但是它的Scriptable是通过UnityEditor命名空间下的类例如Editor类等来实现的),所以在部署构建的时候不可以用于存储游戏运行时更改的数据,但是可以使用之前存储好的数据,也就是ScriptableObject生成的数据资源文件在Editor外具有只读属性,这是非常需要注意的一点,如果你需要在游戏中修改数据并存储下来,就不推荐使用ScriptableObject了

总结:它就是用来在编辑器模式下保存和存储数据到本地Assets下的,数据保存以后是可以共享的,就像纹理、shader等资源一样,是可以共享于当前整个工程和其它工程的;这个ScriptableObject在真机上不可修改的,就像我们不可以在游戏运行时修改一个shader资源的代码、不可以修改一个纹理资源的像素内容一样,而在Unity Editor里可以修改ScriptableObject是因为Unity的编辑器对它格式的支持,就像使用vs code修改shader和使用ps修改一张纹理一样;

属性

  • name:返回对象的名称

静态方法

  • CreateInstance:在游戏运行时创建一个Scriptable类型的实例,不使用时被GC回收;
//静态方法,使用了ScriptableObject类约束的泛型参数T
public static T CreateInstance<T>() where T : ScriptableObject;
  • Instantiate:实例化一个对象,返回一个实例;类似于GameObject的Instantiate(),其它函数也和GameObject类似;
public static T Instantiate(T original);
public static T Instantiate(T original, Transform parent);
public static T Instantiate(T original, Transform parent, bool worldPositionStays);
public static T Instantiate(T original, Vector3 position, Quaternion rotation);
public static T Instantiate(T original, Vector3 position, Quaternion rotation, Transform parent);
Parameters

Message:ScriptableObject内部实现上也继承自MonoBehavior,它只有四个消息函数,Awake()、OnDestroy()、OnEnable()、OnDisable();

二、ScriptableObject与MonoBehaviour

1.两个类都是继承自Ojbect类

首先看一下MonoBehaviour类:

ScriptableObject对象是不可以作为组件附加到对象上的,因为它的出现就是为了要让数据独立于具体的游戏对象;

2.ScriptableObject对象拥有非常少的回调函数

Awake():创建一个对象实例时调用;

OnEnable():如下,创建或被加载时调用;

OnDisable / OnDestroy:当调用销毁函数时,

如果想仔细了解ScriptableObject的回调函数何时调用,可以自己定义一个ScriptableObject对象,然后在四个回调函数中加上相应的Debug.Log()即可;

三、案例使用

1.简单应用:创建Scriptable实例与保存到文件资源

我们已经知道我们在开发中如何去使用脚本化对象了,回顾一下:

  • 第一种:在游戏运行时创建脚本化对象实例,然后可以将数据保存到本地(如果不保存,它会在游戏结束后销毁);
  • 第二种:在代码中引用Assets文件夹下的脚本化对象资源(也许是从游戏运行时保存下来的数据,也许是手动创建的数据);

使用案例:游戏配置文件;商品清单;敌人统计数据等;

2.CreateAssetMenuAttribute-使用ScriptableObject

命名空间在UnityEngine的一个特性,它有三个属性filename,menuname,order,它用于标记一个派生自Scriptable的类,可以在Assets下的create下添加子菜单项,用于创建.asset资源文件(Scriptable类是和.asset资源关联的);它和AddComponentMenu特性类似,只不过后者是将脚本组件添加到组件按钮的子菜单项,Unity中提供了很多用于编辑器菜单项扩展的属性例如MenuItem、ContextMenu等等,我们往往根据它的名称就可以大概知道它用在什么地方;

注意:虽然使用MenuItem也可以在Assets/Create下创建一个菜单项,但是两种方式还是有区别;

如果是使用MenuItem在静态方法中调用CreateAsset创建资源,那么需要指定创建的目录和资源名称,如果资源已经存在,则不会创建新资源;而CreateAssetMenu则可以在Assets下任意目录创建资源,而且可以创建多个资源;

public class MakeScriptableObject {
    [MenuItem("Assets/Create/My Scriptable Object")]
    public static void CreateMyAsset()
    {
        MyScriptableObjectClass asset = ScriptableObject.CreateInstance<MyScriptableObjectClass>();
 
        AssetDatabase.CreateAsset(asset, "Assets/NewScripableObject.asset");
        AssetDatabase.SaveAssets();
 
        EditorUtility.FocusProjectWindow();
 
        Selection.activeObject = asset;
    }
}
[CreateAssetMenu(fileName = "data", menuName = "ScriptableObjects/SpawnManagerScriptableObject", order = 1)]
public class SpawnManagerScriptableObject : ScriptableObject {
    public string prefabName;
    public int numberOfPrefabsToCreate;
    public Vector3[] spawnPoints;
}

然后,在Assets下创建一个可编程对象资源,设置好所需数据;如果需要在其它脚本中获取该数据,是需要声明一个该类型变量,然后为其赋值或加载该数据资源;然后,就像使用用一个类中的公有变量一样使用即可;

public SpawnManagerScriptableObject spawnManagerValues;
//spawnManagerValues.prefabName

提示:可以使用CustomEditor来为ScriptableOjbect创建自定义属性面板;

3.ScriptableObject与Json

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值