Unity 性能优化-代码
1、GameObject本机-托管桥接
与C#对象相比,GameObject和MonoBehaviour是特殊对象,因为它们在内存中有两个表示:一个表示存在于管理C#代码相同系统管理的内存中,C#代码是用户编写的(托管代码),另一个表示存在于另一个单独处理的内存空间中(本机代码)。数据可以再这两个内存之间移动,因此每次移动都会导致额外的CPU开销和 可能的额外内存分配,这种效果一般称为跨越本机-托管的桥接。
由以上理论,触发这种额外开销的有以下两种常见情况:
对GameObject空引用检查
一般我们使用以下方式对GameObject空引用检查:
if(gameObject!=null){//DoSomething }
另一种更好地方式是利用System.Object.ReferenceEquals(),其运行速度大约是上边的两倍:
if(!System.Object.ReferenceEquals(gameObject,null)) {//DoSomething }
以上方式也适用于MonoBehaviour。
GameObject的字符串属性
从GameObject中检索字符串属性是另一种意外跨越本机-托管桥接的方式。通常使用的两个属性是tag和name,因此使用这两个属性是不好的,然而GameObject提供了
CompareTag()
方法,它则完全避免了本机-托管的桥接。即使用gameObject.CompareTag("tag")而不是使用gameObject.tag=="tag"。除此之外,name属性没有对应方法,因此尽可能使用Tag属性。
2、获取组件优化
Unity中获取组件GetComponent()有3个可用的重载,分别是GetComponent(string),GetComponent< T >()和GetComponent(typeof(T))。在这三个方法中,最好使用GetCompnent< T >()重载。
此外,GetComponent()方法也不应该运用在类似Update()逐帧计算中,最好的方法是初始化过程中(Awake或Start等)就获取引用并缓存它们,直到需要使用它们为止。同样的技巧也适用于在运行时决定计算的任何数据,不需要要求CPU在每次执行Update()时都重新计算相同的值,因此可以提前将其缓存到内存中。
3、移除空的回调定义
在MonoBehaviour脚本中常用其周期函数,常用的有Awake()、Start()、Update()、FixedUpdate()等,这些回调函数会在场景第一次实例化时添加到一个函数指针列表中,又因为在所有的Update()回调(包括场景中所有的MonoBehaviour)完成之前,渲染管线不允许呈现新帧,因此当场景中有大量MonoBehaviour脚本时(包含空的Start()或Update()),场景的初始化以及每帧都会严重消耗资源从而影响帧率。因此我们需要在编写脚本时注意删除空的周期函数,例如Start(),Update()等。
4、运行时修改Transform的父节点
Transform组件的父-子关系比较像动态数组,因此Unity尝试将所有共享相同父元素的Transform按顺序存储在预先分配的内存缓冲区中,并在Hierarchy窗口中根据父元素下面的深度进行排序。这种数据结构允许整个组中进行更快的迭代,对于物理和动画等多个子系统有利,但是如果将一个GameObject的父对象重新指定为另一个对象,父对象必须将子对象放入预先分配的缓冲区中,并根据新的深度对所有Transform进行排序。另外如果父对象没有预先分配足够的空间,就必须扩展缓冲区。对于较深、复杂的GameObject结构,这需要一些时间来完成。
通过GameObject.Instantiate()实例化新的GameObject时,想为其设置一个父物体,在我们使用时很多情况会写成类似以下代码:
GameObject listItem = (GameObject)Instantiate(Resources.Load("Prefabs/UI/Items/PersonListItem"));
listItem.transform.SetParent(m_PersonSelectContnt, false);
以上情况在listItem实例化之后立即将Transform的父元素重新修改为另一个元素,它将丢弃一开始分配的缓冲区,为了避免这种情况,应该将父Transform参数提供给GameObject.Instantiate()
调用,这调用可跳过这个缓冲区分配步骤,从而提升一部分性能:
GameObject listItem = (GameObject)Instantiate(Resources.Load("Prefabs/UI/AMMT/Items/PersonListItem", m_PersonSelectContnt, false));
5、减少对Transform的改变
不断更改Transform组件属性,也同时会向其他组件(如Collider、Rigidbody、Light、Camera等)发送内部通知,这些组件也必须进行处理,因为物理和渲染系统都需要知道Transform的新值,并相应进行更新。
在复杂的过程中,在同一帧中多次替换Transform组件的属性很常见,每次Transform发生改变时,都会触发内部消息。因此,应该尽量减少修改Transform属性的次数,方法是将其变化缓存在一个成员变量中,只在帧的末尾修改Transform值,如下所示
6、避免在运行时使用Find()和SendMessage()
SendMessage()
和GameObject.Find()
方法非常昂贵,应不惜一切代价尽量避免使用。Find()会迭代场景中的每个GameObject对象。不过,在场景初始化期间调用Find()有时是可以的,例如在Awake()或Start()中。
7、Vector计算
在进行向量乘法计算时,有一点需要注意乘法顺序,因为向量乘比较耗时,所以我们应该尽可能减少向量乘法运算。可以基于之前CustomTestTimer来做一个实验:
private void Start(){int numTests = 1000000;using (new CustomTestTimer("向量在中间", numTests)){for (int i = 0; i < numTests; i++){Func1();}}using (new CustomTestTimer("向量在最后", numTests)){for (int i = 0; i < numTests; i++){Func2();}}}private void Func1(){Vector3 a = 3 * Vector3.one * 2;}private void Func2(){Vector3 a = 3 * 2 * Vector3.one;}
最终结果如下:
由结果可以看出,以上两个方法结算结果相同,但是Func2却比Func1耗时少,因为后者比前者少了一次向量乘法。所以,应该尽可能合并数字乘法,最后再进行向量乘。
8、循环
在迫不得已需要写多重循环时,应该尽量把遍历次数较多的循环放在内层。做测试如下:
private void Start(){int numTests = 10000000;using (new CustomTestTimer("大循环在外", numTests)){for (int i = 0; i < numTests; i++){for (int j = 0; j < 2; j++){int k = i * j;}}}using (new CustomTestTimer("大循环在内", numTests)){for (int i = 0; i < 2; i++){for (int j = 0; j < numTests; j++){int k = i * j;}}}}
测试结果如下:
9、MonoBehaviour
非必要不要继承MonoBehaviour,因为MonoBehaviour中有很多unity定义的属性方法。实例化出来用不到,就是浪费。
10、拆装箱
减少拆装箱操作,相同操作方法用泛型
一种常规易造成拆装箱的写法:
string str = string.Format("{0}", 1);
上面会造成拆装箱,下面的写法就没问题
string str = 1.ToString();
注意:string str = string.Format("{0}", i.ToString());依然会有拆装箱
11、string 与 stringBuilder
多次操作字符串使用stringBuilder,对string每次操作都会生成新的对象,从而产生GC垃圾。
stringBuilder如果能确定长度,最好先指定长度。
12、结构体
对于值类型的集合结构使用结构体。占的是栈内存,栈内存效率较高
但栈内存总容量也就是1M,而且在进行形参传递时会进行值拷贝
13、系统自带默认值
Vector3 v3 = new Vector3(0,0,0); 与 Vector3 v3 = Vector3.zero;使用Vector3.zero更好,因为减少了一次new的过程。Vector3.zero全局只有一个。
Unity 性能优化-代码相关推荐
- Unity性能优化 – 脚本篇
最近开始进行Unity性能优化的工作,主要分为三类:CPU.GPU和内存.由于我们游戏的核心战斗是计算密集型,所以主要是受限于CPU.CPU的优化又分为渲染和脚本,本文将着重于脚本优化. 一般来说,优 ...
- Unity性能优化(2)-官方教程Diagnosing performance problems using the Profiler window翻译
http://www.cnblogs.com/alan777/p/6135703.html Unity性能优化(2)-官方教程Diagnosing performance problems using ...
- 【Unity性能优化】静态资源优化——Audio优化
文章目录 写在前面 1. 前言 2. 使用Asset Checker进行资源检测 3. Audio优化 3.1 启用Force to Mono 3.2 压缩格式与采样率 3.3 音乐加载类型 3.4 ...
- Unity性能优化 :合批篇
前言 本系列为一些性能优化的小知识,是日常游戏开发中与性能表现的一些点,本篇为该系列文章的第二篇,前篇链接: 第一篇: Unity性能优化:资源篇 在早期Unity中,对于合批的处理手段主要是下面三种 ...
- Unity 性能优化:资源篇
Unity性能优化 大的方面来说,通过Unity对于项目的性能优化大概可以分为下面几个部分: 资源 渲染 程序 项目配置 而在这个部分中,资源的性能优化属于最基础.最有效的优化手段,也是游戏开发者日常 ...
- Unity 性能优化(力荐)
开始之前先分享几款性能优化的插件: 1.SimpleLOD : 除了同样拥有Mesh Baker所具有的Mesh合并.Atlas烘焙等功能,它还能提供Mesh的简化,并对动态蒙皮网格进行了很好的支持. ...
- Unity性能优化之编辑器检查——贴图
优化选项 图片资源一般可做如下优化设置 打包图集 mipmap不必要时选择关闭 Read/Write Enabled不必要时关闭 纹理压缩 图集打包的可以参考Unity性能优化之图集打包:mipmap ...
- Unity - 性能优化 - 包体,内存 - 偏静态资源的优化
文章目录 静态资源优化 - AssetPostprocessor Texture 压缩 Model 网格.动画 压缩 音频压缩 纹理的优化经验 尺寸 通道 发布出来的包资源再次分析 如何工具快速定位静 ...
- Unity性能优化大合集,All In One !(更新至8.18)
引擎把握篇 性能卡顿.内存泄露.资源冗余...这些开发中遇到的疑难杂症大部分都能在这里找到解决方法.这些经验总结无不通过大量的项目深度优化和周而反复的测试验证.如果你想避开一些不必要的坑,请务必啃下这 ...
最新文章
- 2021年大数据Spark(一):框架概述
- python3 练习3
- html5学习笔记1
- Spring Cloud构建微服务架构:分布式服务跟踪(入门)
- MySQL查询日志介绍
- springmvc学习笔记--Interceptor机制和实践
- Python 调用 DLL
- 网站程序安全分析器 VB源码
- 为什么mysql查询结果有前缀_字符串的公共前缀对Mysql B+树查询影响回溯分析
- react native webview 百度地图_react-native-baidu-map使用及注意问题
- 转为html5播放器插件,15个HTML5播放器插件
- html博客音乐播放器代码大全,播放器代码大全
- 本地差分隐私 随机响应_本地化差分隐私:如何面对非可信的世界
- java随机生成数字和字母_使用java如何生成随机的字母数字字符串?
- 断句规则 Segmentation Rule
- 使用python库relate搭建LMS学习管理系统
- Android判断手机号码是否是正确的手机号码
- Landmark Guidance Independent Spatio-channel Attention and Complementary ContextInformationbased FER
- 获取Android设备的唯一识别码|设备号|序号|UUI
- 【深进1.例1】求区间和
热门文章
- CTGU-2023春-MySQL数据库实验4_2-视图45
- Insert 28, 23, 54, 61, 98, 37 into an initially empty AVL tree first. Then immediately insert one of
- 贝叶斯推理:一个更有趣的例子
- linux 部署调用SAP接口
- 腾讯笔试、OMG一面、二面、HR面
- 2.2. nmtui
- JSOI 2018 Round 1 游记
- Chrome 开发者工具介绍
- RK3399平台入门到精通系列讲解(导读篇)资料下载与介绍
- badbody录制脚本