本文主要解决在不打开unity的情况下搜索出无效引用的资源的方法
1. 概述
- 一般只要遍历一下目录里所有资源,判空一下就好了
- 但有些情况下,不希望打开unity, 尤其希望是在资源整合时,想更快验证资源的合法性, 这对合并提交及出包验证时,都要较大的需求
2. 简单的验证方法
- 简单来说,要直接分析untiy的资源文件, 而且unity资源大部分是文本文件
- 他们的文本格式大多是YAML, 如果直接使用PyYaml, 目前发现不行, 不过幸好, 格式相对简单, 通过分析属性的名称,再找到属性的内容,方法,可以定位出Guid的值, 例如, 要查找XXX, YYY的值
import osGuidStr = "guid: "
GuidStrLen = len(GuidStr)class CheckAssetInfo:def __init__(self, filePath):self.filePath = filePathself.allGuids = []self.isInit = Falseself.checkValueNames = ["XXX", "YYY"]self.initData()def initData(self):if not self.isInit:self.allGuids = self.get_file_to_guids(self.filePath) self.isInit = Truedef get_all_guids(self):return self.allGuidsdef get_target_value(self, valueName, valueInfos):for i in range(len(valueInfos)):n, j = valueInfos[i]if n == valueName:return ireturn -1def get_file_to_guids(self, targetFilterPath):allGuids = []with open(targetFilterPath, 'r') as assetFile:contentLines = assetFile.readlines()valueInfos = self.get_all_value_index(contentLines)for checkName in self.checkValueNames:valueIndex = self.get_target_value(checkName, valueInfos)if valueIndex != -1:valueName, stline = valueInfos[valueIndex]valueName, endLine = valueInfos[valueIndex+1]allGuids.extend(self.get_value_guids(contentLines, stline, endLine))return allGuidsdef get_all_value_index(self, contentLines):valueInfos = []lineCount = len(contentLines)for i in range(lineCount):oneLine = contentLines[i]findIndex = oneLine.find(":")if findIndex != -1:valueName = oneLine[0:findIndex].strip()if valueName[0].isalpha():valueInfos.append((valueName, i))valueInfos.append(("", lineCount))return valueInfosdef get_value_guids(self, contentLines, startLineIndex, endLineIndex):allGuids = []for i in range(startLineIndex, endLineIndex):oneLine = contentLines[i]guidIndex = oneLine.find(GuidStr)if guidIndex != -1:endIndex = oneLine.find(", ", guidIndex)allGuids.append(oneLine[guidIndex + GuidStrLen: endIndex].strip())return allGuids
- 获得引用的GUID后,还要知道都有什么GUID的资源, 这个比较简单,只要分析meta文件就好, 以下就简单粗暴地遍历资源目录下的meta文件即可
class MetaInfo: def __init__(self, filePath):self.filePath = filePath; self.guid = self.get_guid_from_file(filePath)def get_guid_from_file(self, filePath):with open(filePath, 'r') as metaFile:allLine = metaFile.readlines()for oneLine in allLine:guidIndex = oneLine.find(GuidStr)if guidIndex != -1: return oneLine[guidIndex + GuidStrLen:len(oneLine)].strip()return ""lass VegChecker: def __init__(self, baseDir):self.checkAssetDir = "Assets/checkDir" self.allAssetDir = "Assets"self.allAssetGuids = {}def get_all_target_files(self, targetDir, extName):res = []for root, dirs, files in os.walk(targetDir):for file in files:if os.path.splitext(file)[1] == extName:file_path = os.path.join(root, file)res.append(file_path)return resdef get_all_asset_guids(self):allMetaFiles = self.get_all_veg_files(self.allAssetDir, ".meta")for metaFile in allMetaFiles:metaInfo = MetaInfo(metaFile)self.allAssetGuids[metaInfo.guid] = Trueprint("Get total asset count meta count: " + str(len(self.allAssetGuids)))
- 获得要引用的GUID与所有的资源GUID,就很简单了,发引用的GUID不在所有的资源GUID里就可以了
def check_all_asset(self):res = Trueself.get_all_asset_guids()checkFiles = self.get_all_target_files(self.checkVegDir, ".asset")for oneFilter in checkFiles:assetInfo = CheckAssetInfo(oneFilter)for guid in assetInfo.allGuids:if not self.allAssetGuids.get(guid, False):print(f"fail to find guid {guid} in check asset {oneFilter}")res = Falsereturn res
3.Q&A
- 可能直接分析文件, 而且要大范围遍历文件,会有效率问题, 但实则试验下来, 还是挺快的, 测试验证几十个文件引用, 遍历了4万多个文件, 在i9机器,大概就8秒, 这已经比直接打unity快多了.
- 当然会有分析错误的风险,但如果在一些相对明确的结构上, 也许是安全,但人工维护是免不了的