南锋

南奔万里空,脱死锋镝余

Cocos Creator找出未用到的图片

最近整理项目的时候发现有些资源文件夹有点轮乱(一些历史原因导致的),而且有很多图片都是没用了的,但是没有被删除掉,还一直放在项目中,导致项目的资源文件夹比较大,而且还冗余。于是今天想着整理一下。

找出未使用的图片

有好几种方法可以找出未使用的图片,找到未使用的扸后,可以选择将其从项目种删除,以减少项目的大小并简化资源管理。

方法一 资源管理器检查

在Cocos Creator的资源管理器中选择你要查看的图片,右键,选择查看uuid的引用,查看图片的引用,如果没有任何场景或者预制引用这张图片,然后查看代码中有没有引用。如果都没有引用,就说明这张图片是未被使用的。这个时候就可以删除这张照片了。

方法二 写一个脚本去帮我们查询

如果我们项目的资源图片比较多,这个时候再用方法一就显得比较“蠢”,毕竟作为一个程序员,这种重复且费时间的事情可以交给程序去做。
这里我写了一个js脚本来帮我找出未使用的图片,代码如下:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
const fs = require('fs');
const path = require('path');

// 项目资源目录
const assetsDir = path.join(project_dir, 'assets');

function getAllImageFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
getAllImageFiles(filePath, fileList);
} else if (filePath.endsWith('.png') || filePath.endsWith('.jpg')) {
fileList.push(filePath);
}
});
return fileList;
}

// 获取所有预制体文件路径
function getAllPrefabFiles(dir, fileList = []) {
const files = fs.readdirSync(dir);
files.forEach(file => {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
getAllPrefabFiles(filePath, fileList);
} else if (filePath.endsWith('.prefab')) {
fileList.push(filePath);
}
});
return fileList;
}

// 解析预制体文件,获取引用的图片UUID
function getReferencedImageUUIDs(prefabFiles) {
const referencedUUIDs = new Set();
prefabFiles.forEach(file => {
try {
const content = fs.readFileSync(file, 'utf8');
const regex = /"__uuid__": "([a-fA-F0-9-@]+)"/g;
let match;
while ((match = regex.exec(content)) !== null) {
referencedUUIDs.add(match[1]);
}
} catch (error) {
console.error(`读取预制体文件 ${file} 时出错:`, error);
}
});
return referencedUUIDs;
}

// 获取图片文件的UUID
function getImageUUID(filePath) {
const metaPath = filePath + '.meta';
try {
const metaContent = JSON.parse(fs.readFileSync(metaPath, 'utf8'));
return metaContent.uuid;
} catch (error) {
console.error(`读取元数据文件 ${metaPath} 时出错:`, error);
return null;
}
}

// 获取未被引用的图片
function getUnreferencedImages(imageFiles, referencedUUIDs) {
const unreferencedImages = [];
imageFiles.forEach(file => {
const uuid = getImageUUID(file)+"@f9941";
if (uuid && !referencedUUIDs.has(uuid)) {
unreferencedImages.push(file);
}
});
return unreferencedImages;
}

try {
const imageFiles = getAllImageFiles(assetsDir);
const prefabFiles = getAllPrefabFiles(assetsDir);
const referencedUUIDs = getReferencedImageUUIDs(prefabFiles);
const unreferencedImages = getUnreferencedImages(imageFiles, referencedUUIDs);

const outputFile = path.join(project_dir, 'unreferenced_images.txt');
fs.writeFileSync(outputFile, '未被引用的图片:\n');
unreferencedImages.forEach(image => {
fs.appendFileSync(outputFile, image + '\n');
});
console.log(`未被引用的图片列表已写入到 ${outputFile}`);
} catch (error) {
console.error('处理过程中出错:', error);
}

使用说明:
直接复制粘贴代码是肯定不行的,需要注意下面几点:
1、将代码中的project_dir改成你自己的项目根路径;
2、我这里所有的节点都是用的预制体的形式,所以我只需要遍历预制即可。如果你项目中的scene,在使用的时候建议遍历scene文件,只需要将代码中的filePath.endsWith('.prefab')改为filePath.endsWith('.scene')即可。
3、关于获取__uuid__时的正则表达式,因为我发现我预制文件中图片的uuid会有一个@,所以我这里的正则表达式是这样的const regex = /"__uuid__": "([a-fA-F0-9-]+)"/g;,如果你的uuid没有@,就需要修改这里的正则,去掉前面正则中的@即可。
4、在行代码中const uuid = getImageUUID(file)+"@f9941";,我在最后加了个+"@f9941",是因为我资源文件夹中图片的uuid没有@f9941,而预制中所有图片的uuid后面都会有一个@f9941,所以为了判断图片的uuid是否在预制中,就在获取到的图片uuid后面加了这行字符串。注意,这里你不一定需要加,或者加的字符串和我的不一样,修改自行修改。否则可能发生找出的未使用图片是你资源文件夹下的所有图片的情况。
5、运行代码node demo.js,就会在你指定的路径下生成一个名为unreferenced_images.txt文件,里面详细写着未被使用的图片路径。

脚本优化

我这里只是简单的找出了未被预制引用的图片,但是这些图片可能在代码中被引用,所以找到这些图片后还不能简单粗暴的直接删除,还得查看代码中是否有使用,如果有使用则不能删除。
所以我们在遍历的时候不能仅仅只遍历预制,最好能够将自己的代码也遍历一遍,看图片是否被代码直接引用。

+