# 概念
ScriptableObject 是 unity 提出的针对数据存储资源的解决方案,能够将数据配置项 可视化
的展示在 unityd 属性面板中,并且极易于 json 进行数据交互。
个人使用过后觉得,其操作方式和继承非常类似,感觉像是对继承进一步封装使其更符合 unity 开发需要,让数据实体化,具现化的展示,通过拖拽式即可像拆装零件一般自由组合数据。(总而言之是对继承和多态的封装)
# 使用场景
用于 ScriptableObject 是针对 unity 的,在 unity 使用时比传统的数据格式如 json,xml 更加方便快捷。而且在面对诸如策划,美工等不了解程序的人员来说,上手更加快捷。
实质上,ScriptableObject 主要还是对数据进行存储和处理,只要是使用数据的地方都可以使用 ScriptableObject 进行配置式管理,这样相比直接继承式的数据传递,耦合性更低。
更符合 多组合少继承
的设计理念。
# 基本使用
遵循步骤:
- 创建资源脚本,继承自
UnityEngine.ScriptableObject
- 创建资源对象
- 在其他脚本中以组件形式指定资源对象
我们可以将一些诸如敌人的血量,攻击,防御等数值存储在 ScriptableObject 中。
# 创建资源脚本
[CreateAssetMenu(menuName = "My Assets/Create ItemAssets")] | |
public class ItemAssets : UnityEngine.ScriptableObject | |
{ | |
// 数据 | |
public string title; | |
public int price; | |
public override string ToString() | |
{ | |
return $"title:{title} price:{price}"; | |
} | |
} |
# 指定资源使用
// 使用 | |
public ScriptableObject itemAsset; | |
itemAsset.title; | |
itemAsset.price; |
# 单例资源脚本
在平时我们一般使用 GameManager 实现单例模式并设置在切换场景时不销毁来达到全局数据的管理。
在 ScriptableObject 中,我们同样可以达到这个效果,由于 ScriptableObject 是一个资源可直接存储在磁盘,不需要设置不销毁也能存储全局数据,管理同步。
# 创建单例资源
using System.Linq; | |
using UnityEngine; | |
namespace Assets | |
{ | |
[CreateAssetMenu(menuName = "My Assets/Create GameStateAssets")] | |
public class GameStateAsset : ScriptableObject | |
{ | |
public int level; | |
public int score; | |
private static GameStateAsset _instance; | |
public static GameStateAsset Instance { | |
get | |
{ | |
if (!_instance) | |
{ | |
_instance= Resources.FindObjectsOfTypeAll<GameStateAsset>().FirstOrDefault(); | |
} | |
if (!_instance) | |
{ | |
_instance= CreateInstance<GameStateAsset>(); | |
_instance.hideFlags = HideFlags.DontSave; | |
} | |
return _instance; | |
} | |
} | |
public override string ToString() | |
{ | |
return $"level:{level} score:{score}"; | |
} | |
} | |
} |
# 使用单例资源
GameStateAsset.Instance.level = 8; | |
GameStateAsset.Instance.score = 255; | |
Debug.Log(GameStateAsset.Instance); |
# 临时资源
有时我们需要动态的生成一些对象,这些对象的数据可能并不是静态的设置好的,并且只是临时的使用这些数据,我们可以直接创建临时的 ScriptableObject 来处理这种情况。
var item = ScriptableObject.CreateInstance<ItemAssets>(); | |
item.title = "测试物品"; | |
item.price = 100; | |
Debug.Log(item); | |
ScriptableObject.Destroy(item); |
# 插槽式资源
有时数据是需要被再加工的,加工的过程不应该影响到数据的使用,对于使用数据的一方,不必知晓数据的具体加工过程,这些过程就好像一个个插槽,就等待着期望的数据进入即可。
从这里我们也可以看出,在 ScriptableObject 中是可以包含 方法
的。
# 创建资源
public abstract class PowerUpAsset : ScriptableObject | |
{ | |
public abstract void Add(GameObject go); | |
} |
[CreateAssetMenu(menuName = "My Assets/Create HealthUpAsset")] | |
public class HealthUpAsset : PowerUpAsset | |
{ | |
public int value; | |
public override void Add(GameObject go) | |
{ | |
go.GetComponent<Health>().currHealth += value; | |
} | |
} |
# 使用资源
public class ItemHpUp : MonoBehaviour | |
{ | |
public HealthUpAsset hpAsset; | |
private void OnTriggerEnter(Collider other) | |
{ | |
if (other.CompareTag("Player")) | |
{ | |
hpAsset.Add(other.gameObject); | |
} | |
} | |
} |
# 与 Json 交互
ScriptableObject 和 json 有很高的亲和性(ScriptableObject 就是基于类的延伸)
在某些情况下,我们可能需要将 json 数据存入 ScriptableObject 或将 ScriptableObject 写入 json 来实现一些如游戏存档等的功能。
using System.Collections.Generic; | |
using System.Linq; | |
using UnityEngine; | |
using UnityEngine.Serialization; | |
namespace Assets.TalkJson | |
{ | |
[CreateAssetMenu(menuName = "My Assets/Create GameFileAsset")] | |
public class GameFileAsset : ScriptableObject | |
{ | |
[System.Serializable] | |
public class Archive | |
{ | |
public string DateTime { get; set; } | |
public string Name { get; set; } | |
public int Level { get; set; } | |
} | |
private static GameFileAsset _instance; | |
public static GameFileAsset Instance | |
{ | |
get | |
{ | |
if (!_instance) | |
_instance = Resources.FindObjectsOfTypeAll<GameFileAsset>().FirstOrDefault(); | |
#if UNITY_EDITOR | |
if (!_instance) | |
InitializeFromDefault(UnityEditor.AssetDatabase.LoadAssetAtPath<GameFileAsset>("Assets/Test game file.asset")); | |
#endif | |
return _instance; | |
} | |
} | |
public List<Archive> archives; | |
public Archive currArchive; | |
public static void InitializeFromDefault(GameFileAsset file) | |
{ | |
if (_instance) DestroyImmediate(_instance); | |
_instance = Instantiate(file); | |
_instance.hideFlags = HideFlags.HideAndDontSave; | |
} | |
public static void LoadFromJson(string path) | |
{ | |
if (!_instance) DestroyImmediate(_instance); | |
_instance = ScriptableObject.CreateInstance<GameFileAsset>(); | |
JsonUtility.FromJsonOverwrite(System.IO.File.ReadAllText(path), _instance); | |
_instance.hideFlags = HideFlags.HideAndDontSave; | |
} | |
public void SaveToJson(string path) | |
{ | |
Debug.LogFormat("Saving game file to {0}", path); | |
System.IO.File.WriteAllText(path, JsonUtility.ToJson(this, true)); | |
} | |
} | |
} |
# 个人理解
ScriptableObject 其实就是对类的特殊处理,在 unity 中将这里的类脚本特异性的识别让其数据可视化,并在内部实现了实例化处理,让其可以直接在脚本中使用而无需实例化对象。
[toc]