天天看點

【Unity遊戲開發】更新Unity2019後,資源管線後處理采坑記錄

一、引子

  最近我們的項目由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檔案,也并不是完整的,隻是先生成一個檔案用來占坑,裡面隻有兩行基本資訊,如下圖所示;

【Unity遊戲開發】更新Unity2019後,資源管線後處理采坑記錄

   隻有在走完資源後處理接口以後,其内容才會被正式地補充完整并寫入,如下圖所示:

【Unity遊戲開發】更新Unity2019後,資源管線後處理采坑記錄

   網上有一種比較流行的判斷資源是否是第一次導入的方式,是通過首次導入的時候在 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 這個接口去巧妙地相容判斷資源檔案是否是被第一次導入,解決了上述問題,希望可以幫到大家。

如果覺得本篇部落格對您有幫助,可以掃碼小小地鼓勵下馬三,馬三會寫出更多的好文章,支援微信和支付寶喲!

 

【Unity遊戲開發】更新Unity2019後,資源管線後處理采坑記錄
【Unity遊戲開發】更新Unity2019後,資源管線後處理采坑記錄

作者:馬三小夥兒

出處:https://www.cnblogs.com/msxh/p/13805008.html

請尊重别人的勞動成果,讓分享成為一種美德,歡迎轉載。另外,文章在表述和代碼方面如有不妥之處,歡迎批評指正。留下你的腳印,歡迎評論!

繼續閱讀