Unity 项目设计与管理
对于 Unity 初学者和经验丰富的开发者而言,项目设计与管理都是迟早需要掌握的重要知识。本文由 Unity 技术支持工程师田彪,为大家全面详细地介绍 Unity 项目设计与管理的系统知识。全文分为五大板块,大家可挑选感兴趣的部分阅读。
游戏项目构成概述
在制作游戏时,游戏内容丰富多样,但使用 Unity 设计游戏项目通常大致包含以下几个部分:
- 项目简述
- 策划文案
- 美术资源
- 程序逻辑
- 性能优化
我们将详细介绍游戏制作过程中的每一步工作,帮助 Unity 开发者以及潜在开发者更深入地了解 Unity 游戏开发,减少开发过程中的弯路。
项目简述
想要制作成功的游戏,前期调研工作至关重要。开发者需要了解玩家的喜好,倾听玩家的声音。当游戏推向玩家后,它就属于游戏的受众群体了。在游戏项目启动具体内容开发时,需要明确以下几点:
项目方向
市场上游戏类型繁多,为避免游戏同质化,项目组相关负责人需确定项目的类型定位,如 RPG、FPS 或 Sport 类等。
时间计划
任何项目组制作的游戏最终都要推向市场,只有获得玩家支持,游戏才能长久发展。项目启动初期,需要制定大致的游戏开发时间计划,例如 8 个月、1 年或 18 个月等。
人员配置
游戏项目开发有经费预算,包括研发预算和推广预算。项目组的人员配置通常取决于游戏复杂度和项目经费预算。在游戏总体内容固定的前提下,人员数量与开发时间大致成反比,但如果组内人员管理不善,可能会出现不协调的情况。因此,在确定项目人员总数后,还需明确项目组内各岗位(策划、美术、程序)的具体人数。
游戏大纲
游戏大纲能让所有项目组开发成员在接触游戏前对其有一个大致的认识,同时激发成员的想象力,为游戏内容提出更好的建议。然而,游戏大纲是目前手游开发过程中大多项目组容易忽略的重要环节。
策划文案
“兵马未动,粮草先行”,优秀的策划文案是项目良好的开端。文案内容相同但排版不同,效果也会有所差异。设计文档应逻辑清晰、排版简洁、简单易懂,最好图文并茂。
美术资源
美术是游戏的门面,玩家对游戏的第一印象往往取决于美术风格,它直接影响玩家是否会在游戏中停留超过十分钟。美术人员在制作相关美术资源时,需要关注以下方面:
标准 - Standard
预先制定完整的美术标准,可以降低整个项目开发中美术资源反复修改的频率,减少美术人员的无用功。美术资源的标准规范大致包括材质数量、模型三角面、纹理尺寸、粒子数量、动画帧频等。
模型 - Model
当美术人员将模型文件导入 Unity 编辑器时,合理设置导入选项对游戏性能有积极影响。导入时需注意相关事项。
着色器 – Shader
Unity 引擎在将 shader 导入编辑器时不会进行编译,而是在开发者发布到具体平台时再进行编译。在 shader 的查看面板可以查询具体信息。
贴图 - Texture
Unity 支持目前主流的各种图片格式,如 PSD、TGA、PNG、GIF、BMP、JPG、TIFF、PICT 等。引擎在导入图片时,会生成自身使用的贴图数据,在图片导入设置中,需合理设置各选项。在编辑器的 Scene 窗口下,选择 Mipmaps 可查看各贴图是否存在精度过剩问题,若有可通过缩减对应贴图尺寸节省空间。
光照 - Lighting
Unity 5 使用了全新的光照系统和更优的光照算法,有助于美术人员制作精美的场景。但在使用光照时,除了考虑场景美观度,还需考虑玩家设备的兼容性。
动画 - Animation/Animator
Unity 目前支持 Animation 和 Animator 两种动画播放形式。Animation 是老式动画系统,Animator 作为新的动画系统有其独特优势。理解相关信息有助于更好地使用 Animator。
音效 - Audio
声音是游戏中不可或缺的部分,良好的音乐效果对提升游戏品质意义重大。Unity 支持主流的各种音乐格式,如 mp3、ogg、wav、aif 等。但如果音乐格式设置不当,会降低游戏性能。
程序逻辑
游戏不仅是美术资源的展示,还存在频繁的玩家交互行为,所有交互均通过程序脚本实现。编写程序时,不仅要实现功能,还要考虑功能的扩展性以及当前功能实现方式对游戏中其他逻辑模块的影响。主要涉及以下几个方面:
逻辑架构
设计程序框架时,建议采用模块化设计各功能,保持各模块的独立性和封装性,使单个模块满足“即插即用”。例如,整个游戏逻辑可分为场景模块、角色模块、UI 模块、网络通信模块、战斗模块等。模块化设计使游戏逻辑树简单易懂,便于代码审查,也有利于项目后期的性能优化和 bug 检查。例如,调试 bug 时,若禁用某一模块后游戏正常运行且 bug 不再出现,则该模块引发 bug 的概率较高;做优化处理时,若禁用某一模块后 CPU 负载大幅下降,则该模块造成性能瓶颈的概率也较高。
脚本应用
Unity 的脚本使用托管机制,若某脚本需要挂载到场景 GameObject 上,则该脚本需继承于 MonoBehaviour。在菜单 Edit -> Project Settings -> Script Execution 下,可更改各类脚本的执行顺序。MonoBehaviour 的执行基于队列存储形式,而非系统反射机制。Unity 引擎由 C++ 代码编写,游戏开发者编写的代码通常为 C#,因此从底层引擎的 C++ 代码到逻辑层的 C# 之间存在托管开销。若在 Unity Profiler 里发现“Overhead”等字眼,其中包含代码的托管开销部分(但不仅限于此)。为减少代码托管开销,设计具体脚本时可进行优化。例如,有一个继承于 MonoBehaviour 的类 ClassA,其 Update 函数会执行具体逻辑,若该类有 30 个实例同时存在,则会产生 30 次托管开销。此时,建议删除 ClassA 中的 Update 函数,用自定义的 UpdateEx 函数替代,并编写一个 ClassAManager 类,缓存一个 ClassA 的数组或队列,每次 Update 时遍历该队列,执行每个实例的 UpdateEx 函数。
Asset 管理
Unity 中的 Asset 序列化支持二进制、文本模式、混合模式三种,可在菜单 Editor Settings -> Asset Serialization 下进行设置。文本模式采用 YAML 格式,可增加可读性,每个资源文件都可生成对应的 meta 文件。制作场景时,建议将场景中的具体 GameObject 制作成 Prefab,而非直接使用 FBX 格式,便于引擎底层进行资源管理。Unity 引擎中各种美术资源都可编译成 Assetbundle,包括纹理、模型、Prefab、AnimationClip、AudioClip 等,且 Assetbundle 支持压缩与非压缩格式,开发者可根据项目实际情况进行设置。
动态更新
动态更新通常包括美术资源与脚本的更新。美术资源建议使用 Assetbundle,脚本更新在 Android 平台上可使用 dll 反射实现(仅限 Android 平台),也可通过其他第三方非官方模式。
性能优化
性能优化是每个游戏项目不可或缺的部分,且没有一劳永逸的方法,需要逐步实现。以下主要介绍性能优化中必须处理的模块,包括图形、物理、程序、文件等,以及一些用于性能优化的工具。
图形优化 – Graphics
要实现图形优化,首先需熟悉图形学整个渲染管线流程,简要介绍如下: 应用程序 (Application) -> 几何体 (Geometry) -> 光栅化 (Rasterizer)。 其中,几何体阶段具体包括:模型变换(Model & View Transform) -> 顶点着色(Vertex Shading) -> 投影变换(Projection) -> 裁切(Clipping) -> 屏幕映射(Screen Mapping)。 光栅化阶段包括:三角设置(Triangle Setup) -> 三角遍历(Triangle Traversal) -> 像素作色(Pixel Shading) -> 合并(Merging)。 进行图形优化时,首要步骤是定位瓶颈所在,是 CPU 还是 GPU。若瓶颈在 GPU,对 CPU 进行优化则得不偿失,如同木桶原则,决定木桶装水量的是最短的那块木板。 如果渲染瓶颈发生在 CPU,通常对 CPU 进行的图形相关优化主要涉及以下几点:
- 合并模型:可由美术人员手动合并或使用引擎的 Batching 技术。但如果被合并的模型未使用同一材质或使用多重材质且不共用贴图,合并操作不会提升性能。
- 减少材质使用数量:尽量实现材质共用。
- 纹理拼接:将多张小尺寸纹理拼接到同一张大尺寸纹理中。
- 避免使用多重渲染:如反射、阴影、像素光照等。
- 动画优化:包括减少骨骼数量、降低动画帧率等。
由于 CPU 在图形渲染中的计算量相对 GPU 较低(前提是设备同时具备 GPU 和 CPU),因此图形优化更多针对 GPU 端,具体包括:
- 模型对象:调整模型三角面是优化美术资源的基本步骤,应根据需求选择合适的面数。当场景中某些对象被标记为 static 时,禁止在脚本中对其位置、朝向、缩放进行更改。设计模型时,尽量减少 UV 映射缝隙和硬边的数量。
- 光照设计:在移动设备上,尽量使用 light map 代替实时光照。若美术人员需要炫目的光照效果,可预先调节好实时光照效果后再进行 light map 烘焙。某些特定情况下,建议使用 shader 实现特定对象的酷炫效果,而非增加额外光源。减少像素光的数量,可降低 CPU 负载和 GPU 消耗。若场景中某像素光照作用的两个模型对象距离较远,不建议对其进行合并操作。
- Shader 性能:Unity 引擎提供了大量内置 shader,基本能满足项目需求。若需自定义 shader,编写时需注意细节。减少或不使用条件语句,考虑变量所需的精度位宽(Float 为 32 位,half 为 16 位,fixed 为 10 位),避免使用复杂的数学计算函数(如 sin、tan、pow、exp、log 等),移动平台避免使用 Alpha Test 和 Alpha Blend 指令,若不可避免,建议使用 Alpha Blend。
- 纹理压缩:压缩纹理可节省内存和运算带宽。建议 3D 模型贴图生成对应的 Mipmaps,虽生成 Mipmaps 会使纹理内存大小相对增加约 33%,但不生成会导致整体性能损失较大。
- LOD 使用:Unity 中关于 LOD 的使用有 LOD Group 和 Camera.layerCullDistances 两种方式。LOD Group 主要用于大型模型对象,Camera.layerCullDistances 主要用于碎片化的模型对象。
物理优化 – Physics
Unity 引擎中物理计算更新在 FixedUpdate 中完成。根据具体游戏项目,若游戏物理更新频率不需要太高,可在菜单 Project Settings -> Time 下更改 Fixed TimeStep 的值。使用物理碰撞体时,在满足设计要求的前提下,建议使用 Sphere Collider 或 Box Collider 代替 Mesh Collider。Mesh Collider 和 wheel Collider 计算复杂,能避免使用则避免。被标记为 static 的碰撞体,禁止移动其位置。若对象不需要 Rigidbody 组件,应一律删除。在脚本中通过 Physics.RaycastAll 类似接口获取固定对象时,应缓存所得到的对象,避免每帧调用该类接口获取固定对象。
程序优化 – Scripts
程序优化的关键在于设计,程序员编写程序时需仔细思考。Unity 引擎由 C++ 编写,上层逻辑脚本基于 Mono 体系,内存管理使用 GC 机制。GC 机制在内存管理上实时性不足,Mono 内存基于内存池,申请的内存不会释放(只有在内存池内存不足时才会申请新内存),只会返回内存池。基于此机制限制,应避免频繁申请内存,尽量使用对象池管理对象。例如,在 Update 函数里每帧都 new 一个数组或队列,可能导致 GC 未回收先前内存,内存池内存不足又重复申请新内存,最终使 Mono 内存池不断增大。在 Unity 引擎层面,可使用 IL2CPP 技术提高脚本运行速度,也可在 Script Call Optimization 中设置忽略异常处理。建议多使用引擎的 Profiler 工具定位具体脚本中的负载瓶颈。
文件优化 – Files
若要降低整体文件大小,首先需了解整个项目中各类资源文件的具体大小。在 Unity 完成相关编译后,选择 Console -> Open Editor Log 选项,可获取各类资源的总大小信息,开发者可按文件大小确定优化顺序。在脚本大小优化方面,根据项目组具体情况,可考虑选择.Net 2.0 Subset,在 IOS 平台也可使用 Stripping 方法。同时,建议删除 Resources 文件夹下未使用的资源,用 Assetbundle 代替需要使用的资源,因为 Resources 文件夹下文件过多会严重影响程序的启动时间。
工具 - Tools
在进行性能优化时,有许多工具可帮助开发者快速定位具体问题,包括引擎自带工具和第三方工具,例如:
- Profiler
- Memory Profiler
- Frame Debugger
- Test Runner
- Mac OS -> Instruments
- Allocations, Time profiler
最后,Unity 的官方手册是 Unity 开发者进行性能优化的重要参考资料。当遇到问题无计可施时,不妨参考官方手册。
来源:unity 官方平台