文件夹结构为何重要

JOSE MENDEZ / UNITY TECHNOLOGIESSenior Software Developer
Dec 28, 2023|16 Min
文件夹结构为何重要
为方便起见,此网页已进行机器翻译。我们无法保证翻译内容的准确性或可靠性。如果您对翻译内容的准确性有疑问,请参阅此网页的官方英文版本。

作为客户成功团队的一名顾问,我经常被问到 "我们的资产捆绑是否正确?我的回答始终如一:这取决于项目。然后,我再与客户讨论具体细节。虽然这个答案是准确的,但它并没有为未来的项目提供任何帮助。

虽然这是一个常见的两难问题,但我很难找到一个更普遍的答案(尽管我们已有针对 Addressables 的指导方针和最佳实践)。有些用户不知道捆绑包是否正确,而我的时间往往仅限于检查项目的内存优化。这意味着我无法确定项目中每个资产的预期用途。此外,随着项目规模的扩大,任何一个人都不可能回答每项资产的相同问题。

最终,我恍然大悟:正确构建资产组合的问题限制了找到更普遍答案的视角。让我来解释一下。

资产捆绑加载时会占用内存,因此如果资产与其他资产捆绑在一起,而这些资产又不能同时加载和卸载,那么内存的使用就没有达到最佳状态。因此,询问一项资产是否在正确的捆绑资产中,或者捆绑资产中是否有正确的资产,本质上就是询问你是否在正确的时间装入了正确的资产。至于什么时候应该装入特定资产,答案是该资产的预期用途是什么,这就是为什么答案通常是 "这取决于项目"。在实践中,我发现了解了资产的预期用途,就能知道如何在内存中加载资产以及如何捆绑资产。

因此,"预期用途 "是关键短语:谁知道资产的预期用途是什么?如何向所有人传达这一意图?最后,什么时候应该这样做?

在我看来,有一个时刻对这个问题的答案是非常明确的:在创建和修改资产时。无论是角色的特定纹理、全局照明着色器,还是在多个游戏关卡中使用的树木网格,创作者都知道其预期用途,因此他们最适合传达这一意图。艺术家可以通过使用统一的文件命名规范和将具有相同预期用途的文件归入同一文件夹来传达这一意图。

然后,程序员和其他团队成员就可以利用这些信息来决定何时以及是否应将某项资产与同时加载的其他资产捆绑在一起。因此,在项目进行期间,资产的预期用途必须一目了然,而文件目录则是所有团队成员的真理之源。

在这篇博文中,我将介绍一些最佳实践和常见的边缘案例,希望能帮助您更好地组织未来的项目。首先,我们来讨论一些常见的文件夹结构及其问题。

文件夹结构

根据我的经验,文件夹结构有四种类型:随机、按资产类型、按功能和按用途。其中,最后一项是迄今为止最好的,因为它传达了意图,因此最适合最佳捆绑策略。

随机性不是我经常看到的。这种情况大多发生在不熟悉软件开发的个人开发人员身上,但随着项目规模的扩大或复杂程度的增加,这种情况显然就难以为继了。缺乏结构或结构随意会带来很多问题--资产很难找到,而且几乎不可能了解预期用途。

按资产类型构建结构是很常见的,因为许多艺术家在将资产导入引擎之前都是这样处理的。如果你知道资产类型,找到它的位置就很容易,但关于它的其他信息就会模糊不清。即使有很好的命名规范,也很难判断角色、环境、用户界面或三者的组合是否需要特定的着色器、纹理、网格等。适当的文件目录不应掩盖信息,而应揭示信息。

按功能划分的文件夹结构很少见,但乍一看似乎很合理。很多公司都会划分功能团队,为什么不对数据进行类似的分组呢?游戏并不是这样使用数据的。在过去,我看到过一些本应捆绑在一起的例子,例如着色器和音频,但由于它们是由不同团队编写的,因此这一事实变得模糊不清。

如果文件目录有一个明确的命名规则,并以资产的用途为导向,就可以避免这些问题。为了说明这一点,我将使用一个名为 "恐龙大乱斗 "的虚构游戏示例。

恐龙大乱斗

在这个例子中,《恐龙大乱斗》是一款第三人称 3D 动作冒险游戏,玩家选择一只恐龙,控制它穿越一个拥有多个生物群落的广阔开放世界,与其他恐龙和史前生物搏斗,试图变得更强大,并将自己的基因传给下一代--所有这一切都是为了在即将到来的冰河时代中生存下去而进行的巨大斗争。该游戏专为手机设计,部分数据将作为原始应用程序的一部分发布。其余部分将根据需要从 CDN 下载。

根据上述概要,我们可以为整个项目设计一个通用的文件夹结构。由于玩家可以选择其他恐龙并与之战斗,因此为每只恐龙创建一个文件夹来存放该恐龙的所有特定资产:网格、音效、纹理、动画、粒子效果等,是非常合理的。我把它们归类为独特资产。

由于这是一款动作游戏,它的环境将是独立的生物群落。因此,我们应该为项目中的每个生物群落或层次创建一个文件夹:平原、沙漠、苔原、沼泽、火山等。

当然,也会有一些资产需要在游戏的整个章节中出现。用户界面元素就是其中之一。您可以将这些资产视为全球资产。就像我们为所有 "独特资产 "建立一个文件夹一样,也应该有一个位于文件夹层次结构顶层的全局文件夹。例如,全局用户界面文件夹、全局恐龙文件夹、全局环境文件夹等。这样,这些游戏版块共享的所有内容都存储在一个地方。

共享资产

这一类资产被定义为由某些独有资产共享,但不是全部。因此,它们既不属于全球资产类别,也不属于独特资产类别。对于《恐龙大乱斗》来说,这类共享资产的一个例子就是所有会飞的恐龙中都有的资产,如粒子、着色器和声音效果,这些都是给人一种在空中翱翔的感觉所必需的。

经常发生的情况是,这些资产被放在第一个需要它们的恐龙的文件夹中。遗憾的是,这并不能准确描述它们的使用方式,从而混淆了它们的本意。在最坏的情况下,资产会被复制到每个会飞的恐龙捆绑包中,这对内存、调试和应用程序大小来说都是低效的。

最好的解决办法是创建一个新文件夹,并命名为 "飞行的恐龙",以表明其用途。决定地点的具体细节比较棘手,没有标准。我更喜欢把它们放在与全局文件夹和独特恐龙文件夹同级的子文件夹中,但把它们与其他独特文件夹放在一起也同样合适。

有意改变

这一惯例的典型边缘情况是,当项目需求发生变化时,原本打算独一无二的资产变成了共享资产。在我们的"恐龙大乱斗"示例中,为了节省开发时间,我们决定使用迅猛龙预制件作为所有其他猛禽(如犹他猛禽、独角猛禽等)的基础。

但开发人员没有意识到的是,当迅猛龙预制件被添加到捆绑包中时,所有其他迅猛龙的资产都将在加载时被下载,从而增加了下载时间,尽管只使用了预制件。

出现这种情况的原因是,资产的意图发生了变化,文件夹结构不再反映这一点。当意图发生变化时,应更新资产的位置和名称以反映这一变化,从而保持系统的一致性和准确性。这就向创建捆绑包的团队传达了哪些资产应放在 "共享迅猛龙 "捆绑包中,哪些资产应留在独特迅猛龙模型中。

意外使用

最常见和最难解决的边缘情况之一是,资产无意中被用于一种从未想过的用途。发生这种情况时,通常都是意外。例如,某人要在截止日期前完成工作,他就会使用项目中已有的资产来快速完成工作。

就拿这个场景来说吧:一位美工正在为即将推出的《恐龙大乱斗》扩展版添加教程,并找到了一种 "金色光芒 "着色器,以突出玩家何时可以对精英敌人进行反击。美工不知道的是,这个着色器是针对巨型霸王龙的终极游戏内容--它是一个独特的 BOSS,有很多资产捆绑在一起。现在,所有这些资产都将在教程中下载,而大部分资产都不会在教程中使用。这个捆绑包非常大,因此通常在游戏过程中下载这类次要资产的系统会受到压力,导致性能飙升或崩溃,因为连接不佳的玩家无法快速下载资产。

以上是一个极端但完全现实的例子,我在不止一个项目中看到过这样的情况。因此,除了文件夹结构外,所有资产都应有一个能表达其意图的名称。例如,如果着色器的名称是 gold_glow_trex_endgame,其用途就会一目了然。这样,在调试时就会发现,在教程中不应该加载该资产。

编程解决方案

如果你熟悉 Addressables,你可能会知道组和标签用于分组和标记资产,与我在上面游戏示例中建议的方法基本相同--使用文件夹和合理的命名约定。您可能会问:"如果使用组和标签就可以实现这些功能,为什么还要费心做这些呢?

我的回答是,你应该两者兼顾。正如我在开头所解释的那样,随着项目中资产数量的增加,一个人要想知道所有资产的用途会变得越来越难,最终甚至不可能。知道 Addressables 组应与文件夹结构相匹配,可以用来确认它们的设置是否正确。

我见过很多使用无 Addressables 资产包的客户将复杂的系统编码作为解决这一问题的方法。例如,他们将创建和维护用于创建捆绑包的主列表,或检查版本控制提交以比较捆绑包中的更改等。我的经验是,从长远来看,这些解决方案并不符合成本效益。这又是一个需要开发和维护的系统,会产生更多的故障点。随着项目规模的扩大,它们会在无数的例外情况和边缘案例面前束手无策,而长期存在的项目自然会积累这些例外情况和边缘案例。最糟糕的是,从根本上说,它们之所以失败,是因为它们没有针对用户错误的补救措施。

结论

结构合理的文件夹系统和文件命名规范应能使资产包和 Addressables 组实现 1 对 1 的匹配。将文件归类为逻辑分组和子文件夹,可确保所有团队成员以统一的方式解释和查找文件,从而在人员变动和项目要求不断变化时,减少潜在的误解和差异。资产创建者可以方便地访问和导航,使其他团队成员不必费时费力地查找特定资产。系统性的方法可以节省宝贵的时间,最大限度地减少错误和疏忽的可能性。成为一个持久的参考点,一个真理的源泉,简化新团队成员的入职流程,确保项目的长期可行性。

您在寻找文件夹结构方面的支持或建议吗?在论坛上与我们交流在 Tech from the Trenches 系列中,您可以查看更多来自 Unity 开发人员的技术博客