一、引子
最近我們的項目由Unity2018升級到了Unity2019.4,但是突然間發現FBX資源導入時的后處理不生效了。經過一系列的實驗,發現了升級到Unity2019以后,資源管線后處理中的一些坑,今天馬三來和大家分享一下這個過程。
二、情況復現與原因排查
在我們的項目還使用Unity2018開發的時候,便有一個資源后處理的Editor代碼,負責處理fbx類型文件導入時的一些自動化配置,比如:壓縮動畫曲線、優化模型的網格,關閉模型的Read/Write Enable選項等操作。同時這個自動化處理操作是只在資源第一次被導入的時候才會運行的,也就是說只有一個新的FBX剛進來的時候才會自動配置,在這之后我們還可以手動去調整一些參數。因此有了一步判斷資源有沒有被自動化后處理過的過程,用的是判斷fbx對應的.meta文件存在與否,來指示這個fbx是否是被首次處理,代碼如下所示:
1 using System.Collections; 2 using System.Collections.Generic; 3 using UnityEngine; 4 using UnityEditor; 5 using System.IO; 6 7 public class FBXPostProcesser : AssetPostprocessor 8 { 9 #region 模型處理 10 /// <summary> 11 /// 模型導入之前調用 12 /// </summary> 13 public void OnPreprocessModel() 14 { 15 16 //判斷資源是否是首次導入 17 #if UNITY_2019_3_OR_NEWER 18 if (!assetImporter.importSettingsMissing) 19 { 20 assetImporter.userData = "v_0.0.1"; 21 return; 22 } 23 #else 24 var metaPath = this.assetPath + ".meta"; 25 if (File.Exists(metaPath)) 26 { 27 return; 28 } 29 #endif 30 31 Debug.Log("==模型導入之前調用==" + this.assetPath); 32 ModelImporter modelImporter = (ModelImporter)assetImporter; 33 34 //模型優化 35 modelImporter.optimizeMesh = true; 36 modelImporter.optimizeGameObjects = true; 37 modelImporter.animationCompression = ModelImporterAnimationCompression.Optimal; 38 modelImporter.animationRotationError = 1.0f; 39 modelImporter.animationPositionError = 1.0f; 40 modelImporter.animationScaleError = 1.0f; 41 } 42 43 44 /// <summary> 45 /// 模型導入之后調用 46 /// </summary> 47 /// <param name="go"></param> 48 public void OnPostprocessModel(GameObject go) 49 { 50 //判斷資源是否是首次導入 51 #if UNITY_2019_3_OR_NEWER 52 if (!assetImporter.importSettingsMissing) 53 { 54 return; 55 } 56 #else 57 var metaPath = this.assetPath + ".meta"; 58 if (File.Exists(metaPath)) 59 { 60 return; 61 } 62 #endif 63 64 // for skeleton animations. 65 Debug.Log("==模型導入之后調用=="); 66 List<AnimationClip> animationClipList = new List<AnimationClip>(AnimationUtility.GetAnimationClips(go)); 67 if (animationClipList.Count == 0) 68 { 69 AnimationClip[] objectList = Object.FindObjectsOfType(typeof(AnimationClip)) as AnimationClip[]; 70 animationClipList.AddRange(objectList); 71 } 72 73 foreach (AnimationClip theAnimation in animationClipList) 74 { 75 try 76 { 77 // 去除scale曲線 78 //foreach (EditorCurveBinding theCurveBinding in AnimationUtility.GetCurveBindings(theAnimation)) 79 //{ 80 // string name = theCurveBinding.propertyName.ToLower(); 81 // if (name.Contains("scale")) 82 // { 83 // AnimationUtility.SetEditorCurve(theAnimation, theCurveBinding, null); 84 // } 85 //} 86 87 // 浮點數精度壓縮到f3 88 AnimationClipCurveData[] curves = null; 89 curves = AnimationUtility.GetAllCurves(theAnimation); 90 Keyframe key; 91 Keyframe[] keyFrames; 92 for (int ii = 0; ii < curves.Length; ++ii) 93 { 94 AnimationClipCurveData curveDate = curves[ii]; 95 if (curveDate.curve == null || curveDate.curve.keys == null) 96 { 97 //Debuger.LogWarning(string.Format("AnimationClipCurveData {0} don't have curve; Animation name {1} ", curveDate, animationPath)); 98 continue; 99 } 100 keyFrames = curveDate.curve.keys; 101 for (int i = 0; i < keyFrames.Length; i++) 102 { 103 key = keyFrames[i]; 104 key.value = float.Parse(key.value.ToString("f3")); 105 key.inTangent = float.Parse(key.inTangent.ToString("f3")); 106 key.outTangent = float.Parse(key.outTangent.ToString("f3")); 107 keyFrames[i] = key; 108 } 109 curveDate.curve.keys = keyFrames; 110 theAnimation.SetCurve(curveDate.path, curveDate.type, curveDate.propertyName, curveDate.curve); 111 } 112 } 113 catch (System.Exception e) 114 { 115 Debug.LogError(string.Format("CompressAnimationClip Failed !!! animationPath : {0} error: {1}", assetPath, e)); 116 } 117 } 118 } 119 #endregion 120 121 }
在Unity2018時,以上的判斷方法可以完好地運行,但是升級到Unity2019以后,就不能再通過.meta文件存在與否來判斷一個fbx是否是第一次被導入了。因為Unity2019.3以后資源后處理管線也由AssetPipline v1升級到了Asset Pipline v2,同時Unity生成.meta和調用資源后處理接口(比如 OnPreprocessModel()、 OnPostprocessModel(GameObject go)這些回調接口)的時序也發生了變化。在Unity2018中,一個資源被導入的時候,會先去調用資源后處理接口,然后再生成.meta文件,因此可以通過.meta文件存在與否來判斷一個fbx是否是第一次被導入。但是在Unity2019中,這個時序變成了先生成一個.meta文件,然后再去調用資源后處理接口。如果此時還通過.meta文件來判斷一個資源是否是被第一次導入的話,就會造成程序認為這個新導入的fbx之前是被處理過的,就不會走到資源自動處理的那部分代碼了,因此也就會表現為資源后處理機制失效了。同時Unity2019中先於資源后處理回調接口生成的這個.meta文件,也並不是完整的,只是先生成一個文件用來占坑,里面只有兩行基本信息,如下圖所示;
只有在走完資源后處理接口以后,其內容才會被正式地補充完整並寫入,如下圖所示:
網上有一種比較流行的判斷資源是否是第一次導入的方式,是通過首次導入的時候在 assetImporter.userData 中寫入一個標記,然后后續通過判斷是否可以讀到這個標記來區分資源是否是首次導入,因為如果以前導入過一次了,那么一定會讀取到userdata的,其代碼類似於這樣:assetImporter.userData = "v_0.0.1"; ,這個userdata數據會存在資源對應的.meta文件中。
但是我們的項目之前沒有加寫入userdata這一步,userdata是讀取不到的。一種比較簡單粗暴的解決辦法就是寫一個工具,遍歷每個fbx資源,然后把userdata寫入到他們的.meta文件中,這樣就批量地完成了資源meta的升級,新舊資源就可以通過userdata來進行區分了。
但是這樣做比較拙劣的問題是,項目中資源很多,批量處理起來可能會有一定的風險,那么有沒有比較優雅且安全系數較高的解決方案呢?其實Unity為我們提供了一個 assetImporter.importSettingsMissing 的接口,他完全可以實現我們的需求。這個接口可以判斷一個資源對應的配置是否存在,對於一個新導入的資源,其配置肯定是不存在的,因此要執行資源后處理代碼;而對於一個已經導入過了的資源,其配置肯定是存在的,所以直接跳過不處理。經過馬三的測驗,它可以良好地在Unity2018和2019上工作,通過使用這個接口來判斷資源是否是被第一次導入,就可以巧妙的解決上述問題了。同時不用批量的去處理每一個資源對應的meta,不但優雅也降低了風險系數,是一個比較好的兼容方式。
三、總結
在本篇博客中,馬三跟大家分享了從Unity2018升級到Unity2019以后,資源導入管線遇到的小問題。一個是Unity2018和Unity2019生成.meta和調用資源后處理回調的時序發生了變化,因此不能再通過判斷.meta文件是否存在來標記一個資源是否是被第一次導入;另一個是通過 assetImporter.importSettingsMissing 這個接口去巧妙地兼容判斷資源文件是否是被第一次導入,解決了上述問題,希望可以幫到大家。
如果覺得本篇博客對您有幫助,可以掃碼小小地鼓勵下馬三,馬三會寫出更多的好文章,支持微信和支付寶喲!
作者:馬三小伙兒
出處:https://www.cnblogs.com/msxh/p/13805008.html
請尊重別人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和代碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!