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)入的大致順序如下:
- Unity不處理的各種資源:.ai、.exe、.plist、mdb等
- Texture
- Material
- FBX
- asset,也就是ScriptableObject
- 各種文本文件:.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è)試下來一切正常,狀況好的令人不敢相信。