【Unity】ScriptableObject
转载:【Unity】ScriptableObject的介绍 —— CSDN
简述
最近对unity提供的ScriptableObject比较感兴趣,想了解一下这方面的技术知识。故此参考网上的学习资料总结一遍。
ScriptableObject的好处
- 将游戏数据存储在了资源文件中,能够在游戏退出后依然保持这一份游戏数据。
- 能依赖继承实现很好的复用关系。
个人觉得第一点是非常好的理念,让游戏的数据存储变得更加的优雅便捷。同时能够实现高复用,来适应多个不同的使用场景。
解决的问题
- 运行时为了保存数据,基本依靠的是Copy Component Value来解决,非常的麻烦,如果存在多个数据想要保存的话基本没办法。这种时候就应该考虑使用ScriptableObject。
- 在场景和项目之间很难实现共享,基本也是要序列化一个新的资源文件来实现。
- 在概念上难以定义这种对象,存储的数据内容不能很好的作区分。扩展性不强。
缺点
- 回调函数较少
- OnEnable
- OnDisable
- OnDestroy
- 真正意义上的共享,因此如果修改了数据,就真的修改了
使用方式
- 继承自MonoBehavior的改成ScriptabObject
using UnityEngine;
[CreateAssetMenu(menuName="MySubMenue/Create MyScriptableObject ")]
public class MyScriptableObject : ScriptableObject
{
public int someVariable;
}
CreateAssetMenu
会在资源菜单创建一个ScriptaObject的选项,方便创建对象。
回调函数的调用时机
- OnEnable
- 创建的时候
- 载入的时候
- 重载代码的完成时候
- OnDisable
- 开始重载代码的时候
- 将要销毁的时候
- OnDestroy
- 销毁时
参考例子——游戏设置
游戏设置继承自ScriptableObject,并实现单例模式。这样子在设置中去调用GetInstance方法可以就可以访问到当前的游戏设置。而且退出游戏时游戏数据也继续保持。
[CreateAssetMenu]
public class GameSettings : ScriptableObject
{
[Serializable]
public class PlayerInfo
{
public string Name;
public Color Color;
...
}
public List<PlayerInfo> players;
private static GameSettings _instance;
public static GameSettings Instance
{
get
{
if (!_instance)
_instance = Resources.FindObjectsOfTypeAll<GameSettings>().FirstOrDefault();
#if UNITY_EDITOR
if (!_instance)
InitializeFromDefault(UnityEditor.AssetDatabase.LoadAssetAtPath<GameSettings>("Assets/Test game settings.asset"));
#endif
return _instance;
}
}
public int NumberOfRounds;
public static void LoadFromJSON(string path)
{
if (!_instance) DestroyImmediate(_instance);
_instance = ScriptableObject.CreateInstance<GameSettings>();
JsonUtility.FromJsonOverwrite(System.IO.File.ReadAllText(path), _instance);
_instance.hideFlags = HideFlags.HideAndDontSave;
}
public void SaveToJSON(string path)
{
Debug.LogFormat("Saving game settings to {0}", path);
System.IO.File.WriteAllText(path, JsonUtility.ToJson(this, true));
}
public static void InitializeFromDefault(GameSettings settings)
{
if (_instance) DestroyImmediate(_instance);
_instance = Instantiate(settings);
_instance.hideFlags = HideFlags.HideAndDontSave;
}
#if UNITY_EDITOR
[UnityEditor.MenuItem("Window/Game Settings")]
public static void ShowGameSettings()
{
UnityEditor.Selection.activeObject = Instance;
}
#endif
...
}
public class MainMenuController : MonoBehaviour
{
public GameSettings GameSettingsTemplate;
...
public string SavedSettingsPath {
get {
return System.IO.Path.Combine(Application.persistentDataPath, "tanks-settings.json");
}
}
void Start () {
if (System.IO.File.Exists(SavedSettingsPath))
GameSettings.LoadFromJSON(SavedSettingsPath);
else
GameSettings.InitializeFromDefault(GameSettingsTemplate);
foreach(var info in GetComponentsInChildren<PlayerInfoController>())
info.Refresh();
NumberOfRoundsSlider.value = GameSettings.Instance.NumberOfRounds;
}
public void Play()
{
GameSettings.Instance.SaveToJSON(SavedSettingsPath);
GameState.CreateFromSettings(GameSettings.Instance);
SceneManager.LoadScene(1, LoadSceneMode.Single);
}
...
}