Unity中简单的优化物理系统

2016年09月20日 12:13 0 点赞 0 评论 更新于 2025-11-21 20:28
Unity中简单的优化物理系统

在使用Unity开发游戏时,物理系统相关的问题常常困扰着开发者,比如:

  • 我的游戏物理系统有什么错误吗?
  • 怎么去处理游戏物理系统?
  • Unity物理系统有什么缺陷吗?
  • Unity是怎样处理物理系统的?
  • 我在Unity物理系统上做了什么控制?
  • 在我开始使用游戏物理系统之前我应该关心什么事情?
  • 我应该在什么时候避免使用物理系统?
  • 我看到了帧速率在下降,这是否是物理系统导致的?

物理学或许并非每个人钟爱的学科,但在游戏开发领域,它却扮演着至关重要的角色。设想这样一种情形:你决定开发一款引人注目的大型游戏,具备逼真的物理效果和精美图像。当设计完成、架构搭建好,一切看似准备就绪,你开始着手处理最棘手的“物理系统”时,可能会遭遇诸多问题,如低FPS、奇怪的物体移动、碰撞器/触发器故障以及高CPU使用率等。不恰当或不正确地使用物理系统,不仅会影响游戏的可玩性,还可能让玩家望而却步。

物理系统是游戏开发中既困难又关键的部分,无法回避。有人认为“好的物理系统需要一个超快的CPU”,但实际上并非总是如此。多数情况下,我们可以深入学习Unity的物理系统工作原理,从而实现更优的物理效果。

我初涉编程时,曾处理过100多个与物理相关的事务,为此花费了近一年时间总结处理物理问题的关键要点。因此,我撰写这篇文章,旨在帮助大家跳过学习阶段的痛苦,成为专业的物理系统开发者。

本文不会详细讲解Unity物理系统的工作原理,而是着重介绍优化物理系统的技巧和要点。如果你是新手,建议先大致了解一下Unity物理系统。由于物理学概念广泛,我将分不同部分进行阐述,力求简洁明了。接下来,让我们开启这段有趣的优化之旅!

降低固定时间步(Fixed Timestep)

在Unity文档中,固定时间步的定义为:“一个不受帧速率影响的时间间隔,用于指定在FixedUpdate()函数中执行物理计算每一帧的时间间隔”。其默认值为0.02(每秒),即每20ms(毫秒)执行一次物理更新,同时FixedUpdate()函数也会每20ms调用一次。

你需要不断调整这个值以达到理想效果。例如,开发简单的卡牌游戏时,不需要频繁使用物理系统,可适当减少物理引擎的调用次数,但操作需谨慎。若减少过多,可能无法获得预期的物理效果。

下面通过一个案例来深入理解Fixed Timestep:

  1. 创建球体:创建3 - 4个球体,并保持一定距离。
  2. 创建物理材质:在Assest文件夹中右键选择Create -> Phycics Material,设置摩擦系数(Friction Amount)为0,弹力系数(Bounciness)为1,弹力混合(Bounce combine)为最大值。
  3. 添加物理材质:将创建的物理材质添加到球体碰撞器的物理材质卡槽中。
  4. 添加刚体组件:为球体添加刚体组件,使其成为物理对象。
  5. 创建平面并添加材质:创建一个平面,为其创建物理材质(先使用默认值),并添加到平面上。
  6. 设置球体位置和重力:让球体位于平面上方,勾选球体的重力选项。
  7. 运行游戏查看结果:点击Play按钮,观察球体的运动情况。

此时,球体做上下运动。接下来,我们调整Fixed Time Step的值。找到Edit >> Project Settings >> Time,其中Fixed Time Step的默认值为0.02。将其设置为0.1,再次点击Play按钮运行游戏。你会发现球体运动变慢,甚至穿过平面而不反弹。这是因为将Fixed Time Step设置为0.1意味着每100ms执行一次物理更新,此时可能无法及时检测到碰撞。

这表明过度降低Fixed Time Step并不合适。我们可将其设置为0.03 - 0.04(具体根据球体情况而定)。再次运行游戏,可能表面上看不到明显变化,但仔细检查会发现物理碰撞检测有细微差异。

物理碰撞检测会有一定延迟,需在当前帧结束后才会反弹。在正常FPS下,这种延迟可能不易察觉,但会影响物理效果。不过,只要能满足游戏需求,这种延迟是可以接受的。通过合理设置Fixed Time Step,节省物理计算的资源可用于渲染和其他计算密集型处理,从而提升游戏性能。

提示:若你在操作过程中遇到困难,可参考Unity的官方文档,深入理解Unity物理引擎是很有必要的。

通过设置Maximum Allowed Timestep在物理系统中保持检查

在Unity文档中,Maximum Allowed Timestep的定义为:“一个不受帧速率影响的时间间隔,当帧率降至最低峰值时,物理计算和FixedUpdate()事件将不会被执行”。

在正常游戏运行时,若游戏能保持60 FPS,意味着每一帧执行时间为0.01666秒(即16.7ms)。将Fixed TimeStep设置为0.01,表明每10ms执行一次物理更新,此时每一帧至少会调用一次物理更新(因为10ms < 16.7ms)。假设由于某些原因帧率降至30 FPS,每一帧执行时间变为0.0333秒(即33.3ms),则每一帧会调用3次物理更新(一次物理更新花费10ms)。若帧率继续下降,每帧内的物理调用次数会更多,这可能导致程序崩溃。

为解决此问题,引入了Maximum Allowed TimeStep。其默认值存在争议(可能文档有误,实际应为0.33333秒,即333.33ms)。根据定义,当物理更新超过指定时间,物理更新将停止,从而为其他进程节约资源。

例如,若每一帧执行时间增加到40ms,会调用更多物理更新。但将Maximum Allowed TimeStep设置为0.033(即33ms)后,物理更新在执行3次(共30ms)后,即使每帧执行时间超过50ms也会停止调用,为其他进程节省了资源。

不过,使用Maximum Allowed TimeStep也存在一些负面影响。当发生性能故障时,动画和物理效果会变慢,出现画面卡顿、延迟现象。因此,在使用时需牢记这是一个重要因素,合理设置可获得良好效果。

总是保持1 - 1 - 1的比例

对于没有物理相关组件的对象,进行放大操作没有问题。但对于物理对象,不建议进行缩放。缩放会导致奇怪的碰撞检测结果,还会影响物体的下落方式。例如,一块大石头在无空气阻力的情况下会快速落地,但如果周围物体都被放大,大石头的下落看起来会变慢。

虽然可以通过调整重力加速度的值来使物体下落看起来正常,但这并非最佳做法。正确的方式是保持1 - 1 - 1的缩放比例,因为Unity的物理引擎在这种情况下性能最佳。

给你的对象设置恰当的质量

和缩放一样,物体的质量也需要精确设置。在Unity中,若假定1个单位长度为米,那么1个单位的质量为1kg。Unity的物理系统是无量纲的,但遵循这样的度量系统准则能获得更理想的结果。同时,Unity在努力解决高浮点数问题,尽量削减取值范围是较好的解决方案。

例如,若飞机质量设为1kg,那么轮子的质量最好不超过1/1000 kg。

尽可能避免使用网格碰撞器(Mesh Collider)

基于网格的碰撞检测比原始的碰撞检测需要更多的计算量,这是物理引擎的普遍特性,Unity内部使用Nvidia的PhysX也不例外。一般来说,不同碰撞检测的相对成本从高到低排序为:三角形网格、凸包(可参考百度百科)、胶囊体、球体、盒子(六面体)、平面、点。

通常,网格碰撞器(Mesh Collider)会被标记为凸包(也建议这样设置),且限制为255个三角形。只有当两个网格碰撞器都标记为凸面(Covex)时,才能相互进行碰撞检测。

根据Unity文档说明,未标记为凸面的网格碰撞器仅支持在没有刚体组件的游戏对象上发生碰撞;若要在刚体上使用网格碰撞器,必须将其标记为凸面。

由于网格碰撞体比传统的碰撞器(如球体、立方体、胶囊体)带来更多的计算负载,因此在理想情况下,应尽量避免使用。

作者信息

孟子菇凉

孟子菇凉

共发布了 3994 篇文章