unitypackage类型文件解压,import,export

77 Lv2

背景

痛点:

在 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";
//需要添加到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"));
//过滤meta文件
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);
}
}

//打包Package 最后三个参数分别是:导出操作将异步运行,在导出完成之后在文件浏览窗口显示导出包文件 | 将通过任何子目录列表递归并包含所有资源 | 除了列出的资产路径,所有相关资产将被包括在内
AssetDatabase.ExportPackage(exportAssets.ToArray(), locationPathName, ExportPackageOptions.Recurse | ExportPackageOptions.IncludeDependencies);
}


  1. 调用Directory.GetFiles去遍历所有的目录,对于.bundle,.framework文件,Directory.GetFiles可以识别为目录,并且找到下面的所有子目录和文件
  2. 但是对于AssetDatabase.LoadAssetAtPath无法将路径中包含.bundle,.framework的文件识别为一个objeject,那么导致添加到 objs 中的都是 null
  3. 从而导致AssetDatabase.GetAssetPath去获取路径的时候无法获取到,最终导致AssetDatabase.ExportPackage导出的unitypackage 中缺少路径中包含 .bundle,.framework的文件

问题解决

  1. 刚开始想看看有没有人遇到类似问题以及解决,但是没发现有人问类似的问题
  2. 然后看了一下 unity 官方 api 文档,也没有针对于 .bundle, .framework 文件的特殊处理
  3. 所以最终决定用最粗暴的把路径名中包含.bundle,.framework的文件,找到.bundle,.framework这一级作为付目录,直接导出.bundle,.framework文件
    即:
    如果路径为abc/def/g.bundle/xxx/xxabc/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
// 辅助函数:提取 .bundle 和 .framework文件的父目录路径
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);
}
// 检查路径中是否包含 .bundle 文件
if (strTempPath.Contains(".bundle"))
{
// 提取 .bundle 文件的父目录
string bundleDirectory = GetAssetDirectory(strTempPath, ".bundle");

// 如果当前已经处理过这个 bundle 目录,就跳过
if (processedBundles.Contains(bundleDirectory))
{
continue;
}

// 标记此 bundle 目录已经处理过
processedBundles.Add(bundleDirectory);
// 加载 .bundle 文件并直接作为一个资源添加
Object asset = AssetDatabase.LoadAssetAtPath(bundleDirectory, typeof(Object));
if (asset != null)
{
objs.Add(asset); // 直接添加整个 .bundle 文件
Debug.Log($"Added .bundle file as asset: {bundleDirectory}");
}
else
{
Debug.LogWarning($"Failed to load .bundle asset: {bundleDirectory}");
}
}
else if (strTempPath.Contains(".framework"))
{
// 提取 .framework 文件的目录
string frameworkDirectory = GetAssetDirectory(strTempPath, ".framework");

// 如果当前已经处理过这个 .framework 目录,就跳过
if (processedBundles.Contains(frameworkDirectory))
{
continue;
}

// 标记此 .framework 目录已经处理过
processedBundles.Add(frameworkDirectory);

// 加载 .framework 文件并直接作为一个资源添加
Object asset = AssetDatabase.LoadAssetAtPath(frameworkDirectory, typeof(Object));
if (asset != null)
{
objs.Add(asset); // 直接添加整个 .framework 文件
Debug.Log($"Added .framework file as asset: {frameworkDirectory}");
}
else
{
Debug.LogWarning($"Failed to load .framework asset: {frameworkDirectory}");
}
}

测试结果

对于以这种方式导出的unitypackage,是否和 unity 编辑器本身的导出方式一致,需要测试

  1. 使用脚本导出文件大小和使用 unity 编辑器的窗口工具导出大小一致
    2025-01-07-18-00-58.png
  2. 使用空工程先后导入两个 package,不存在文件 new 差异
    遇到的问题是即使导入完全一样的 package 两次,.png, .strings, .h, .plist, .nib 这些文件会被再次标记为 new,即使是使用 unity 的 aeests export package 导出的包也是如此,所以可以认为脚本导出的包该行为不存在问题
  3. import 导入脚本导出的项目后测试功能正常

其他tips

本来想通过解压 unitypackage 然后结合 beyond compare 来比较两个包,但是发现解压之后包中的文件过多,以及一些.meta文件导致的差异无法忽略,所以最有效的方式还是分别 import 的形式进行比较。

unitypackage 解压命令

1
tar -xvf abc.unitypackage -C temp
  • 标题: unitypackage类型文件解压,import,export
  • 作者: 77
  • 创建于 : 2025-01-07 17:28:07
  • 更新于 : 2025-01-13 14:06:12
  • 链接: https://www.jiaheqi.cloud/2025/01/07/unitypackage类型文件解压-import-export/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
unitypackage类型文件解压,import,export