Unity 脚本优化的那些坑

本文探讨了Unity脚本优化中的常见误区,尤其是关于GetComponent方法和属性访问器的性能影响。尽管频繁调用GetComponent会增加CPU开销,但并未直接导致GC。官方建议减少GetComponent的使用,而测试表明GetComponent<>()在5.x版本中效率最高。属性访问器如transform和gameObject的性能良好,缓存它们对优化帮助有限。测试结果基于Unity 5.x,不同版本可能有所差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

在很长一段时间里,Unity项目的开发者的优化指南上基本都会有一条关于使用GetComponent方法获取组件的条目(例如14年我的这篇博客《深入浅出聊Unity3D项目优化:从Draw Calls到GC》)。有时候还会发展为连一些Unity内部对象的属性访问器都要小心使用的注意事项,记得曾经有一段时间我们的项目组也会严格要求把例如transform、gameobject之类的属性访问器进行缓存使用。这其中的确有一些措施是有道理的,但很多朋友却也是知其然而不知其所以然,朦胧之间似乎有一个印象,进而成为习惯。那么本文就来聊聊Unity优化这个题目中偶尔会被误解的内容吧。

来自官方的建议

本文主要是关于Unity脚本优化的,而脚本和引擎打交道的一个常见情景便是使用GetComponent之类的方法, 接触过Unity的朋友大都知道要将GetComponent的结果进行缓存使用。不过很多人的理由是:

使用GetComponent会造成GC,从而影响效率。

所以从Unity官方的手册来寻找关于GetCompnent的线索是最好的途径。的确,2011年的3.5.3版本的官方手册就已经建议减少使用GetCompnent方法来获取组件了,同时建议我们使用变量缓存获取的组件。

Reduce GetComponent Calls
Using GetComponent or built-in component accessors can have a noticeable overhead. You can avoid this by getting a reference to the component once and assigning it to a variable (sometimes referred to as “caching” the reference).

但是,我们可以发现手册上只说了频繁的调用GetComponent会导致CPU的开销增加,但是并没有提到GC的问题。所以,为了验证GetComponent到底会导致哪些性能上的问题,我们可以做几个小测试。

和GC无关的性能优化

众所周知,GetComponent有三个重载版本,分别是:

  • GetComponent()
  • GetComponent(typeof(T))
  • GetComponent(string)

所以,测试的第一步就是先确定一个效率最高的重载版本,之后再去检查它们各自引起的堆内存分配。

“效率之王”

为此,我们在5.X版本的Unity中准备一个空白的场景并实现一个简单的计时器,之后就可以开始测试了。

using System;
using System.Diagnostics;

/// <summary>
/// 简易的计时类
/// </summary>
### 创建方形地形的方法 在 Unity 中创建特定形状的地形,比如方形,可以通过多种方式实现。一种常见的方式是通过编程自定义地形的高度图数据来形成所需的几何结构。 对于想要创建具有方形特征的地形,在编辑器中手动雕刻是一个直观的选择;然而,如果追求程序化生成,则可以基于给定算法调整顶点位置以模拟挖掘效果[^1]。具体来说: - 首先按照常规流程建立基础平坦地形; - 接着定位准备开挖区域中心坐标以及设定好洞尺寸参数; - 计算该区域内各点的新高度值使其低于周围环境从而构成凹陷形态; - 应用这些更改后的数值更新对应网格节点的位置属性完成最终造型塑造。 下面给出一段用于演示如何利用C#脚本来改变指定范围内地形高度的例子,这能够帮助理解怎样编码实现上述概念中的“降低某些部分”的操作: ```csharp using UnityEngine; public class CreatePit : MonoBehaviour { public Terrain terrain; private float[,] heights; // 存储原始高度信息 void Start() { heights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight); int centerX = (int)(terrain.terrainData.size.x / 2); int centerZ = (int)(terrain.terrainData.size.z / 2); DigSquareHole(centerX - 5,centerZ - 5 ,centerX + 5,centerZ + 5,-3f); SetTerrainHeights(); } /// <summary> /// 在指定范围(xMin,zMin)-(xMax,zMax)之间设置新的较低高度level /// </summary> void DigSquareHole(int xMin,int zMin,int xMax,int zMax,float level){ for (int x=xMin;x<xMax;++x){ for (int z=zMin;z<zMax;++z){ if ((float)x/terrain.terrainData.size.x * heights.GetLength(0)<heights.GetLength(0)&& (float)z/terrain.terrainData.size.z * heights.GetLength(1)<heights.GetLength(1)){ heights[(int)((float)x/terrain.terrainData.size.x*heights.GetLength(0)),(int)((float)z/terrain.terrainData.size.z*heights.GetLength(1))] += Mathf.Clamp(level-heights[(int)((float)x/terrain.terrainData.size.x*heights.GetLength(0)),(int)((float)z/terrain.terrainData.size.z*heights.GetLength(1))],-Mathf.Infinity,0); } } } } void SetTerrainHeights(){ terrain.terrainData.SetHeights(0, 0, heights); } } ``` 此代码片段展示了如何在一个既有的`Terrain`对象基础上,针对其中心附近的一个正方形区域应用下沉处理,进而形成了一个简易版本的“”。需要注意的是实际项目开发过程中可能还需要考虑更多细节因素如边界条件判断、性能优化等问题[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值