Unity 之 Scriptable Objects 更直观的序列化

在日常开发中总会在Inspector面板中填写一些序列化信息,如:名称、血量、或者秘钥序列号等(图一)。但看似方便,还是有些缺点的,例如填写的序列化信息不能共享,如果在prefab上对应的脚本丢失,填写的信息也随之丢失。如果需要填写的信息特别多,这绝对是个灾难。

vuforia的SDK 7版本相对于6版本最直观的改进就是原来的配置文件改成了Scriptable Objects。

7643202-9d6895928200ba07.png
图一

而且在【UWA DAY 2018 张鑫大佬 提到过】,使用Scriptable Objects可显著的减少内存的占用,其中的示例是这样的:每个prefab上有1000个public int 变量,实例化100个Prefab ,在红米2中的数据,常规情况下为: 耗时38.44ms 堆内存391.8KB,然后我们使用 Scriptable Objects后对应的数据为,耗时:10.76ms,堆内存1.6KB,耗时占用为原来的 28%,堆内存占用为原来的 0.4%(效果还是很可观的)。


有说的错误或者不准确的地方欢迎留言指正


下面笔者就和大家聊一聊 Scriptable Objects
首先放出官方连接
https://unity3d.com/cn/learn/tutorials/modules/beginner/live-training-archive/scriptable-objects?playlist=17117
https://unity3d.com/cn/learn/tutorials/topics/scripting/ability-system-scriptable-objects?playlist=17117
https://unity3d.com/cn/learn/tutorials/topics/scripting/character-select-system-scriptable-objects?playlist=17117
还有一篇是国内开发者的文章【貌似是冯乐乐,shader入门精要的作者】
https://blog.csdn.net/candycat1992/article/details/52181814

如想更深入了解的Scriptable Objects 可以去Unity在YouTube 的官方网站(自备梯子),里面有几个视频讲解的很详细,不过语速过快,笔者本人已经放弃


要是使用 Scriptable Objects ,首先需要准备一个模板,然后根据指定的模板创建Scriptable Objects。

#region using
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
#endregion

public class PlayerDataScriptableObject : ScriptableObject
{
    [Header("玩家 ID")]
    public int playerId;
    [Header("玩家 分数")]
    public float playerScore;
    [Header("玩家 名称")]
    public string playerName;
    [Header("玩家 地址")]
    public string playerAddress;
    [Header("玩家 随机出生点")]
    public Vector3[] playerSpawnPoint;
    [Header("玩家 描述列表")]
    public List<Description> playerDescription = new List<Description>();

}

[Serializable]
public class Description
{
    public string descriptionOne;
    public string descriptionTwo;
}

注意:PlayerDataScriptableObject 继承ScriptableObject,非传统的MonoBehaviour。Description类需要添加[Serializable]特性,这个PlayerDataScriptableObject 是无法直接拖拽到Prefab等Object物体上的

模板已经创建好,下面我们介绍两种创建 ScriptableObject的方式

第一种

在Editor文件夹中创建ScriptableObjectUtility脚本

using UnityEngine;
using UnityEditor;
using System.IO;

public static class ScriptableObjectUtility
{
    public static void CreateAsset<T>() where T : ScriptableObject
    {
        T asset = ScriptableObject.CreateInstance<T>();

        string path = AssetDatabase.GetAssetPath(Selection.activeObject);
        if (path == "")
        {
            path = "Assets";
        }
        else if (Path.GetExtension(path) != "")
        {
            path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
        }

        string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + "/New " + typeof(T).ToString() + ".asset");

        AssetDatabase.CreateAsset(asset, assetPathAndName);

        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        EditorUtility.FocusProjectWindow();
        Selection.activeObject = asset;
    }
}

public class CreateClassAsset
{
    [MenuItem("Assets/Create/Su9257/ScriptableObject")]
    public static void CreateAsset()
    {
        ScriptableObjectUtility.CreateAsset<PlayerDataScriptableObject>();
    }

}
7643202-f6653cf1ce197c80.png
7643202-35b4e44e7f118f5a.png
第二种 相对比较快捷

直接在对应的模板类上添加CreateAssetMenu特性

7643202-c07ff74c5b2ff132.png
7643202-b30742245fa7ea36.gif

注意:创建的模板类名称要与对应的脚本名称保持一致


上面我们已经创建完对应的ScriptableObject文件,接下来是我们如何使用

创建测试脚本PlayGame 放到任意场景物体上
public class PlayGame : MonoBehaviour
{

    public PlayerDataScriptableObject playerDataScriptableObject;

    void Start()
    {

    }


    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ++playerDataScriptableObject.playerScore;
        }
    }
}
7643202-e74372bd8adbc9c8.gif

然后我们运行,会发现这个.asset序列化文件的好处,在运行模式下产生的数据不会因为退出运行而消失,同一个模板类产生的两个.asset文件不会产生数据干扰,相互独立

7643202-773240cf56b3190e.gif

什么?不想拖拖拽拽?有办法!

把对应的.asset放到Resources文件夹下调用Start方法中的code就可以了(Assetbundle的加载目前还没测试)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayGame : MonoBehaviour
{

    public PlayerDataScriptableObject playerDataScriptableObject;

    void Start()
    {
        playerDataScriptableObject = (Resources.Load<PlayerDataScriptableObject>("New PlayerDataScriptableObject"));

        //克隆的,运行结束时数据也随之消失,换句话就是和原来的New PlayerDataScriptableObject的序列化文件没什么关系了
        //playerDataScriptableObject = Instantiate(Resources.Load<PlayerDataScriptableObject>("New PlayerDataScriptableObject"));
    }


    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ++playerDataScriptableObject.playerScore;
        }
    }
}

这时有一个疑问?这玩应难道仅仅就服务于属性、字段?答案当然不是,ScriptableObject官方文档中都有对应额回调函数,起码也能支持咱们自己写的函数吧~

然后我们把原来的模板稍加改动,改成抽象类然后添加一个抽象方法Attack;,当然,我们的创建方法也需要稍加改动一下。然后调用Attack

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "TestPlayerDataOne", menuName = "Su9257/PlayerDataOne", order = 100)]
public class PlayerDataOne : PlayerDataScriptableObject {
    public override void Attack()
    {
        Debug.Log($"玩家{playerName}进行了攻击");
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayGame : MonoBehaviour
{

    public PlayerDataScriptableObject playerDataScriptableObject;
    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            ++playerDataScriptableObject.playerScore;
        }

        if (Input.GetKeyDown(KeyCode.Alpha1))
        {
            playerDataScriptableObject.Attack();
        }
    }
}

7643202-b0a985dd801fd032.gif
在实际游戏开发中,我们会设置游戏的难度,简单、困难之类的,里面需要配置的东西非常的多,根据这个特点,我们可以根据不同的难度级别配置不同的 .asset序列化文件,用法和这个Attack的用法如出一辙。《炉石传说》对应的配置文件就用过这玩应

最后总结一下优缺点

优点:

  • 可以保存为assets文件
  • 可以在运行时保存
  • 可以引用而不是像MonoBehaviour那样复制
  • 性能非常快
  • 适合插槽式设计,即插即用

缺点:

  • 需要额外的编辑脚本
  • 无法在Unity外编辑

最后,欢迎大家留言补充,非常感谢!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值