Arnoya

loading...

ScriptableObject在Unity项目中的使用

创建时间2023-12-08
修改时间2023-12-27
657次阅读

前言

ScriptableObject 是一个可独立于类实例来保存大量数据的数据容器。它的一个主要用例是通过避免重复值来减少项目的内存使用量。

ScriptableObject 派生自基本 Unity 对象,但与 MonoBehaviour 不同,不能将它附加到游戏对象。正确的做法是需要将它们保存为项目中的资源。

简而言之,ScriptableObject是一种Unity提供可自定义的一种数据结构,使用ScriptableObject创建出的文件通常保留在项目中,成为一种本地数据

使用情景

一、游戏存档

如上所言,作为本地的数据,是可以通过最初的结构设计上使其能够进行修改和创建的,以下为实现的一个简单演示案例。

1.创建ScriptableObject子类

这里我创建了一个名为ArchiveSO的类对象用于继承ScriptableObject

using System.Collections.Generic;
using UnityEngine;

// 声明使用CreateAssetMenu,之后我们会在右键创建菜单栏里实例出ArchiveSO,其实例结构默认名字为fileName指定的名字
[CreateAssetMenu(fileName = "LoadData", menuName = "Load/LoadFiles", order = 1)]
public class ArchiveSO : ScriptableObject
{
    // 存档通常以多个存档形式存在,而这里我们使用list对其进行填充
    public List<LoadFile> loadFileList = new List<LoadFile>();
}

/// 假定LoadFile为游戏存档所需的基本信息,使其序列化(这能更好进行手动添加或者移除所实例出来的对象)
[System.Serializable]
public class LoadFile
{
    // 游戏存档所需的基本配置,这里模拟当前等级,血量,和位置
    public int currentLevel;
    public float currentHP;
    public Vector3 location;

    // 自定义构造函数
    public LoadFile(int currentLevel, float currentHp, Vector3 location)
    {
        this.currentLevel = currentLevel;
        currentHP = currentHp;
        this.location = location;
    }
}

右键->创建->load->loadFiles因为我的unity有汉语包因此load被翻译成加载,其并不会影响导向,但是还是建议有个很好的区分名

其后我们可以在本地进行存档的手动填充和修改

2.创建存档管理脚本

我创建了LoadManager脚本以控制基本存档的新增加载删除修改

using UnityEngine;

public class LoadManager : MonoBehaviour
{
    // 模拟创建新开始所需参数
    private int leve=1;
    private float health=100;
    private Vector3 location = new Vector3(0, 0, 0);
    // ArchiveSTB声明
    public ArchiveSO archiveSo;
    
    // 新游戏,自动创建一个新存档
    public void NewGame()
    {
        LoadFile newLoadFile = new LoadFile(leve,health,location);
        archiveSo.loadFileList.Add(newLoadFile);
        Debug.Log("新游戏存档创建成功");
        
    }
    
    // 加载游戏,默认加载存档最新
    public void LoadGame()
    {
        // 检查是否有最新文档,没有则,创建新文档
        if (archiveSo.loadFileList.Count != 0)
        {
            Debug.Log("当前等级:" + archiveSo.loadFileList[^1].currentLevel);
            Debug.Log("当前血量:" + archiveSo.loadFileList[^1].currentHP);
            Debug.Log("当前位置:" + archiveSo.loadFileList[^1].location);
        }else NewGame();
    }
    
    // 删除存档,默认删除最后一条存档
    public void DelLoad()
    {
        // 判定是否有可用存档
        if (archiveSo.loadFileList.Count != 0)
        {
            archiveSo.loadFileList.RemoveAt(archiveSo.loadFileList.Count - 1);
        }
    }
    
    // 修改存档,默认修改最新存档等级使其变成Lv:10
    public void ModifyLoad()
    {
        archiveSo.loadFileList[^1].currentLevel = 10;
        Debug.Log("当前等级已修改成:"+archiveSo.loadFileList[^1].currentLevel);
    }
}

3.基本绑定与调试

https://www.bilibili.com/video/BV1xN4y1s74B/?share_source=copy_web&vd_source=794e5b0006ca32bba80baa96a95ca3b3

二、背包系统1

如存档系统相近,需要设定好背包物品的存储信息,之后再进行相关的管理脚本设定,以下是一个简单的背包内物品使用ScriptableObject方式来管理背包物品和查看物品信息.

1. 基本UI布局

布局草图如下,对应颜色为:
黑色:bg
橙色:Left
红色:Scroll View
蓝色:Right
灰色:Item Icon
绿色:Info Area
黄色:Btn

效果和层级图如下:

ps:通常而言item项多是预制体通过代码动态创建,而在此处之为了方便演示选择手动填充相应数量的item项

2. 创建ScriptableObject子类

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

[CreateAssetMenu(fileName = "BagItemsData", menuName = "BagItems/BagItem", order = 2)]
public class BagItemsSO : ScriptableObject
{
    public List<BagItem> bagItemList = new List<BagItem>();
}

// 背包物品类,用于定义背包物品的基本属性,其中这里演示包含所需的图标、是否为装备
[System.Serializable]
public class BagItem
{
    public Sprite itemIcon;
    public string itemName;
    [TextArea]
    public String itemInfo;
    public bool isEquipment;
}

创建一个名为BagItemsSOScriptableObject子类并同上述存档操作一致,创建出相应的可序列化数据结构,并进行相关配置

3. 创建管理逻辑脚本

1. Item物品类脚本
using UnityEngine;
using UnityEngine.UI;

public class Item : MonoBehaviour
{
    // 获取Toggle组件
    private Toggle _toggle;
    // 公开基本物品参数,
    public Image itemIcon;
    public Text itemName;
    public string itemInfo;
    public bool isEquipment;

    // 展示面板信息绑定
    public Image showIcon;
    public Text showInfo;
    public Text showTip;

    private void Start()
    {
        _toggle = GetComponent<Toggle>();
        _toggle.onValueChanged.AddListener(b => UpdateInfoPanel());
    }

    // 更新信息面板函数
    public void UpdateInfoPanel()
    {
        showIcon.sprite = itemIcon.sprite;
        showInfo.text = itemInfo;
        showTip.text = isEquipment ? "装备" : "使用";
    }

}

Item脚本由于挂载在Item物品类对象上,定义了基本的物品信息和展示界面相关的GameObject组件,用于之后的点击互动展示

2. BagItems背包物品类脚本
using UnityEngine;

public class BagItems : MonoBehaviour
{
    public BagItemsSO bagItemsSo;
    private Item _itemScript;
    private void Start()
    {
        // 更新填充Item脚本的属性
        for (int i = 0; i < transform.childCount; i++)
        {
            _itemScript = transform.GetChild(i).GetComponent<Item>();
            _itemScript.itemIcon.sprite = bagItemsSo.bagItemList[i].itemIcon;
            _itemScript.itemName.text = bagItemsSo.bagItemList[i].itemName;
            _itemScript.itemInfo = bagItemsSo.bagItemList[i].itemInfo;
            _itemScript.isEquipment = bagItemsSo.bagItemList[i].isEquipment;
        }
    }
}

BagItems脚本挂载在Scroll View下的Content

4. 基本绑定于调试

https://www.bilibili.com/video/BV1xN4y1s74B/?share_source=copy_web&vd_source=794e5b0006ca32bba80baa96a95ca3b3