Unity 代码编写规范

  1. 纹理优化
  2. 渲染优化
  3. CPU优化​
  4. 合理使用AssetBundles
  5. Build AssetBundles
  6. AssetDatabase.ImportAsset
  7. 预处理和后处理函数
  8. Build Player
  9. 项目发布策略

#优化标准
诚然每个游戏的标准不同,但是大体上面是有一个测试标准的.
这个标准应该称之为行业内标准,我们可以根据这个标准作出在合理范围内上下浮动就证明这个游戏是比较差,中,好.

##代码优化标准:

  • 1 尽量避免使用Lambda表达式:存在内存泄露隐患。

  • 2 尽量避免使用LINQ:部分功能无法在某些平台上使用,且会分配大量GC Alloc。

  • 3 控制StartCorountine()的次数:开启一个协程,至少分配37B的内存(Coroutine类的实例 - 21B;Enumerator - 16B)。建议:少用/不用协程,创建一个总协程,全局只使用一个协程等.

  • 4 使用StringBuilder来代替String进行字符串拼接:StringBuilder.Append方法在拼接字符串时,变化总是发生在同一个内存块中;而String + String + …这种字符串拼接方式,会频繁申请内存并释放,导致GC频繁调用。

  • 5 缓存组件:每次GetComponent均会分配一定的GC Alloc;每次获取对象名称Object.name(实际为Object.get_name())会分配39B(具体大小视实际情况而定)的堆内存。建议:提前获取,不要在循环体中获取,最好不要使用Object.name等.

  • 6 避免使用开销大的函数:如GameObject.Find、GameObject.FindWithTag、Object.FindObjectOfType等查询方法。

  • 7 使用GameObject.CompareTag代替GameObject.tag:GameObject.tag会在内部循环调用对象分配的标签属性并分配额外的内存。

  • 8 使用ObjectPool对象池来管理对象:避免频繁的Instance和Destroy,这两个操作都挺耗性能的。

  • 9 延时操作总能降低峰值.尽量不要在Update()函数中做复杂计算:如若需要,可以隔N帧计算一次。

  • 10 递归绝不推荐,尽量减少函数调用栈:如用x = (x > 0 ? x : -x)代替x = Mathf.Abs(x)。减少 while(true){}里面循环一直调用的方式,除非在非常重要的功能上面,比如战斗.

    纹理优化

    ● 纹理压缩格式:iOS使用PVRTC格式;Android使用ETC格式;Windows使用DXT格式。

     ● 凡是3D场景,都应该尽可能启用MipMaps。如果硬件MipMaps无法满足效果要求:1、使用自己生成的Mipmaps;2、使用Aniso。
    
     ● 如果纹理用于UI或2D场景,禁用MipMaps。
    
     ● 通过减色的方式降低纹理大小。

    ##内存优化
    ● 建议总内存控制在150MB以下;堆内存控制在40MB以下。

     ● 切换场景时避开内存峰值。当前一个场景还未释放就切换到新场景,这时由于两个场景的内存叠加容易造成内存峰值。可以采用在屏幕中间遮盖一个Loading场景,在旧场景释放完成,并且新场景初始化结束后,再隐藏Loading场景。
    
     ●​ 使用Prefabs(凡是场景物体,都应该制作成Prefabs)。
    
     ● 通过ObjectPool对象池来避免内存的频繁操作,从而避免内存碎片影响到大内存块的申请;尝试在切换场景时不释放公共UI资源。
    
     ●​ 通过LoadManager,保证在同一时间段内仅载入一个WWW对象,实现顺序加载。
    
     ●​ 纹理内存占用过大:峰值 < 50MB。
    
     ●​ 内存泄露:内存需保持升降一致,避免泄露。
    
     ●​ 网格资源内存过大:峰值 < 20MB。
    
     ●​ 动画片段:内存 < 15MB,警惕资源泄露。
    
     ●​ 音频资源:内存 < 15MB,警惕资源泄露。​

    渲染优化

     ●​ DrawCall数量:峰值 < 250,主体范围(5% - 95%)应在(0 - 200)个之内。
    
     ● Triangle数量:峰值 < 100000/帧。
    
     ● VBO上传量:峰值 < 5MB,频繁加载的资源使用缓存池缓存。
    
     ●​ Skinned Mesh数量:峰值 < 50,对应Profiler中的MeshSdinning.Update、Animator.Update。

​ ● RenderTexture:数量 < 10。

CPU优化​

  ●​ Rigidbody数量:峰值 < 50/帧,对应Profiler中的Physics.Simulate。

   ● 碰撞体数量:峰值 < 100/帧,对应Profiler中的Physics.Simulate。

合理使用AssetBundles

● 采用依赖性打包

●​ Assetdatabase.GetDependencies

●​ Push / Pop

● WWW vs. CreateFromFile

● WWW常被用于加载采用Unity标准压缩方法压缩过的AssetBundles。

●​  CreateFromFile只能加载非压缩格式的本地AssetBundles;可以先采用其他压缩格式进行压缩,下载到本地后自行解压,再行加载和使用。

● AssetBundle.mainAsset的功能往往被忽视

● 它可以是标准的资源asset,用来存储AssetBundles中最重要的资源文件。

●  也可以是TextAsset,其中可以包含任意内容,推荐用来记录每个AssetBundle中的物体列表和信息描述。

Build AssetBundles

● 问题:当修改AssetBundles中的内容时,需要重新Build这些AssetBundles才能使其生效。这个过程往往非常耗时并影响开发效率。

● 开发阶段和发布阶段使用不同的策略:

●​ 为了提高在Unity Editor中反复修改AssetBundles的调试效率,推荐开发时使用Resources.LoadAssetAtPath来避免重新建立AssetBundles。

●​ 在发布时才使用唯一的脚本打包AssetBundles。

AssetDatabase.ImportAsset

● AssetDatabase.ImportAsset有三个重要事实:

●​ 它是将一个外部asset转化为Unity内部能够识别的internal asset,但并不实际在内存中加载asset,更没有返回值。

● 它会根据AssetImporter中的相关设定对asset的相关属性进行修改,尽管两个API之间没有任何的参数传递。

● 它常常被用于在调用BuildPipeline.BuildAssetBundle前强行重新导入一个asset。

●​ 几种常用的AssetImporter类型:TextureImporter、ModelImporter及MovieImporter。

预处理和后处理函数

● AssetPostprocessor的作用

●​ AssetPostprocessor可以在import每个asset之前和之后分别自动触发AssetPostprocessor.OnPreprocessXXX和AssetPostprocessor.OnPostprocessXXX。

●​ 方便用户介入和修改资源导入的过程。

Build Player

● 自动编译Player

●​ 支持命令行进行编译。

● 编写编译脚本,并放在Assets/Editor目录下。

●​ 脚本中声明一个类,包含一个静态方法,通过BuildPipeline.BuildPlayer来实现编译工作。

● 编译后处理脚本

●​ Unity支持在编译好可执行程序之后,通过后处理脚本对其进行一定的修改,例如制作安装文件等。

    ●​ 在Windows平台下,可以在编辑器脚本(Editor Script)中使用BuildPipeline.BuildPlayer方法来进行编译,并在其后添加任意的后处理代码。

    ●​ 在Mac平台下,Unity在编译工程后会自动查找一个名为PostprocessBuildPlayer的后处理脚本文件,用于实现编译后处理;后处理脚本可以是sh、perl或任意与命令行兼容的语言。

项目发布策略

● 对移动芯片的支持

● 从Unity 4.0开始,Unity停止了对ARM V6架构的支持。

●​ Unity 3.5.x版本仍旧支持ARM V6架构。

● Graphics Emulation

●​ 在不使用实际硬件设备的前提下,在Unity编辑器中就可以模拟测试不同图形硬件对当前项目中所使用的图形API的支持能力(对Editor->Graphics Emulation进行设置)。

●​ 发布包的代码保护

●​ Unity脚本在iOS平台下是安全的,与是否使用AssetBundle无关。

●​ 对于Android和其他平台,可以采用AssetBundle + TextAsset的方式进行,也可以采用代码混淆的方式进行。

●​ 在所有平台下都可以使用Native DLL。

● 发布包的代码加密

● 需通过AssetBundle和TextAsset来实现,即把代码编译为TextAsset,并打包入AssetBundle中,参考官方文档:“Including scripts in AssetBundles”一节:[http://docs.unity3d.com/Documentation/Manual/scriptsinassetbundles.html](http://docs.unity3d.com/Documentation/Manual/scriptsinassetbundles.html)

● 其中TextAsset并不是真正意义上的Text,而是编译好的dll文件,是以binary形式存在的。参考官方文档“Text Asset”中的“Binary data”一节:[http://docs.unity3d.com/Documentation/Components/class-TextAsset.html](http://docs.unity3d.com/Documentation/Components/class-TextAsset.html)

● 发布包的美术资源加密

●​ 对于所有平台,最常见的是对贴图进行加密,通过AssetBundle和TextAsset来实现。

● 程序发布后的增量更新

●​ 原理:Unity脚本经过编译后就不能再修改,但是可以添加。

●​ 方案1:将脚本与资源全部分离。

    ●​ 项目管理非常麻烦。

    ●​ 动态挂载所有脚本。

    ●​ 无法在编辑器中使用动态编辑。

● 方案2:将接口与实现分离。

    ●​ 不改变用户工作流程。

    ●​ 仍可在编辑器重使用动态编辑。

    ●​ 对代码起到一定的保护作用(可以混淆)。

    ●​ 对原有的AssetBundles打包方案没有任何影响。

FPS 帧率
高端机 60 帧左右
低端机/中端机统一 30 帧(包含)以上
低端机以红米 note4 为准,CPU 型号是 Helio X20
低于 24 帧必定要做优化

#UWA内存优化
UWA有 4 种测试模式

  • 模式 1 Overview
  • 模式 2 Mono 由于目前项目大部分采用 il2cpp,所以这个模式会被逐渐放弃.但是即使你采用了il2cpp 模式也有必要去使用 mono 模式打包,并去测试,不要嫌麻烦.
  • 模式 3 Assets 资源检测,检测资源重复,是否耗费运行资源过大.
  • 模式 4 Lua 检查 Lua,收费模式

#UPR内存优化


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1487842110@qq.com

Love

Title:Unity 代码编写规范

文章字数:2.3k

Author:诸子百家-谁的天下?

Created At:2020-05-08, 11:41:32

Updated At:2021-03-28, 02:59:27

Url:http://yoursite.com/2020/05/08/Unity/Optimize/%E4%BB%A3%E7%A0%81%E7%BC%96%E5%86%99%E8%A7%84%E8%8C%83/

Copyright: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

爱你,爱世人