Unity 中 prefab 掛載的 monoscript 腳本丟失


一:原因

通過驗證發現,Unity 是通過 meta 文件來索引資源,生成唯一的 guid,僅和具名的相對資源路徑有關,和文件內容無關。

同一目錄下不能存在同名的目錄和文件,因此可以保證生成的 guid 的唯一。

如果存在 monoscript 找不到了的話:

  • 在 Assets 中的可能是相對路徑變更導致,或者腳本被重命名
  • 在 Dll 中的則和打包前所在路徑無關,可能是命名空間或類名變更,還跟 dll 的 guid 相關

 prefab 本身屬於 Unity 資源,之前掛載的配置信息都以 yml 格式的配置格式存儲,因此可以通過腳本批量替換資源的索引來實現。

下面是 prefab 中記錄 monoscript 資源信息的例子:

 

 

 

二:生成原理

假如 monoscript 在 Assets 中時,Unity 只需要通過其 guid 就能做唯一性確定,其 fileID 統一被 Unity 統一為 11400000。

如果在 Dll 中,這時候 fileID 就能排上用場了,上圖中在 m_Script 對應的資源信息中, guid 為 Unity 根據 dll 路徑計算的 guid,fileID 則為根據 dll 中的 (命名空間 + 類名)計算出的唯一標志。

在 UnityEditor 中應該是可以通過:

AssetDatabase.LoadAllAssetsAtPath + AssetDatabase.TryGetGUIDAndLocalFileIdentifier 獲取 guid 和 fileID。

fileID 也可以直接計算:

將 "s\0\0\0" + spacename + classname 作為密鑰通過 csharp 的 MD4 散列化后取前四個字節,然后通過小端序單字節讀取,即 (0<<8)|byte 得到新的 32 位帶符號整數,即為新的 fileID。

大白話就是 MD4 加密后單字節拼接成串,取前四個字符,然后首尾鏡像翻轉,此時再弄成 32 位的二進制,用帶符號的方式讀取,32 位的話也就是 4 Gb 多 。

帶符號讀取也就是說 fileID 取值區間為 負的20多億 到 正的20多億,因此如果有 20多億 個 dll 中的mono類在同一個項目,那 unity 就有一半的機率崩潰。

三:解決方案

知道了 Unity 索引 monscript 的方式,那么就可以通過腳本的方式批量替換 prefab 中舊的索引來達到修復在新工程中使用的目的。

const  repPrefab =  url  =>
read( url)
. map( repMonoScript([ "8f154857129e6754d89d0b85120f3d6d", "1647811386"],[ "10f249957fdaec046913066eefc2e56c", "2108208475"]))
. map( repMonoScript([ "8f154857129e6754d89d0b85120f3d6d", "1829626281"],[ "10f249957fdaec046913066eefc2e56c", "-295734741"]))
. map( repMonoScript([ "8f154857129e6754d89d0b85120f3d6d", "601656639"],[ "ecffc06ff217de3489dc39cc78b7bb2a", "2033745238"]))
. map( repScriptReference)
. map( write( url));
prefbArr. forEach( repPrefab);

這里有個需要注意的問題就是需要一次性成功替換所有缺失的 monoscript , UnityEditor 不會在你每替換成功一個缺失的 script 或屬性后在編輯器中做出正確的呈現。

如果是非 moscript 比如是某些掛在對象上的引用路徑,那么就必須在腳本中預先替換正確,否則 UnityEditor 會自動將其置空,導致信息丟失。

四:思考

假如通過動態掛載腳本,可能就不會產生引用丟失的問題。

但會產生新的問題,因為掛載的類名是在代碼中寫死,也就是說這個 mono 不應當被重命名,不應當被放入某個別的命名空間,否則就必須要改代碼才能解決引用丟失的問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM