unitypackage类型文件解压,import,export
背景
痛点:
在 2019 版本的 unity 编辑器中,会将 iOS 依赖的.bundle
,.framework
文件识别为一个目录的格式,而 2022 版本的 unity 编辑器会将其识别为一个文件,导致现存的 export 的脚本无法正常导出这两种类型的文件,最终生成的 unitypackage 文件中会缺少.bundle
,.framework
解决办法:
查询和 GPT 无果后决定使用最简单的方式,把.bundle
,.framework
文件作为一个文件类型导出。
原因分析
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| string packageName = "ZeusSDK.unitypackage";
string[] dirs = new string[] { "Adjust", "Editor", "ExternalDependencyManager", "GravityEngine", "Resources", "ThinkingAnalytics", "WebGLTemplates", "WX-WASM-SDK-V2" }; locationPathName += "/" + packageName;
List<Object> objs = new List<Object>();
List<string[]> assets = new List<string[]>();
foreach (string dir in dirs) { Debug.Log($"dir:{dir}"); assets.Add(Directory.GetFiles(Application.dataPath + "/" + dir, "*", SearchOption.AllDirectories)); }
for (int i = 0; i < assets.Count; i++) { foreach (string strPath in assets[i]) { string strTempPath = strPath; strTempPath = strTempPath.Substring(strTempPath.IndexOf("Assets")); if (strTempPath.EndsWith(".meta")) { continue; } Object obj = AssetDatabase.LoadAssetAtPath(strTempPath, typeof(Object)); objs.Add(obj); } } var assetPathNames = new string[objs.Count];
for (var i = 0; i < assetPathNames.Length; i++) { assetPathNames[i] = AssetDatabase.GetAssetPath(objs[i]); }
assetPathNames = AssetDatabase.GetDependencies(assetPathNames);
string androidRoot = "Assets/Plugins/Android/";
List<string> excludeFiles = new List<string>{ "Assets/Editor/ExportPackage.cs", androidRoot + "baseProjectTemplate.gradle", androidRoot + "launcherTemplate.gradle", androidRoot + "mainTemplate.gradle", androidRoot + "gradleTemplate.properties", androidRoot + "LauncherManifest.xml", androidRoot + "AndroidManifest.xml", androidRoot + "google-services.json", "Assets/Plugins/iOS/ZeusSDK/ZeusSDKInfo.plist", "Assets/Plugins/iOS/ZeusSDK/GoogleService-Info.plist" };
List<string> exportAssets = new List<string>();
foreach(string assetPath in assetPathNames){ if(!excludeFiles.Contains(assetPath)){ exportAssets.Add(assetPath); } }
AssetDatabase.ExportPackage(exportAssets.ToArray(), locationPathName, ExportPackageOptions.Recurse | ExportPackageOptions.IncludeDependencies); }
|
- 调用
Directory.GetFiles
去遍历所有的目录,对于.bundle
,.framework
文件,Directory.GetFiles
可以识别为目录,并且找到下面的所有子目录和文件
- 但是对于
AssetDatabase.LoadAssetAtPath
无法将路径中包含.bundle
,.framework
的文件识别为一个objeject,那么导致添加到 objs 中的都是 null
- 从而导致
AssetDatabase.GetAssetPath
去获取路径的时候无法获取到,最终导致AssetDatabase.ExportPackage
导出的unitypackage 中缺少路径中包含 .bundle
,.framework
的文件
问题解决
- 刚开始想看看有没有人遇到类似问题以及解决,但是没发现有人问类似的问题
- 然后看了一下 unity 官方 api 文档,也没有针对于 .bundle, .framework 文件的特殊处理
- 所以最终决定用最粗暴的把路径名中包含
.bundle
,.framework
的文件,找到.bundle
,.framework
这一级作为付目录,直接导出.bundle
,.framework
文件
即:
如果路径为abc/def/g.bundle/xxx/xx
,abc/def/g.bundle/xxx
则只导出abc/def/g.bundle
文件,.framework
文件` 同理
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| private static string GetAssetDirectory(string path, string extension) { int extensionIndex = path.IndexOf(extension); if (extensionIndex == -1) { return string.Empty; }
return path.Substring(0, extensionIndex + extension.Length); }
if (strTempPath.Contains(".bundle")) { string bundleDirectory = GetAssetDirectory(strTempPath, ".bundle");
if (processedBundles.Contains(bundleDirectory)) { continue; }
processedBundles.Add(bundleDirectory); Object asset = AssetDatabase.LoadAssetAtPath(bundleDirectory, typeof(Object)); if (asset != null) { objs.Add(asset); Debug.Log($"Added .bundle file as asset: {bundleDirectory}"); } else { Debug.LogWarning($"Failed to load .bundle asset: {bundleDirectory}"); } } else if (strTempPath.Contains(".framework")) { string frameworkDirectory = GetAssetDirectory(strTempPath, ".framework");
if (processedBundles.Contains(frameworkDirectory)) { continue; }
processedBundles.Add(frameworkDirectory);
Object asset = AssetDatabase.LoadAssetAtPath(frameworkDirectory, typeof(Object)); if (asset != null) { objs.Add(asset); Debug.Log($"Added .framework file as asset: {frameworkDirectory}"); } else { Debug.LogWarning($"Failed to load .framework asset: {frameworkDirectory}"); } }
|
测试结果
对于以这种方式导出的unitypackage,是否和 unity 编辑器本身的导出方式一致,需要测试
- 使用脚本导出文件大小和使用 unity 编辑器的窗口工具导出大小一致

- 使用空工程先后导入两个 package,不存在文件 new 差异
遇到的问题是即使导入完全一样的 package 两次,.png, .strings, .h, .plist, .nib 这些文件会被再次标记为 new,即使是使用 unity 的 aeests export package 导出的包也是如此,所以可以认为脚本导出的包该行为不存在问题
- import 导入脚本导出的项目后测试功能正常
其他tips
本来想通过解压 unitypackage 然后结合 beyond compare 来比较两个包,但是发现解压之后包中的文件过多,以及一些.meta
文件导致的差异无法忽略,所以最有效的方式还是分别 import 的形式进行比较。
unitypackage 解压命令
1
| tar -xvf abc.unitypackage -C temp
|