通过验证发现,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 不应当被重命名,不应当被放入某个别的命名空间,否则就必须要改代码才能解决引用丢失的问题。