为什么要使用ScriptableObject序列化资源?
在设计模式中的抽象工厂模式或者建造者模式,其核心都是根据一份数据,创建出一个实体
数据的来源可以有很多种:
1.在脚本里初始化变量的时候赋予初始值
2.在脚本的初始值添加序列化字段,将创建出来的实体保存为预制体,在预制体的检查器页面设置实体数据的初始值
3.将数据保存在工厂类,在创建实体的时候为实体手动逐条赋值
4.在游戏开始时动态读取数据表赋予初始值
方法有很多,但是各自有各自的问题
在脚本中指定初始值,序列化变量,预制体设置序列化变量,工厂类逐条赋值,都有一个最根本的问题,每个单位的数值都绑定在单位自己的身上,当实体单位存在几百上千个的时候,会创建和个体相同数量的数据存储到计算机内存中,造成极大的内存浪费
比如游戏中的士兵,攻击力是10点,在你的游戏中有着一千个士兵,内存中就分配了1000个int或float类型变量的内存空间用来保存每个士兵的攻击力,但攻击力不同于当前生命值,是不会随着游戏进程而变化的,保存1000个相同的变量除了占据计算机内存使运行时电脑变的更卡以外一点作用都没有
将数据表的数据保存到数据类里面,再用实体访问数据类的数据,这样确实能够避免为多个相同的变量分配内存,但是数据表的数据类型毕竟是有限的,只能读取到字符串,在通过字符串转换成其他类似int,float之类的数据,遇到需要指定预制体,需要指定材质球,需要指定颜色的时候,就捉襟见肘了
在此,我隆重向大家介绍Unity专门为了解决这个问题而诞生的功能
ScriptableObject序列化资源
ScriptableObject序列化资源能做什么?
ScriptableObject序列化资源可以保存一份数据,这份数据被多个类同时访问同时只在加载时分配一次内存,避免了内存占用的问题
同时,在ScriptableObject序列化资源中可以指定几乎Unity中所有的资源类型,预制体,材质球,颜色,只要是在代码中可以用public关键字序列化出来的变量类型,都可以在编辑器中直接指定
ScriptableObject序列化资源要怎么用?
如何创建ScriptableObject序列化资源?
cs
using UnityEngine;
[CreateAssetMenu(fileName = "PlayerData", menuName = "CreatScripTable/PlayerData")]
public class PlayerScripData : ScriptableObject
{
public float hp = 100.0f;
public int damage = 10;
public string getName = "player";
public Color color = Color.white;
public GameObject prefab;
public Material material;
}
上面这个脚本的功能就是创建ScriptableObject序列化资源
直接创建一个C#的Scrip脚本,然后将上面的内容粘贴并覆盖到脚本,就能创建ScriptableObject序列化资源了
CreateAssetMenu标签中需要指定两个变量的属性
fileName:创建出来文件的名字
menuName:通过选择右键菜单页面的哪个子选项创建资源

在脚本创建完成之后等待编辑器刷新一下,然后右键资源的空白部分,按照图片位置点击菜单选项,就可以创建出ScriptableObject序列化资源了,资源的名字可以随意更改

创建出来的资源如上图,其中资源的属性为继承了ScriptableObject类的数据类定义的带有public标签的变量,面板中的数值的初始值就是在脚本中指定的值,数据可以在编辑器中随意更改,预制体和材质也可以直接从资源页面拖拽,脚本中指定的值只影响创建资源的初始值,最终的数值以编辑器中输入的数值为准
如何使用ScriptableObject序列化资源?
核心只有一句代码
cs
public PlayerScripData playerData;
void DebugValue()
{
Debug.Log("生命值:" + playerData.hp);
Debug.Log("攻击力:" + playerData.damage);
}
定义一个在编辑器中序列化出来的,继承了ScriptableObject类型的类的变量,然后在编辑器中将创建好的ScriptableObject序列化资源资源拖拽过去,然后直接通过变量访问里面的属性,访问出来的属性就是编辑器中指定的属性
下面是使用PlayerScripData写了个简单的工厂和实体的示例代码:
cs
using UnityEngine;
public class CreatPlayerEntity : MonoBehaviour
{
public PlayerScripData playerData;
public void CreatPlayer()
{
GameObject g = GameObject.Instantiate(playerData.prefab);
g.GetComponent<PlayerEntity>().Init(playerData);
}
}
cs
using UnityEngine;
public class PlayerEntity : MonoBehaviour
{
private PlayerScripData playerData;
private float hp;
public void Init(PlayerScripData playerData)
{
this.playerData = playerData;
hp = playerData.hp;
}
public void Hit(PlayerEntity player)
{
player.BeHit(playerData.damage);
}
public void BeHit(int damage)
{
hp -= damage;
}
}