Python 实现 Unity 资源检测
问题背景
需求:需要在项目中进行诸如判断 ScriptableObject 文件引用的材质或者贴图有没有丢失的工作
问题:直接调用 Unity 接口,需要执行 Unity 命令行,Unity 启动耗时太大,会导致检查脚本执行过久。
解决方式:使用 python 直接解析 AssetDataBase
知识背景
Unity 资源导入流程
放入 Unity 的美术资源(称之为 源资源 )的格式往往不是最终能使用的格式,比如常用来保存贴图的无损贴图格式 png,并不能直接提交给 GPU 采样,最后要转换成平台兼容的格式,比如 ASTC,这个转换之后的最终文件,依赖于源资源(可能不止一个,比如考虑图集的情况),以及资源的 导入设置 ,具体而言就是 源资源 对应的 meta 文件存储的设置,对于每个 源资源,Unity 会分配一个 GUID 。
ScriptableObject
Unity 提供了名为 ScriptableObject 的基类来存储各类自定义序列化对象,该类生成的文件也是一种 源资源,也有对应的 GUID ,它具体而言是一种名为 yaml 的序列化格式文件(meta 文件也是相同的格式)。ScriptableObject 上引用的各类源资源,最后会被记录成 GUID 保存在 yaml 格式的文本中。
SourceAssetDB
上文提过Unity 会为源资源分配 GUID,除此之外还会记录一些资源导入的设置等,该结果会被 Unity 保存到 Library\SourceAssetDB 下,此外其中还保存着源资源注入最后修改时间、hash等信息。(注:本文说的都是使用了 Asset pipeline v2 的情况)
LMDB
Unity 将 SourceAssetDB 保存成了名为 LMDB 的数据库文件,这是一种 key-value 型的数据库。
工程实现
解析 ScriptableObject
ScriptableObject 是 yaml 格式的文件,python 下直接使用 yaml 库即可解析,但要注意 Unity 添加了一些自定义 tag,这里简单处理跳过头三行字符串。
def read_ymal(file_path):
file = None
try:
file = open(file_path, 'r')
lines = file.readlines()
#跳过开头三行的 unity 的自定义 Tag 与 handler
return yaml.safe_load("".join(lines[3:]))
except Exception as e:
print("read ymal exception : {} ".format(e))
return None
finally:
if file:
file.close()
读取 LMDB
可以使用 lmdb 的 python 实现功能, 它提供的 lmdb.open 接口要求数据库目录下有两个文件, data.mdb 与 lock.mdb ,可直接将 Library 下的 SourceAssetDB 与 SourceAssetDB-lock 拷贝成这两个文件(其实有另外的法子,但为了安全不直接使用原 DB 文件,就这样处理了,可以优化一下减少拷贝开销),然后就能直接读取数据库了,下面以检查 GUID 是否存在为例
#拷贝 DB 文件
shutil.copyfile(DB_path, DB_new_path)
shutil.copyfile(DB_lock_path, DB_new_lock_path)
def is_exist_guid(guid):
env = None
db = None
try:
env = lmdb.open(os.path.join(get_imdb_path()), map_size=1024*1024*30, subdir = True, readonly = True, max_dbs = 1)
db = env.open_db("GuidToPath".encode())
with env.begin(db) as txn:
return txn.get(guid_str_to_lmdb_bytes(guid)) != None
except Exception as e:
print("is_exist_guid exception : {}".format(e))
return True
finally:
if env: