本文主要解决在不打开unity的情况下搜索出无效引用的资源的方法
1. 概述
- 一般只要遍历一下目录里所有资源,判空一下就好了
- 但有些情况下,不希望打开unity, 尤其希望是在资源整合时,想更快验证资源的合法性, 这对合并提交及出包验证时,都要较大的需求
2. 简单的验证方法
- 简单来说,要直接分析untiy的资源文件, 而且unity资源大部分是文本文件
- 他们的文本格式大多是YAML, 如果直接使用PyYaml, 目前发现不行, 不过幸好, 格式相对简单, 通过分析属性的名称,再找到属性内容的方法,可以定位出Guid的值, 例如, 要查找XXX, YYY的值
import os
GuidStr = "guid: "
GuidStrLen = len(GuidStr)
class CheckAssetInfo:
def __init__(self, filePath):
self.filePath = filePath
self.allGuids = []
self.isInit = False
self.checkValueNames = ["XXX", "YYY"]
self.initData()
def initData(self):
if not self.isInit:
self.allGuids = self.get_file_to_guids(self.filePath)
self.isInit = True
def get_all_guids(self):
return self.allGuids
def get_target_value(self, valueName, valueInfos):
for i in range(len(valueInfos)):
n, j = valueInfos[i]
if n == valueName:
return i
return -1
def 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 allGuids
def 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 valueInfos
def 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 ""
class 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 res
def 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] = True
print("Get total asset count meta count: " + str(len(self.allAssetGuids)))
- 获得要引用的GUID与所有的资源GUID,就很简单了,判断引用的GUID不在所有的资源GUID里就可以了
def check_all_asset(self):
res = True
self.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 = False
return res
3.Q&A
- 可能直接分析文件, 而且要大范围遍历文件,会有效率问题, 但实质试验下来, 还是挺快的, 测试验证几十个文件引用, 遍历了4万多个文件, 在i9机器,大概就8秒, 这已经比直接打开unity快多了.
- 当然会有分析错误的风险,但如果在一些相对明确的结构上, 也许是安全,但人工维护是免不了的