需求很简单,提供一个工具,找出所有未被引用的资源。提供快捷删除功能。

实现思路:

找出目标路径下的所有资源找出资源的依赖,并添加到依赖列表中读取lua文件内容,并保存到列表中(由于项目是用lua热更新的,配置表最终也是生成lua文件,所以lua文件中会配置了引用资源的名称)判断资源是否被引用(依赖列表中的引用,lua脚本内容列表中的引用)绘制编辑器窗口内容单个删除批量删除同类型资源筛选出某个文件夹中为引用的文件

备注:(首次扫描资源时比较耗时,我这边五千多个资源首次大概耗时:29.7秒,之后都是14.9秒,查了下大致是unity做了缓存);当然不同电脑时间肯定不一致,仅供参考

这里说明一下获取资源依赖API:

第二个参数是是否递归查找引用:我大概试了一下就是,资源A引用了另一个资源B,B引用了资源C,D

false:只会查找到引用了 B

true:会查到到 B C D

当然开启递归后就很耗时,对于该功能也没必要,因为最后还是会遍历到B,C,D。所以如果选了true就会多了很多无用的查找。

AssetDatabase.GetDependencies(info.path,false);

效果:

 

代码实现就是根据上边的思路步骤实现,就不做过多的解释了。

using System;

using System.Collections.Generic;

using System.IO;

using System.Text.RegularExpressions;

using UnityEditor;

using UnityEditor.SceneManagement;

using UnityEditor.UI;

using UnityEngine;

using Object = UnityEngine.Object;

public class NoReferenceWindow:EditorWindow

{

private enum AssetType

{

None,

Prefab,

Material,

Scene,

Asset,

Lua,

Image,

FBX,

Controller,

Animation,

}

private class FileInfo

{

public string path;

public AssetType assetType;

public Object asset;

}

private static string[] ASSET_FILE_ROOT = {"Assets/Resources","Assets/Scene","Assets/StreamingAssets"};

// 过滤文件夹

private static string[] FilterDirectory = {"Assets/Resources/UI/GenAtlas"};

private Dictionary allFileDic = null;

// key:luaPath,value:luaContent

private Dictionary luaFileContentDic = null;

private List assetDependsList = null;

// 未被引用资源

private Dictionary> noReferenceAssetDic = null;

private Vector2 ve2;

private bool initComplete = false;

// 筛选文件夹

private List siftPathList = new List();

// 筛选文件夹中未使用的文件

private Dictionary> noReferenceSiftAssetDic = null;

private bool isAll = true;

private void Awake()

{

siftPathList.Add("Assets/Resources");

}

// 绘制编辑器窗口内容

private void OnGUI()

{

EditorGUILayout.Space();

if (initComplete == false)

{

return;

}

if (noReferenceAssetDic == null)

{

InitAllFileDic();

return;

}

DrawSift();

ve2 = EditorGUILayout.BeginScrollView(ve2);

var tmpNoRefernceDic = noReferenceAssetDic;

if (!isAll)

{

tmpNoRefernceDic = noReferenceSiftAssetDic;

}

foreach (var assetType in tmpNoRefernceDic.Keys)

{

List infos = tmpNoRefernceDic[assetType];

if(infos.Count == 0) continue;

EditorGUILayout.LabelField(assetType.ToString(),EditorStyles.boldLabel);

for (int i = 0; i < infos.Count; i++)

{

CreateNpReferenceItem(infos[i]);

}

if (GUILayout.Button("一键删除",GUILayout.Height(30)))

{

DeleteAllAsset(assetType);

}

EditorGUILayout.Space();

}

EditorGUILayout.EndScrollView();

EditorGUILayout.Space();

}

[MenuItem("Tools/Find no-Ref Assets", false, 10)]

private static void OpenNoReferenceWindow()

{

NoReferenceWindow window = (NoReferenceWindow)EditorWindow.GetWindow(typeof(NoReferenceWindow),true,"未被引用资源列表");

window.InitAllFileDic();

}

public void RefreshInfo()

{

InitAssetDependDic();

InitLuaFileContentDic();

InitNoReferenceAssetDic();

initComplete = true;

}

private bool IsInFilterDirectory(string assetPath)

{

for (int i = 0; i < FilterDirectory.Length; i++)

{

if (assetPath.Contains(FilterDirectory[i]))

{

return true;

}

}

return false;

}

///

/// 找出目标路径下的所有资源

///

private void InitAllFileDic()

{

Debug.Log("开始扫描");

long t = System.DateTime.Now.Ticks;

allFileDic = new Dictionary();

string[] guids = AssetDatabase.FindAssets("",ASSET_FILE_ROOT);

for (int i = 0; i < guids.Length; i++)

{

string path = AssetDatabase.GUIDToAssetPath(guids[i]);

if (EditorUtility.DisplayCancelableProgressBar($"资源加载中:({i}/{guids.Length})", path,

(float) i / (float) guids.Length))

{

EditorUtility.ClearProgressBar();

Close();

return;

}

if(IsInFilterDirectory(path)) continue;

AssetType assetType = GetAssetTypeByPath(path);

if(assetType == AssetType.None) continue;

FileInfo info = new FileInfo();

info.path = path;

info.assetType = assetType;

info.asset = AssetDatabase.LoadAssetAtPath(path);

allFileDic.Add(path,info);

}

EditorUtility.ClearProgressBar();

Debug.Log("资源扫描完成");

RefreshInfo();

Debug.Log($"耗时:{(System.DateTime.Now.Ticks - t)/10000} 毫秒");

}

///

/// 找出资源的依赖,并添加到依赖列表中

///

private void InitAssetDependDic()

{

assetDependsList = new List();

List infos = new List(allFileDic.Values);

if(infos.Count == 0) return;

for (int i = 0; i < infos.Count; i++)

{

FileInfo info = infos[i];

if(info.assetType == AssetType.Lua || info.assetType == AssetType.Image) continue;

if (EditorUtility.DisplayCancelableProgressBar("扫描资源依赖关系...",$"({i}/{infos.Count})\n path:{info.path}",

(float) i / (float) infos.Count))

{

EditorUtility.ClearProgressBar();

Close();

return;

}

string[] depends = AssetDatabase.GetDependencies(info.path,false);

for (int j = 0; j < depends.Length; j++)

{

string dependPath = depends[j];

FileInfo dependInfo = FindFileInfo(dependPath);

// 依赖项为空或者是自己不需要添加

if (dependInfo == null || dependInfo == info) continue;

if (!assetDependsList.Contains(dependInfo))

{

assetDependsList.Add(dependInfo);

}

}

}

Debug.Log("扫描资源依赖关系完成");

EditorUtility.ClearProgressBar();

}

///

/// 读取lua文件内容,并保存到列表中(由于项目是用lua热更新的,配置表最终也是生成lua文件,所以lua文件中会配置了引用资源的名称)

///

private void InitLuaFileContentDic()

{

luaFileContentDic = new Dictionary();

foreach (var info in allFileDic.Values)

{

if (info.assetType == AssetType.Lua)

{

luaFileContentDic.Add(info.path,File.ReadAllText(info.path));

}

}

}

// 初始化未被引用资源

private void InitNoReferenceAssetDic()

{

noReferenceAssetDic = new Dictionary>();

foreach (var info in allFileDic.Values)

{

if(info.assetType == AssetType.Lua) continue;

// 判断资源是否被引用(依赖列表中的引用,lua脚本内容列表中的引用)

// 被其他资源引用

if (CheckOtherAssetReference(info.path)) continue;

// 被lua脚本引用

if (CheckLuaReference(info.path)) continue;

AddNoReferenceAsset(info);

}

Debug.Log("未被引用资源添加完成");

}

private FileInfo FindFileInfo(string assetPath)

{

if (!allFileDic.ContainsKey(assetPath)) return null;

return allFileDic[assetPath];

}

private void AddNoReferenceAsset(FileInfo info)

{

List list = null;

if (!noReferenceAssetDic.TryGetValue(info.assetType, out list))

{

list = new List();

noReferenceAssetDic.Add(info.assetType,list);

}

list.Add(info);

}

private void RemoveNoReferenceAsset(Dictionary> noReferenceAsset,string path)

{

FileInfo info = FindFileInfo(path);

if(info == null) return;

List list = null;

if (noReferenceAsset.TryGetValue(info.assetType, out list))

{

list.Remove(info);

}

}

private AssetType GetAssetTypeByPath(string assetPath)

{

string extension = Path.GetExtension(assetPath);

switch (extension.ToLower())

{

case ".prefab": return AssetType.Prefab;

case ".mat": return AssetType.Material;

case ".unity": return AssetType.Scene;

case ".asset": return AssetType.Asset;

case ".lua": return AssetType.Lua;

case ".png" : return AssetType.Image;

case ".jpg" : return AssetType.Image;

case ".fbx" : return AssetType.FBX;

case ".controller" : return AssetType.Controller;

case ".anim" : return AssetType.Animation;

}

return AssetType.None;

}

///

/// 检查被其他资源引用

///

///

///

private bool CheckOtherAssetReference(string assetPath)

{

if (string.IsNullOrEmpty(assetPath))

{

Debug.LogError("CheckOtherAssetReference error: assetPath is null");

return false;

}

FileInfo info = FindFileInfo(assetPath);

if (info == null) return false;

if (info.assetType == AssetType.Scene)

{

return EditorSceneManager.GetActiveScene().name.Equals(Path.GetFileNameWithoutExtension(assetPath));

}

foreach (var dependInfo in assetDependsList)

{

if (dependInfo == info)

{

return true;

}

}

return false;

}

// 检查被lua脚本引用

private bool CheckLuaReference(string assetPath)

{

string fileName = Path.GetFileNameWithoutExtension(assetPath);

if (string.IsNullOrEmpty(fileName))

{

return false;

}

foreach (var content in luaFileContentDic.Values)

{

if (Regex.IsMatch(content, fileName + "\"") || Regex.IsMatch(content, fileName + "\'"))

{

return true;

}

}

return false;

}

private void DeleteAsset(FileInfo info,bool refresh = false)

{

RemoveDepends(info);

RemoveNoReferenceAsset(noReferenceAssetDic,info.path);

if (!isAll && noReferenceSiftAssetDic != null) RemoveNoReferenceAsset(noReferenceSiftAssetDic,info.path);

allFileDic.Remove(info.path);

AssetDatabase.DeleteAsset(info.path);

if(refresh)

AssetDatabase.Refresh();

}

// 删除资源

private void DeleteAllAsset(AssetType assetType)

{

List infos = noReferenceAssetDic[assetType];

for (int i = infos.Count - 1; i >= 0; i--)

{

DeleteAsset(infos[i]);

}

AssetDatabase.Refresh();

}

// 移除依赖数据

private void RemoveDepends(FileInfo info)

{

if (assetDependsList.Contains(info))

{

assetDependsList.Remove(info);

}

}

// 初始化筛选文件中未被引用资源

private void RefreshNoReferenceSiltAssetDic()

{

noReferenceSiftAssetDic = new Dictionary>();

foreach (var info in allFileDic.Values)

{

if(info.assetType == AssetType.Lua) continue;

if(!CheckInSiltDirectory(info.path)) continue;

// 判断资源是否被引用(依赖列表中的引用,lua脚本内容列表中的引用)

// 被其他资源引用

if (CheckOtherAssetReference(info.path)) continue;

// 被lua脚本引用

if (CheckLuaReference(info.path)) continue;

AddNoReferenceSiltAsset(info);

}

}

private bool CheckInSiltDirectory(string assetPath)

{

for (int i = 0; i < siftPathList.Count; i++)

{

string path = siftPathList[i];

if(string.IsNullOrEmpty(path)) continue;

if (!Directory.Exists(path)) continue;

if (Regex.IsMatch(assetPath, path))

{

return true;

}

}

return false;

}

private void AddNoReferenceSiltAsset(FileInfo info)

{

List list = null;

if (!noReferenceSiftAssetDic.TryGetValue(info.assetType, out list))

{

list = new List();

noReferenceSiftAssetDic.Add(info.assetType,list);

}

list.Add(info);

}

#region // ---------------------- 绘制相关 ------------------------- //

private void CreateNpReferenceItem(FileInfo info)

{

GUILayout.BeginHorizontal();

// var obj = AssetDatabase.LoadAssetAtPath(info.path);

EditorGUILayout.ObjectField(info.asset,typeof(UnityEngine.Object));

// string assetName = Path.GetFileNameWithoutExtension(info.path);

// EditorGUILayout.DelayedTextField(assetName);

// if (GUILayout.Button("定位",GUILayout.Width(70)))

// {

// var obj = AssetDatabase.LoadAssetAtPath(info.path);

// if(obj != null)

// EditorGUIUtility.PingObject(obj);

// }

if (GUILayout.Button("删除资源",GUILayout.Width(70)))

{

DeleteAsset(info,true);

}

GUILayout.EndHorizontal();

}

private void DrawSift()

{

EditorGUILayout.LabelField("筛选列表",EditorStyles.boldLabel,GUILayout.Height(30));

for (int i = 0; i < siftPathList.Count; i++)

{

EditorGUILayout.BeginHorizontal();

EditorGUILayout.LabelField("Path:",GUILayout.Width(50));

siftPathList[i] = EditorGUILayout.TextField(siftPathList[i]);

if (GUILayout.Button("x",GUILayout.Width(20)))

{

siftPathList.RemoveAt(i);

}

EditorGUILayout.EndHorizontal();

}

if (GUILayout.Button("+",GUILayout.Height(20)))

{

siftPathList.Add(string.Empty);

}

EditorGUILayout.BeginHorizontal();

if (GUILayout.Button("筛选资源",GUILayout.Height(40)))

{

string noExistDirectoryStr = "";

for (int i = 0; i < siftPathList.Count; i++)

{

string path = siftPathList[i];

if(string.IsNullOrEmpty(path)) continue;

if (!Directory.Exists(path))

{

noExistDirectoryStr = noExistDirectoryStr + path + "\r\n";

continue;

}

}

if (noExistDirectoryStr != "")

{

EditorUtility.DisplayDialog("提示", "文件夹不存在:\r\n" + noExistDirectoryStr, "ok");

}

RefreshNoReferenceSiltAssetDic();

isAll = false;

}

if (GUILayout.Button("全部资源", GUILayout.Height(40)))

{

isAll = true;

}

EditorGUILayout.EndHorizontal();

EditorGUILayout.Space();

EditorGUILayout.Space();

}

#endregion // ---------------------- 绘制相关 ------------------------- //

}

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: 

发表评论

返回顶部暗黑模式