- texture = /foo/bar/miracle.png;
方式 III - 基于 GUID 的引用
(形如 {77BA2B2B-3EA5-4C49-A3D2-0DA6A03D2B44})
- texture = {77BA2B2B-3EA5-4C49-A3D2-0DA6A03D2B44};
- texture = v1_ui_mainframe_miracle_png_hd;
呼~~终于说到这一次的实践了。
- texture = /foo/bar/miracle.png:bd37de66ffdcfd5bf544502a1fae1e99;
还好一句话就能说清楚:在路径后面加一个该资源的内容摘要 (算法随意不影响,目前使用 MD5) 就是我目前采取的方案。
关键点那么与上面的方案相比,这个方案有何不同呢?
1.资源重命名或移动时,能够做到自动检测和修改。
- 一般情况下,如果仅仅是重命名或移动,根据内容算出来的摘要是不变的,当通过路径找不到资源时,通过比较摘要,就可以提示用户 (或自动重定向到) 重命名或移动后的资源。
- 检测和修改是可惰性的,可延迟至对应的资源打开时再转换,不必立即一次性扫描和更新所有引用。
- 重命名和更新可以在 OS 的文件系统内完成,无需在特定工具内。
2.资源更新时自动识别和更新摘要。
- 当资源发生变化时 (通常是美术/策划保存了一个新版本) 编辑器会在加载此资源的引用者时为其生成新的摘要。
- 这个也是可惰性的,也就是加载了哪个资源,哪个资源才需要重新生成。
3.不像 GUID 那样需要单独存储,无需额外的 metadata 文件管理负担。
- 由于摘要没有产生资源以外的额外信息,随时可以根据资源本身生成,所以无需额外的 metadata 文件。
4.简化全库范围的操作。
- 方便检查重复资源 (全库比较摘要即可);
- 全库范围自动修复所有的重命名和移动 (完全应用 1.);
- 全库范围自动重算 (完全应用 2.)。
实现逻辑
有同学可能会问:“如果移动,重命名,更新等各种操作混杂在一起,我怎么知道什么时候该自动重定向,什么时候该更新摘要呢?”
嗯,这就是路径 (Path) 和摘要结合 (Digest) 的精髓所在了。我们根据引用去查找资源时,是按照下面伪码的逻辑进行的:
- Resource* getResource(const std::string refString)
- {
- // 分解为路径和摘要两部分
- std::string path = GetPathPart(refString);
- std::string digest = GetDigestPart(refString);
-
- // 尝试访问位于此路径的文件
- Resource* res = GetActualFile(path);
- if (res)
- {
- // 文件存在的情况,检查摘要是否一致
- if (digest == GetActualDigest(path))
- {
- return GetActualFile(path);
- }
- else
- {
- // 文件存在,摘要不一致,则认为是资源更新,重算摘要
- RefreshDigest(path);
- }
- }
- else
- {
- // 文件如果不存在,符合重命名/移动的条件,提示用户资源未找到,是否进行全库范围搜索
- if (/*在另一个地点找到了摘要符合的资源*/)
- {
- // 提示用户 (或自动) 更新引用路径
- RefreshPath(newPath);
- }
- else
- {
- // 提示资源缺失 (in-editor) 或使用 err-placeholder (in-runtime)
- ...
- }
- }
- }
- }
- }
- ...
- // 提示资源缺失 (in-editor) 或使用 err-placeholder (in-runtime)
- {
- else
- }
- RefreshPath(newPath);
- // 提示用户 (或自动) 更新引用路径
- {
- if (/*在另一个地点找到了摘要符合的资源*/)
- // 文件如果不存在,符合重命名/移动的条件,提示用户资源未找到,是否进行全库范围搜索
- {
- else
- }
- }
- RefreshDigest(path);
- // 文件存在,摘要不一致,则认为是资源更新,重算摘要
- {
- else
- }
- return GetActualFile(path);
- {
- if (digest == GetActualDigest(path))
- // 文件存在的情况,检查摘要是否一致
- {
- if (res)
- Resource* res = GetActualFile(path);
-
- std::string digest = GetDigestPart(refString);
- std::string path = GetPathPart(refString);
- // 分解为路径和摘要两部分
- {
也就是说,路径的判定优先级高于摘要。在认定属于何种情况时,路径为主导,摘要为辅助。如果路径吻合但摘要不符,则认为属于资源更新的情况;如果路径失效,则使用摘要去全库匹配。两种行为分别针对两种不同情况的处理,泾渭分明,各司其职。
批量处置
上面的代码是单个资源获取的流程,实际上在编辑器中打开一张地图 (或一个 UI 界面) 时,如果一个资源一个资源地单独汇报和处置,效率就太低了,可以在全部加载完毕后,统一批量地进行一次全库范围的匹配,然后弹出一个汇报和处置的对话框。在这个处置对话框中,重命名/移动/更新都是叹号,而无法识别/找不到资源则是红色叹号,通常如果都是叹号的话直接全部更新就可以了。
代码中的引用
在代码中为了简便,可以仅使用路径即可。在运行游戏的过程中,会自动生成一个 digest_cache.txt 文件,每一行是一个资源的完整引用,可以把这个文件提交到版本管理的库中。这样,很容易通过程序手段在资源发生重命名,移动和更新等事件时,检测并更新这个文件,必要时,可提示用户代码内的路径需要更新。
小结
总得来说,这个方案具有以下的特征:
- 良好的可读性;
- 无需额外的 metadata 文件存储;
- 对资源的重命名/移动无需在编辑器等专有工具内完成,没有潜在的破坏其他资源引用的心理负担;
- 唯一需要保证的是,重命名和移动资源的时候,不要同时更新其内容即可。
好了,关于这个资源引用管理的实践,到这里就讲完了。在资源管理方面,你有什么心得呢?欢迎跟我一起讨论。
相关阅读:一个有趣的交互BUG 兼谈游戏的引导系统锐亚教育,游戏开发论坛|游戏制作人|游戏策划|游戏开发|独立游戏|游戏产业|游戏研发|游戏运营| unity|unity3d|unity3d官网|unity3d 教程|金融帝国3|8k8k8k|mcafee8.5i|游戏蛮牛|蛮牛 unity|蛮牛
- 对资源的重命名/移动无需在编辑器等专有工具内完成,没有潜在的破坏其他资源引用的心理负担;
- 无需额外的 metadata 文件存储;
- 全库范围自动修复所有的重命名和移动 (完全应用 1.);
- 检测和修改是可惰性的,可延迟至对应的资源打开时再转换,不必立即一次性扫描和更新所有引用。
- 还没有人评论,欢迎说说您的想法!