概念
ScriptableObject是unity提出的针对数据存储资源的解决方案,能够将数据配置项可视化的展示在unityd属性面板中,并且极易于json进行数据交互。
个人使用过后觉得,其操作方式和继承非常类似,感觉像是对继承进一步封装使其更符合unity开发需要,让数据实体化,具现化的展示,通过拖拽式即可像拆装零件一般自由组合数据。(总而言之是对继承和多态的封装)
使用场景
用于ScriptableObject是针对unity的,在unity使用时比传统的数据格式如json,xml更加方便快捷。而且在面对诸如策划,美工等不了解程序的人员来说,上手更加快捷。
实质上,ScriptableObject主要还是对数据进行存储和处理,只要是使用数据的地方都可以使用ScriptableObject进行配置式管理,这样相比直接继承式的数据传递,耦合性更低。
更符合多组合少继承的设计理念。
基本使用
遵循步骤:
- 创建资源脚本,继承自
UnityEngine.ScriptableObject
- 创建资源对象
- 在其他脚本中以组件形式指定资源对象
我们可以将一些诸如敌人的血量,攻击,防御等数值存储在ScriptableObject中。
创建资源脚本
1 2 3 4 5 6 7 8 9 10 11 12
| [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}"; } }
|
指定资源使用
1 2 3 4
| public ScriptableObject itemAsset; itemAsset.title; itemAsset.price;
|
单例资源脚本
在平时我们一般使用GameManager实现单例模式并设置在切换场景时不销毁来达到全局数据的管理。
在ScriptableObject中,我们同样可以达到这个效果,由于ScriptableObject是一个资源可直接存储在磁盘,不需要设置不销毁也能存储全局数据,管理同步。
创建单例资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| 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}"; } } }
|
使用单例资源
1 2 3
| GameStateAsset.Instance.level = 8; GameStateAsset.Instance.score = 255; Debug.Log(GameStateAsset.Instance);
|
临时资源
有时我们需要动态的生成一些对象,这些对象的数据可能并不是静态的设置好的,并且只是临时的使用这些数据,我们可以直接创建临时的ScriptableObject来处理这种情况。
1 2 3 4 5 6
| var item = ScriptableObject.CreateInstance<ItemAssets>(); item.title = "测试物品"; item.price = 100; Debug.Log(item);
ScriptableObject.Destroy(item);
|
插槽式资源
有时数据是需要被再加工的,加工的过程不应该影响到数据的使用,对于使用数据的一方,不必知晓数据的具体加工过程,这些过程就好像一个个插槽,就等待着期望的数据进入即可。
从这里我们也可以看出,在ScriptableObject中是可以包含方法的。
创建资源
1 2 3 4
| public abstract class PowerUpAsset : ScriptableObject { public abstract void Add(GameObject go); }
|
1 2 3 4 5 6 7 8 9
| [CreateAssetMenu(menuName = "My Assets/Create HealthUpAsset")] public class HealthUpAsset : PowerUpAsset { public int value; public override void Add(GameObject go) { go.GetComponent<Health>().currHealth += value; } }
|
使用资源
1 2 3 4 5 6 7 8 9 10 11
| 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 来实现一些如游戏存档等的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| 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中将这里的类脚本特异性的识别让其数据可视化,并在内部实现了实例化处理,让其可以直接在脚本中使用而无需实例化对象。