Unity ScriptableObject 实例不随资源文件更新的解决方案

Posted 文弱书生陈皮皮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity ScriptableObject 实例不随资源文件更新的解决方案相关的知识,希望对你有一定的参考价值。

问题描述

在 Unity ScriptableObject 的使用过程中,遇到了在 Unity 编辑器内 ScriptableObject 实例没有与资源文件同步更新的情况。

我使用的 Unity 编辑器版本为 2020.2.5f1。

遇到该问题的具体场景:

  • 在系统资源管理器中直接修改 .asset 文件。
  • 通过版本管理工具(如 Git、SVN 等)更新或回退 .asset 文件。

经过以上任意一种操作后,再次回到 Unity 编辑器后会有对应资源的刷新过程,但是实际上 ScriptableObject 实例的数据并没有刷新成功,还是之前的数据。

即使在 Project 面板中 Reimport / Refresh 该资源也无济于事,也有尝试在代码中调用 AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate) 函数来重新导入该资源,结果依然没有任何变化。

问题原因

经过一顿 Google 后,在 Unity 官方论坛中找到了类似问题的帖子。

看来这个问题早在 Unity 5.5 甚至更早的版本中就已经存在,Unity 官方技术人员称在 5.6 beta 版本中修复该问题,然而并没有修复成功~

从帖子中可以得知,该问题在 2020.3.13f1 (LTS) 中依然存在…

所以,目前可以确认这个就是 Unity 编辑器的 Bug 了。

解决方案

经过一次又一次的尝试后,最终还是找到了一个可行的解决方案。

简单来说就是:

  1. 将资源文件和其 .meta 文件移动到项目 Assets 目录之外的任意目录;
  2. 刷新 Unity 资源数据库;
  3. 然后再将文件移动回原来的位置。

具体代码:

public static async void ReimportAsset(Object asset)

  EditorUtility.DisplayProgressBar("正在重新导入资源...", "请稍候...", 1);
  
    // 资源文件路径
    string assetPath = AssetDatabase.GetAssetPath(asset);
    string filePath = Application.dataPath + assetPath.Substring(6);
    string metaPath = filePath + ".meta";
    FileInfo fileInfo = new FileInfo(filePath);
    string dirPath = fileInfo.DirectoryName!.Replace("\\\\", "/");
    // 临时目录路径(项目根目录)
    string tempDirPath = Application.dataPath.Replace("/Assets", "");
    string tempFilePath = filePath.Replace(dirPath, tempDirPath);
    string tempMetaPath = tempFilePath + ".meta";
    // 移动到临时目录,并刷新资源数据库
    File.Move(filePath, tempFilePath);
    File.Move(metaPath, tempMetaPath);
    AssetDatabase.Refresh();
    // 等待一会
    await Task.Delay(100);
    // 移动回项目内原位置,并刷新资源数据库
    File.Move(tempFilePath, filePath);
    File.Move(tempMetaPath, metaPath);
    AssetDatabase.Refresh();
    // 选中资源
    Selection.activeObject = AssetDatabase.LoadAssetAtPath<Object>(assetPath);
  
  EditorUtility.ClearProgressBar();

Unity中使用ScriptableObject存储数据

一、ScriptableObject简介

1.ScriptableObject概述

ScriptableObject类和MonoBehaviour是并列的,都继承自Object,它可以用来存储大量的数据,并且它是可序列化的,这个特点也正决定了它的一个主要用处就是通过将数据存储在ScriptableObject对象中来减少工程以及游戏运行时因拷贝值所造成的内存占用;

2.ScriptableObject特点

  1. 它能够保存和存储数据到本地Assets下的,数据保存以后是可以共享的,就像纹理、shader等资源一样,是可以共享于当前整个工程和其它工程的。
  2. 很少的回调函数

3.ScriptableObject和预制体

在一个游戏场景中我们往往会用到大量的预制体,对于一个预制体如果我们只想修改它部分的值来让它变成一个不同数据的预制体,那么我们在保留原有预制体的情况下就必须新创建一个预制体,这样就会消耗内存空间;相反如果我们把它做成ScriptableObject,那么我们在创建一个新的预制体的时候只需要引用ScriptableObject中的数据即可,然后生成我们想要的物体,并不会产生多余的内存,并且这些数据也会在游戏运行结束后保存。

二、ScriptableObject的使用

1.使用方法

创建一个脚本,脚本名称可以起你想要存储物品类型的名称,点击脚本后开始编辑,注意要继承ScriptableObject类,可以在脚本中定义私有或公有变量用于存储数据。

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

//fileName定义的是创建出来的ScripetableObject的默认命名
//MenuName是在Assers下右键单击后Create后面的选项的名称
//加上‘/’可以设置多个层级,可以定义多个类似于Ttem的脚本放置于同一个根选项下
[CreateAssetMenu(fileName ="New Item",menuName ="Item/new Item")]
public class Items : ScriptableObject //需要继承ScriptableObject类
{
    public string name = "Z_hongli";
    public int number = 123456;
    public int age = 20;
    public int score = 90;
}

创建好后保存,我们可以在Assets下右键单击创建我们定义的ScriptableObject用于存储数据

点击创建好的存储文件我们可以修改其名称,同时也可以在外部修改其内部变量

2.示例演示

我创建了一个Student脚本,同时将创建的ScriptableObject物体改名叫StudentInformation用于存储数据,student脚本如下:

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

public class Student : MonoBehaviour
{
    //创建一个Items类作为保存数据的数据库,这里我们已经创建好了,就是StudentInformation
    public Items thisitems;  

    public string name;
    public int number;
    public int age;
    public int score;
   
    void Start()
    {
        Read_data();
    }

   
    void Update()
    {
        if(Input.GetKeyDown(KeyCode.A))
        {
            score = score + 10;
            Debug.Log("更新后的分数为:"+score);
        }

        if(Input.GetKeyDown(KeyCode.D))
        {
            Save_data();
            Debug.Log("保存成功!");
        }
        
    }
    
    public void Read_data()
    {
        score = thisitems.score;
        Debug.Log("初始分数为:" + score);
    }
    
    public void Save_data()
    {
        thisitems.score = score;
    }
}

回到Unity中创建一个空物体我更名为Student,将Student脚本拖拽给它,同时将我们定义的“数据库”实例化

下面是运行脚本后的测试,可以看见能够成功的保存相关数据

以上是关于Unity ScriptableObject 实例不随资源文件更新的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

csharp 每个实例的ScriptableObject实例的自定义图标;来自https://www.reddit.com/r/Unity3D/comments/3yq4we/custom_icon_

Unity中使用ScriptableObject存储数据

unity怎么调用asset文件

unity, 由scriptableObject创建.asset

Unity进阶:ScriptableObject使用指南

Unity学习使用Json长久储存ScriptableObject数据