作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
编者注:本文由我们的编辑团队于2023年10月1日更新. 它已被修改,以包括最近的来源,并与我们目前的编辑标准保持一致.
团结是用于多平台开发的直接且直观的工具. 虽然它的原理很容易理解, 在团结中很容易犯基本错误. 这样的错误会减慢开发进度, 特别是从初始原型阶段到最终版本的转换.
通过了解开发者面临的常见团结问题, 你将更好地准备运用策略来避免这些陷阱. 这里概述的注意事项主要集中在3D应用程序开发上, 但也同样适用于2D开发.
为项目的营销努力建立支持, 弄清楚它的商业模式至关重要. 您需要知道您的应用程序发布的目标平台, 最小支持的设备规格:是否支持旧的, 低端设备, 或者只是最近的型号? 这些考虑将深刻影响应用程序的性能和视觉效果. 开发成本是下面讨论的每个主题的关键.
在技术方面, 项目经理需要与团队记录和共享项目的工作流程细节(从开始到交付). 这个工作流程应该考虑到迭代开发过程(例如.g.(更新模型,精炼代码). 还必须建立所需的帧速率和顶点预算. 对这些数据的分析将使您能够更好地为模型分配最大分辨率和详细级别(LOD)规格. 您将希望建立并始终使用单一的度量系统, 哪些可能需要在导入阶段调整模型. (详见第1号错误. 2.)
关卡设计和划分会影响性能,因此对未来的工作至关重要. 在设计新关卡时要考虑性能,并确保不超出项目的要求和限制. 问问你自己:“这个水平可以合理地达到吗?如果不能,那就继续前进,除非达到这个水平对你的业务需求是必要的.
当模型在场景中使用时, 它应该可以随时使用,不需要进一步修改. 其规模必须与您的应用程序一致. 如果你的3D建模软件不允许这样做,也许它会使用不同的单位. 你应该在应用的模型导入设置中特别设置比例因子(留0).为3dsMax和Modo设置01,设置1.0 (Maya). 您可能需要在更新比例后重新导入对象. 这些设置允许团队使用基本尺度1,1,场景1, 在没有物理问题的情况下实现一致的行为. 有了这些设置,动态批处理也可能正常工作. 将此规则应用于模型中的每个子对象—而不仅仅是主对象.
当您需要调整对象尺寸时, 它是否与3D建模应用程序中的对象相关. 在团结中尝试缩放以确定合适的值. 然后, 在最后的应用程序和一致的工作流程, 在将模型导入团结之前,您将做好一切准备.
确保您的模型划分得很好,并且在需要重用它们的情况下保持最小的子对象:子对象越少越好, 更好的.
每个对象及其子对象的枢轴应该根据对象的主要功能适当地对齐和旋转. 主对象的Z轴应该指向前方,并在对象的底部旋转, 允许在场景中更好地放置. 在物体上使用尽可能少的材料.
每个资产的名称应该清楚地描述它的类型和功能. 在您的工作中,始终保持这些命名约定的一致性.
在团结中,你可以通过拖放对象引用来轻松创建原型并实现功能,从而简化场景中的寻址对象并访问其相关组件.
虽然看起来很简单,但这种拖放实现有潜在的危险. 你可能会, 例如, 创建一种相互依赖关系,其中一个代码单元完全依赖于代码的另一部分——甚至依赖于应用程序中的另一个系统或脚本——甚至依赖于当前场景或场景. 如果你不小心, 您还可能降低应用程序的性能 , 因为在层次结构中查找对象和访问组件有开销.
采用模块化方法并创建可用于应用程序其他部分的部件, 甚至可以在整个应用程序组合中共享. 在团结 API上构建框架和库,就像构建知识库一样. 如果您发现某个特定组件需要与应用程序中的另一个系统通信, 使用接口可以使系统的相关部分更加抽象,从而可以重用.
或者,您可以使用事件驱动的方法对外部事件作出反应. 您既可以创建消息传递系统,也可以直接注册为另一个系统的侦听器.
分开是很有挑战性的 gameObject
来自程序逻辑的属性(至少对于模型-控制器原理之类的东西), 因为很难识别哪些对象正在被修改 gameObject
的变换属性(e.g.,位置,旋转). 转换应该是控制器的专属责任.
从以下角度处理文档, 您是否需要在长时间离开后重新访问您的代码, 它应该是快速和容易理解和识别代码的功能. 但在记录你的工作时,有时也不要太过火, 一个合适的类, 方法, 或属性名称就足够了.
关注性能是一项需要认真对待的要求. 一个笨拙的应用程序,如果需要进行太多的计算,或者在用户界面上要求过于详细的分辨率,即使是功能强大的新手机,也会耗尽电池的电量, 控制台, 或者台式电脑. 因此, 您应该投资于性能优化, 因为这将决定你的游戏或应用与竞争对手的区别. 当你让应用程序的某个部分更高效时, 您可以使用这些额外的周期来优化应用程序的其他部分.
优化机会很多, 即使我们用一整篇文章来讨论这个话题, 我们只会触及表面. 目前,以下是核心优化目标:
优化 | 描述 |
---|---|
更新循环 | 与其在更新循环中使用高成本操作,不如使用缓存. 例如,当您设计对场景中的组件或另一个对象的访问时, 或者在脚本中编写昂贵的计算. 如果可能的话,缓存所有内容 醒着的 方法,或者将体系结构设计为事件驱动的,以便仅在触发时调用功能. |
实例化 | 预初始化高实例化成本对象的池(例如.g.(FPS游戏中的子弹). 然后,当需要时,激活该对象. 当不再需要该对象时,停用它并将其返回到池中. |
呈现 | 使用遮挡剔除或LOD技术来限制场景渲染. 使用一个优化的模型,你将能够保持场景的顶点计数下降. Note: Vertex count isn’t just the number of vertices in a model; a vertex count is influenced by things like normals (hard edges), UV坐标(UV接缝), 顶点颜色. 也, 场景的动态光照数量会显著影响整体表现, 所以尽量提前烤好, 只要有可能. |
把电话 | 减少抽牌次数. 在团结中,你可以对静止物体使用静态批处理,对移动物体使用动态批处理. 你必须首先准备你的场景和模型(批处理对象必须共享相同的材料). 动态对象的批处理仅适用于低分辨率模型. 或者,你可以通过脚本将网格组合成一个( 网.Combine网es ). 如果你是组合网格而不是批处理, 注意不要创建太大的对象,以免在某些平台上利用视图截锥体剔除. 使用尽可能少的材料,并在整个场景中共享它们. 您可能需要从纹理创建一个图集,以便能够在不同的对象之间共享一个材料. 一个好的 提示 是使用更高分辨率的场景光图纹理(不是生成的分辨率, 但纹理输出分辨率),以降低纹理总数,当你在一个更大的环境烘烤光. |
透支的问题 | 只有在必要的时候才使用透明纹理,因为它会导致填充率问题. 透明纹理适用于渲染复杂和较远的几何对象, 特别是树木或灌木. 应用透明纹理, 支持alpha混合着色器,而不是那些带有alpha测试或切割着色器的移动平台. 对于这些问题的一般识别, 降低应用程序的分辨率,因为这会使填充率或未优化的着色器问题更加明显. 否则,内存问题可能导致透支问题. |
着色器 | 通过减少通道数来优化着色器以获得更好的性能, 使用精度较低的变量, 用预先生成的查找纹理代替复杂的数学. 始终使用分析器来确定瓶颈可能出现的位置. 对于渲染,你也可以使用非常棒的Frame Debugger. 这个工具将帮助开发人员理解事物是如何工作的,同时用它来分解呈现过程. |
尽管事实是 垃圾收集器 (GC)帮助我们更高效,更专注于编程问题, 您还应该意识到GC的使用不是免费的.
一般, 您应该避免不必要的内存分配,以防止GC过于频繁地触发自己,从而通过帧速率峰值破坏性能. 理想情况下,新的内存分配不应该在每个帧内定期发生.
那么,我们怎样才能实现这个目标呢? 最终, 应用程序的体系结构与此有很大关系, 但这里有一些有用的规则可以遵循:
foreach
循环). 团结使用的是旧版本的Mono.醒着的
方法或在事件中.StringBuilder
对象而不是字符串.从一开始就掌握应用程序的内存和空间使用情况是必要的. 在发布之前进行优化要复杂得多. 对于资源有限的移动设备来说,这一点尤为重要.
此外,许多公司限制通过其蜂窝网络下载应用程序. 如果超出这些规模限制,您可能会失去大量客户. A customer will better appreciate an app that does not waste their time or precious phone resources; they will be more likely to download or buy your app if it is smaller.
若要定位资源耗尽器,请查看编辑器日志. 在每次新构建之后,您将看到资源的大小划分为不同的类别(例如.g.,音频,纹理,dll). 为获得更好的定位,可以在 统一资产存储. 这些将为您提供文件系统中参考资源和文件的详细摘要.
实际的内存消耗也可以在分析器中看到, 但建议在应用连接到目标平台上的构建时进行消费测试,因为在目标平台以外的任何平台上进行测试都会产生意想不到的结果.
纹理通常是最大的内存消耗者. 最好使用压缩纹理,使用相对较少的空间和内存. 理想情况下,所有纹理都是平方的. 使两边的长度为2的幂(POT), 但请记住,团结也可以自动缩放非功率(NPOT)纹理到POT. 此外,纹理可以压缩为POT形式.
Atlas纹理一起填充整个纹理. 有时你甚至可以使用纹理alpha通道为你的着色器提供一些额外的信息,以节省额外的空间和性能.
当然,尽量重复使用场景纹理. 如果不妨碍视觉效果,可以使用重复的纹理. 对于低端设备,在团结的质量设置中降低纹理的分辨率. 对于较长的音频片段,使用压缩音频格式,例如.g.,背景音乐).
在处理多个平台时, 决议, 或本地化, 使用资源包为不同的设备或用户打包不同的纹理集. 资产包可以在应用程序安装后从互联网动态加载,以限制初始应用程序的下载大小,并将加载的内容强加给后台进程.
当移动场景中的物体时, 我们不一定意识到物体有对撞机, 而改变它的位置将迫使团结重新计算整个物理世界.
在这种情况下,添加 Rigidbody
组件添加到应用程序中. 集 Rigidbody
变成非运动学的,如果你不想涉及外力的话.
调整:修改与…相关联的对象的位置 Rigidbody
,设置 Rigidbody.位置
当一个新的位置不跟随前一个位置时,或设置 Rigidbody.MovePosition
当它是连续运动时,这就要考虑到插值. 修改位置时,应用于 Fixed更新
函数,而不是in 更新
功能. 这将确保一致的物理行为.
物理可能是应用程序性能的瓶颈. CPU开销和原始碰撞器之间的碰撞计算起来要快得多. 如果可能的话,使用原始碰撞器 gameObjects
像球体,盒子或圆柱体,而不是网格碰撞器. 从多个这些形状组成最终的碰撞器. 当物理交互的准确性不是那么必要时,你也可以调整时间管理器中的固定时间步长设置来减少物理固定更新的频率.
我们有时倾向于通过在游戏模式中进行实验来手动测试功能. 这很有趣,而且我们所需要的一切都在我们的直接控制之下.
但这种乐趣很快就会消失. 应用程序越复杂, 程序员就越需要重复和分析任务的结果,以确保应用程序的行为符合其初衷. 这些迭代很容易成为开发过程中最不吸引人的部分. 当某件事不再有趣时, 我们的参与度降低了,我们变得草率了, 增加bug通过测试的可能性.
你可以使用团结的工具自动进行测试. 加上适当的架构和代码设计, 单元测试将验证独立的功能, 集成测试将确认更复杂的场景. 当您记录实际数据并将其与所需状态进行比较时,可以显著地减少查找和查找方法.
毫无疑问,有限的手工测试是开发的关键部分. 当没有办法自动化测试时, 准备好您的测试场景,这样您就可以快速地专注于需要解决的问题. 理想情况下,这是在玩家按下Play按钮后的几帧. 实现快捷方式或作弊来设置所需的测试状态. 确保隔离测试情况,以便更好地确定问题的根本原因.
时间累积起来. 在游戏模式中花费的总时间是有价值的. 当测试, 测试问题的初始偏差越大, 你就越有可能根本不测试这个问题, 希望一切都能顺利进行,但这不太可能.
我曾与一些客户合作过,他们可能出于习惯,严重依赖于资产商店插件. 并不是说没有有用的团结扩展 统一资产存储; there are many. 如此之多,事实上,有时很难决定选择哪一个. 但如果你引入的插件不能很好地配合使用,你就有可能破坏应用的稳定.
为应用程序引入新功能, 资产商店中经过良好测试的产品可以节省您的时间, 如果不是几天, 的发展. 确保你项目的安全, 在下载任何资产/产品之前,请先查看其最高评价.
如果您希望添加的功能足够快速和简单, 考虑自己写吧, 增加您的个人(或公司)库以供将来重用,同时扩展您的知识和工具集.
团结 Editor环境似乎是基本游戏测试和关卡设计的理想选择. 一些开发人员认为扩展它是浪费时间. 但请相信我,事实并非如此.
团结的魔力源于它对特定问题的适应能力. 利用这一优势, 要么改善开发人员的体验, 或者显著加快整个开发和关卡设计工作流程.
如果不使用方便的功能,那就是疏忽了, 比如内置或自定义属性抽屉, 装饰的抽屉, 自定义组件检查器设置, 甚至不用团结自己的编辑器窗口来构建整个插件.
通过让自己熟悉, 认识到, 或者预见到开发者常犯的团结错误, 你可以积极主动地采取措施来避免这些痛点. 在你的项目中,你可能对解决这些问题有不同的意见或程序. 最重要的是在你的项目中保持一致,并透明地与你的团队合作 团队 和利益相关者.
团结为电脑游戏和模拟提供了一个跨平台引擎,其输出可以在桌面上运行, 游戏机, 移动客户端.
团结面临着一些资产库的前向兼容性问题, 使引擎更新具有挑战性.
团结允许开发人员生成具有高质量视觉效果的应用程序. 团结的可定制引擎易于学习,其资产库为创建功能丰富的解决方案提供了坚实的工具基础.
团结开发人员能够通过视频和书籍格式轻松访问在线学习资源来学习和升级他们的技能.
世界级的文章,每周发一次.
世界级的文章,每周发一次.