北京市网站建设_网站建设公司_营销型网站_seo优化
2026/1/19 22:57:04 网站建设 项目流程

unity性能优化-实际开发中需要注意的点

性能优化建议

在进行下面优化之前,建议先使用 Unity Profiler 定位真实的瓶颈。

优化核心原则:

能缓存的就缓存(变量、组件、材质)。

能不更新就不更新(降低 update 频率,使用事件驱动或Coroutine)。

避免在循环中分配内存(减少 new 关键字,使用对象池)。

1.复杂数学计算,比如mesh顶点数据的创建,大小和归一化属性都非常耗费 CPU(都涉及计算平方根)

可以使用对象池来空间换时间处理,涉及开根号的用平方比较

2.shader的合批处理

3.一些List,Vector不要用集合,而是指定它的大小

4.不要再update里面去大量调用Component(新手容易忽略重要)

5.优化字符串对比

6.常量的字符串写成类的成员变量

7.裁剪引擎,去掉音频

8.裁剪引擎,去掉物理,注意此时包括包围盒都不能使用

9.固定帧率,减少帧率

10.避免使用拆箱装箱

在C#中,装箱(Boxing)和拆箱(Unboxing)是指值类型与引用类型之间的转换。装箱是将值类型转换为对象类型(通常是将值类型包装在堆上),而拆箱则是从对象类型还原为值类型。

装箱(Boxing)开销

内存分配

装箱过程中,会在堆上分配内存来存储值类型的数据。堆内存的分配通常比栈内存的分配慢,因为堆内存需要处理垃圾回收(Garbage Collection),而且堆内存分配涉及更多的管理操作。

数据复制

装箱需要将值类型的数据从栈上复制到新分配的堆对象中。这涉及到数据的复制操作,增加了额外的开销。

垃圾回收

装箱产生的堆对象最终会由垃圾回收器(GC)回收。垃圾回收是一个耗时的过程,会影响应用程序的性能。

拆箱(Unboxing)开销

类型检查

拆箱过程中,需要进行类型检查,以确保对象确实是预期的值类型。这涉及到运行时的类型元数据访问和检查,增加了开销。

数据复制

拆箱需要将堆上的数据复制回栈上。这同样涉及数据的复制操作,带来了额外的性能开销。

避免装箱和拆箱的方法

使用泛型:泛型可以减少装箱和拆箱的需要,因为泛型可以在编译时确定类型,而不需要在运行时进行类型转换,但是泛型带来另一个问题,即转换为il2cpp的时候,代码会膨胀,这个后面可以再说

C#

// 使用泛型避免装箱和拆箱
List<intnumbers = new List<int();
numbers.Add(42);
int number = numbers[0];

避免频繁的值类型到引用类型的转换:在设计程序时,尽量减少值类型与引用类型之间的转换。如果可能,尽量使用值类型本身来进行计算和处理。

使用struct而不是类:在某些情况下,可以使用struct来代替类,因为struct是值类型,不需要装箱和拆箱。

避免接口调用:接口调用通常需要装箱,特别是在接口接受值类型参数时。尽量避免在接口方法中使用值类型参数

11.GameObject.Instantiate 和 SetParent

C#
public class ESDRoadEdgePool
{
//public int amountToPool = 100; // 池中对象的数量
public int amountToPool = 25;

pooledObjects = new List<GameObject>();

for (int i = 0; i < amountToPool; i++)
{
GameObject obj = Instantiate(objectToPool); // 实例化可以缩减
obj.SetActive(false); // 可以预先设置预制体的activeSelf
obj.transform.parent = this.transform; // 可以合并到Instantiate中
pooledObjects.Add(obj);


GameObject obj = Instantiate(objectToPool, transform);
pooledObjects.Add(obj);
}
}

12.嵌套循环中new Vector3等

C#

for(int i = 0; i < count; i++)
{
...
Vector2 p1l_uv = new Vector2(offest.x, uvStart / 10f);
Vector2 p1r_uv = new Vector2(offest.y, uvStart / 10f);
Vector2 p2l_uv = new Vector2(offest.x, (uvStart + distance) / 10);
Vector2 p2r_uv = new Vector2(offest.y, (uvStart + distance) / 10);
...
}

// 可以将对象创建挪到循环外进行复用

Vector2 p1l_uv = new Vector2();
Vector2 p1r_uv = new Vector2();
Vector2 p2l_uv = new Vector2();
Vector2 p2r_uv = new Vector2();

for(int i = 0; i < count; i++)
{
...
// 同时可以使用Vector3.Set来一次性对向量的每个分量进行赋值
p1l_uv.Set(offest.x, uvStart / 10f);
p1r_uv.Set(offest.y, uvStart / 10f);
p2l_uv.Set(offest.x, (uvStart + distance) / 10);
p2r_uv.Set(offest.y, (uvStart + distance) / 10);
...
}

13.每帧循环调用的计算逻辑,可以使用对象池替代

C#

for (int i = 0; i < len - 1; i++)
{
...
SquareCell cell = new SquareCell();
...
}

// 加入对象池
public class SquareCellPool
{
public static SquareCell Allocate()
{
if (pool.TryDequeue(out var cell))
{
totalCount--;
return cell;
}

for (int i = 0; i < poolInterval; i++)
{
SquareCell newCell = new SquareCell();
pool.Enqueue(newCell);
totalCount++;
}

if (totalCount > 65535)
{
Debug.LogError("Pool Out of Count!" + totalCount);
return null;
}
cell = pool.Dequeue();
totalCount--;
return cell;
}
}

14.Shader中处理逻辑能不使用高精度可以使用低精度的

15.避免 Camera.main

在 Unity 中,Camera.main 实际上是调用了 GameObject.FindWithTag("MainCamera"),这是一个非常耗时的操作

发现unity文档里面,这块其实也是做了缓存gameobject

但是缓存肯定还是最快的,因为gameobject也有开销

本文档会陆续补充更新,优化无止境

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询