[RS] 項(xiàng)目重新導(dǎo)入時(shí),加載不到ScriptableObject

1、環(huán)境

  • Unity 2021.3.8f1

2、問題背景

??我們?yōu)榱俗隹膳渲玫馁Y源導(dǎo)入設(shè)置,使用ScriptableObject進(jìn)行規(guī)則的配置。AssetPostprocessor里處理資源導(dǎo)入時(shí),會(huì)查找項(xiàng)目中的ScriptableObject配置文件,然后使用里面的規(guī)則對(duì)每個(gè)資源進(jìn)行Importer的設(shè)置。
??但是我們發(fā)現(xiàn)當(dāng)打開一個(gè)全新的工程(無Library)時(shí),格式化沒有生效。經(jīng)過定位發(fā)現(xiàn),在導(dǎo)入資源時(shí),ScriptableObject的配置加載不到!??!

[RS] Unity問題記錄里的第3個(gè)問題很類似.

3、原因

??該問題的本質(zhì),其實(shí)還是Unity資源導(dǎo)入順序(這里是純Asset階段的順序)。ScriptableObject在我們需要設(shè)置導(dǎo)入格式的資源之后才會(huì)導(dǎo)入,所以第一次使用時(shí)加載不到它。

測(cè)試下來,資源導(dǎo)入的大致順序如下:

  1. Unity不處理的各種資源:.ai、.exe、.plist、mdb等
  2. Texture
  3. Material
  4. FBX
  5. asset,也就是ScriptableObject
  6. 各種文本文件:.md、.json等

??這個(gè)導(dǎo)入順序雖然給我的使用帶來了問題,但其實(shí)它的設(shè)計(jì)是很合理的。我們只看2~5步的話,它完全是按照依賴走的:

  • Texture沒有依賴,自己可以獨(dú)自導(dǎo)入完成;
  • Material依賴Texture,需要Texture導(dǎo)入好,Material的引用關(guān)系才能正確;
  • FBX在導(dǎo)入材質(zhì)的情況下,會(huì)依賴Material;
  • ScriptableObject根據(jù)用戶寫的代碼,可以引用上面的所有資源類型

4、解決

??技術(shù)群、百度、Bing、ILSpy到處搞,折騰了大概一天時(shí)間終于發(fā)現(xiàn)還是有搞頭的。解決方法有兩個(gè),也可以是兩個(gè)階段,可以按需選擇。

4.1 不經(jīng)過AssetDatabase導(dǎo)入,直接加載資源對(duì)象

??用下面的方式,可以直接在文件系統(tǒng)中,加載到需要的序列化對(duì)象。其返回值可以直接用,也只能直接用。因?yàn)檫@里加載到的對(duì)象,并不會(huì)被AssetDatabase記錄、識(shí)別。也就是說,用該方式加載到對(duì)象后,再用AssetDatabase查找也還是找不到(除非它走了正常的導(dǎo)入)。

Object[] assets = InternalEditorUtility.LoadSerializedFileAndForget(assetPath);
foreach (Object asset in assets)
{
    T rst = asset as T;
    if (null != rst) return rst;
}

??這個(gè)方法是在ILSpy里搜AssetDatabase時(shí),偶然看到ScriptableObjectSaveLoadHelper<T>帶來的啟發(fā)。ScriptableSingleton<T>內(nèi)部也是用的這個(gè)方式 (我是先測(cè)試了ScriptableSingleton<T>可以在導(dǎo)入前加載到對(duì)象)。

4.2 借助ScriptedImporter將導(dǎo)入asset的順序提前

??該方式是覆蓋Unity默認(rèn)的asset導(dǎo)入處理,并將其執(zhí)行的優(yōu)先級(jí)提高。
??其優(yōu)點(diǎn)是,經(jīng)過處理后ScriptableObject可以保持之前的用法,不用關(guān)心順序問題,與特殊的加載方式。缺點(diǎn)是,處理方式有點(diǎn)繞,都是些特殊邏輯。

[ScriptedImporter(1, null, new string[] {"asset"}, -3000)]
public class WKScriptedImporter : ScriptedImporter
{
    public override void OnImportAsset(AssetImportContext ctx)
    {
        string ap = ctx.assetPath;
        Object[] assets = InternalEditorUtility.LoadSerializedFileAndForget(assetPath);
        foreach (Object asset in assets)
        {
            ScriptableObject so = asset as ScriptableObject;
            if (null != so)
            {
                ctx.AddObjectToAsset("asset", so);
                ctx.SetMainObject(so);
                break;
            }
        }
    }
}
[InitializeOnLoad]
public class ImportProcess : AssetPostprocessor
{
    static string OptAssetPath = "Assets/ABBuildOption.asset";

    static ImportProcess()
    {
        // 先注冊(cè)自己的導(dǎo)入處理腳本
        AssetDatabase.SetImporterOverride<WKScriptedImporter>(OptAssetPath);
    }

    static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets,
        string[] movedAssets, string[] movedFromAssetPaths)
    {
        // 第一批導(dǎo)入處理完成后清空,以后導(dǎo)入都走Unity默認(rèn)的
        AssetDatabase.ClearImporterOverride(OptAssetPath);
    }
}

??ScriptedImporter其實(shí)是最早在網(wǎng)上搜到的方式,不過網(wǎng)上的文章普遍是說它不能處理Unity內(nèi)置已經(jīng)處理的資源類型。雖然我自己看接口發(fā)現(xiàn)它現(xiàn)在支持了Override方式,但沒找到自己加載ScriptableObject對(duì)象的方法,直到發(fā)現(xiàn)了LoadSerializedFileAndForget,幾個(gè)問題一起解決了。
??還有一個(gè)令人疑惑的地方,按理說這種將asset導(dǎo)入提到最前的方式,會(huì)讓依賴其他資源的asset出問題,因?yàn)槠渌Y源還沒有被導(dǎo)入。但,測(cè)試下來一切正常,狀況好的令人不敢相信。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容