62.性能优化-CPU-物理-刚体
62.1 知识点
知识回顾:碰撞体、刚体、运动学刚体、碰撞检测类型
碰撞体:
不移动、但需与物理对象产生碰撞的物体,只需加碰撞器组件。
只带碰撞器的对象称为静态碰撞体。
刚体:
需要移动且有物理受力表现的物体,需同时添加碰撞器和刚体。
带刚体和碰撞器的对象称为动态碰撞体。
运动学刚体:
勾选 Is Kinematic 的刚体。
作用类似静态碰撞体,与其他对象碰撞时自身不受力,但可利用刚体 API 移动。
运动学刚体不会对撞击它的对象做出物理反应,运行时会简单把其他动态碰撞器推开,因此有时会将玩家对象设为运动学刚体。
碰撞检测类型(Collision Detection):
- Discrete(离散检测)
原理:每个物体只在固定物理步长的采样点检查是否与其他碰撞体重叠。
优点:性能最好,开销最小。
缺点:高速小物体可能在两帧之间跳过碰撞体,出现穿透。
适用:绝大多数常速移动的物体(角色、箱子、环境物件)。 - Continuous(连续检测)
原理:在两帧之间做运动轨迹扫掠检测(sweep test),以减少穿透。
优点:大幅降低高速物体穿透概率。
缺点:CPU 开销比 Discrete 高。
适用:非主角的小型高速物体(子弹、飞镖、投射物)。 - Continuous Dynamic(连续动态检测)
原理:比 Continuous 更严格,会检测所有 Continuous 和 Continuous Dynamic 的物体。
优点:最稳妥地避免穿透。
缺点:开销最高,不适合大规模使用。
适用:关键高速动态物体(Boss 大锤、主角飞行体)。 - Continuous Speculative(连续推测式检测)
原理:在下一帧前预测可能碰撞,比 Continuous 更轻量。
优点:比 Continuous Dynamic 省性能,能避免多数小物体穿透。
缺点:可能误判(没撞上也认为撞上),偶尔虚假碰撞。
适用:中高速物体,兼顾性能与准确性。
首选离散碰撞检测
对于物理类项目,刚体 Collision Detection 参数建议:
- 首选 Discrete(离散检测),性能消耗最低。
- 少数高速投射物(子弹、飞镖)用 Continuous。
- 关键高速动态物体用 Continuous Dynamic。
- 兼顾性能与准确性时,可尝试 Continuous Speculative,但需注意误判。
注意:
非物理类游戏建议不使用刚体和碰撞器做碰撞检测。
大部分商业游戏中,战斗伤害检测往往使用射线检测、范围检测或自实现逻辑。
解算器迭代次数
除在物理系统基础设置中全局设置外,还可对单个刚体设置迭代次数:
// 设置该对象解算器迭代次数,影响位置、旋转精度和稳定性
rigidbodyComp.solverIterations = 8;
// 设置该对象解算器速度迭代次数,影响摩擦、反弹的稳定性
rigidbodyComp.solverVelocityIterations = 2;
优化思路:
- 全局设置较低基线(如 4/1、6/1)。
- 对个别需要稳定的刚体(主角、载具)在刚体参数中局部上调。
不要因个别对象物理不稳定而调高全局解算器迭代次数,否则会增加所有对象的物理计算开销。
刚体相关 API 在 FixedUpdate 中调用
物理相关逻辑应在 FixedUpdate 中处理,尤其移动刚体时,应尽量使用:
MovePosition、MoveRotationAddForce、修改velocity
不要在 Update 中直接修改刚体的 Transform,这会增加性能开销。
原因:
在 Update 中改 Transform 相当于绕过物理系统的运动学路径,引擎不得不做额外的强制同步和状态修复。
Unity 物理的基本运行逻辑是:FixedUpdate → 物理世界更新 → Rigidbody 位置、旋转写回 Transform。
在 Update 里直接改 Transform 会导致:
- 破坏物理世界与 Transform 的一致性
Rigidbody 的物理状态(位置、速度、加速度、接触点等)与直接改的 Transform 脱节,Unity 必须在下次物理更新时发现并修正。 - 强制触发物理同步
刚体被视作瞬间传送到新位置,引擎需重新计算 AABB、更新 Broadphase(四叉树/八叉树/动态 BVH),丢弃上一帧速度、动量,接触点全部失效。 - 昂贵的接触点重建
原本只需做连续碰撞检测和轻量接触更新,现在要从零开始 Broadphase → Narrowphase → 新接触点解算,物体多时会造成 CPU 峰值。 - 丢失物理连续性
瞬移导致物理不计算中间路径,高速穿透检测失效。
示例:在 FixedUpdate 中移动刚体
void FixedUpdate()
{
rigidbodyComp.MovePosition(rigidbodyComp.position + velocity * Time.fixedDeltaTime);
}
岛屿效应
什么是岛屿
在物理引擎(如 PhysX)中,刚体之间若通过碰撞接触点(Contacts)或关节(Joints)相连,会被分到同一个岛屿。
每个岛屿是独立的连通子系统,内部刚体相互影响,与其他岛屿无关。
举例:
- 地上接触在一起的箱子与地面形成一个岛屿。
- 空中独立漂浮的球是单独一个岛屿。
- 远处完全不接触的物体是另一个岛屿。
为什么有岛屿
- 性能优化
岛屿之间完全独立,解算时可分开甚至多线程并行,每个岛屿是独立的解算单元。 - 休眠管理
引擎以岛屿为单位判断是否可休眠,从而批量降低 CPU 开销。
什么是岛屿效应
若岛屿内所有刚体都满足休眠条件(速度低于阈值并保持一段时间),则整个岛屿休眠,节省性能。
但若岛屿内有一个刚体被唤醒(施加力、被碰撞、Transform 被改动),则整个岛屿都会被唤醒。
即“一荣俱荣,一损俱损”。
可能带来的性能风险
- 大型链条、布娃娃
一个关节还在晃动,整条链都会醒着,CPU 持续计算。 - 成堆的物体
玩家踢动一个箱子,整堆箱子都醒,计算量突增。
优化方向
- 避免无效关节绑定,不要把不必要的刚体用关节绑定。
- 能做成一个刚体 + 多个子碰撞体的,就不要做成 N 个独立刚体互相堆叠。
- 利用碰撞层矩阵过滤,避免无效碰撞。
- 使用关节时,将不传导力的连接点换成运动学刚体,用位移或动画驱动。
运动学刚体不被力学解算,可将岛屿切断,尤其在关节链条中。 - 在物理系统基础设置中适当提高休眠阈值、反弹阈值等,让对象不太敏感。
- 减少动态碰撞体,能用静态碰撞体的就用静态碰撞体。
- 减少关节、布娃娃系统的使用。
62.3 知识点代码
Lesson62_性能优化_CPU_物理_刚体.cs
using UnityEngine;
public class Lesson62_性能优化_CPU_物理_刚体 : MonoBehaviour
{
public Rigidbody rigidbodyComp;
void Start()
{
#region 知识回顾 碰撞体、刚体、运动学刚体、碰撞检测类型
//碰撞体:
//对于不移动的物体,但是需要和物理对象产生碰撞的物体
//只需要加碰撞体组件,因为它不需要有受力表现,只希望阻挡其他物理对象
//我们一般称只带有碰撞器的对象为 静态碰撞体
//刚体:
//要移动,并且需要有物理受力表现的物体
//需要添加碰撞器和刚体
//我们一般称带刚体和碰撞器的对象为 动态碰撞体
//运动学刚体:
//刚体上勾选了 是运动学的(Is Kinematic)选项的刚体称为运动学刚体
//它的作用类似 静态碰撞体 的效果,即与其它对象产生碰撞时,自身不会有力作用表现
//好处是我们可以利用刚体相关API让其移动
//由于运动学刚体对象不会对撞击它的对象做出物理反应
//它在运行时会简单的把其他动态碰撞器推开
//因此
//有时我们会在某些类型的游戏中将玩家对象设置为运动学刚体
//碰撞检测类型:
//Collision Detection(碰撞检测).
//Discrete(离散检测)
//原理:每个物体只在 固定物理步长的采样点 检查是否与其他碰撞体重叠
//优点:性能最好,开销最小
//缺点:高速小物体可能在两帧之间跳过另一个碰撞体,出现穿透现象
//适用场景:绝大多数常速移动的物体(角色、箱子、环境物件)
//Continuous(连续检测)
//原理:在两帧之间会额外做 运动轨迹的扫掠检测(sweep test),以减少穿透
//优点:大幅降低高速物体穿透的概率
//缺点:CPU 开销比 Discrete 高
//适用场景:非主角的小型高速物体(例如子弹、飞镖、投射物)
//Continuous Dynamic(连续动态检测)
//原理:比 Continuous(连续检测) 更严格,专门用于高速移动的刚体
// 它会检测所有 Continuous (连续检测)、Continuous Dynamic (连续动态检测)的物体
//优点:最稳妥地避免穿透
//缺点:开销最高,不适合大规模使用
//适用场景:关键的高速动态物体(例如主角手里的高速挥舞武器、主角发射的重要投射物)
//Continuous Speculative(连续推测式检测)
//原理:一种相对新的模式(比 Continuous(连续检测) 更轻量)
// 会在下一帧前 预测可能的碰撞,即使物体很小很快,也能避免大多数穿透
//优点:比 Continuous Dynamic(连续动态检测) 更省性能;避免了很多小物体穿透
//缺点:推测结果可能带来误判(没撞上也认为撞上了),偶尔出现虚假碰撞
//适用场景:中高速物体,既需要避免穿透,又不想消耗太多性能的情况
#endregion
#region 知识点一 首选离散碰撞检测
//如果你的项目是一个物理类型的项目
//对于刚体中 Collision Detection(碰撞检测)参数
//1.应该首选 Discrete(离散检测)
// 因为它的性能消耗最低
//2.少数高速投射物(比如子弹、飞镖)
// 用 Continuous(连续)
//3.关键高速动态物体(例如 Boss 的大锤、主角飞行体)
// 用 Continuous Dynamic(连续动态)
//4.兼顾性能与准确性
// 可尝试 Continuous Speculative(连续推测式)
// 但要注意误判问题
//注意:
//在游戏开发时,建议非物理类游戏 都不要使用刚体和碰撞器来进行碰撞检测
//在大部分商业游戏中,战斗系统的伤害检测,往往使用 射线检测、范围检测 或 自己实现检测逻辑
#endregion
#region 知识点二 解算器迭代次数
//我们之前在讲解物理系统基础设置时,提到了解算器迭代次数
//其实除了在基础设置中进行全局迭代次数设置
//我们还可以利用刚体中的参数设置单个对象的迭代次数
//1.solverIterations
// 设置该对象解算器迭代次数,影响位置、旋转精度和稳定性
rigidbodyComp.solverIterations = 8;
//2.solverVelocityIterations
// 设置该对象解算器速度迭代次数,影响摩擦、反弹的稳定性
rigidbodyComp.solverVelocityIterations = 2;
//对于解算器的优化思路就是
//1.全局设置到一个较低的基线(比如4/1,6/1)
//2.对个别需要稳定的刚体(主角、载具)利用刚体参数局部上调
//不要因为个别对象物理表现的不稳定而去调整全局的解算器迭代次数
//那样会增加所有对象物理计算的开销!
#endregion
#region 知识点三 刚体相关API在FixedUpdate中调用
//物理相关逻辑用 FixedUpdate 进行处理
//特别是在移动刚体对象时
//应尽量使用刚体中的
//MovePosition 和 MoveRotation
//AddForce 和 改velocity
//相关API
//千万不要在Update中直接修改刚体对象的Transform
//这会增加性能开销
//原因:
//如果在Update中通过修改刚体对象的Transform修改对象位置角度等
//相当于绕过了物理系统的运动学路径,引擎不得不做额外的强制同步和状态修复
//Unity中物理的基本运行逻辑是
//FixedUpdate → 物理世界更新 → Rigidbody 位置、旋转被写回 Transform
//当你在 Update 里直接改 Transform 时会造成以下问题
//1.破坏物理世界与 Transform 的一致性
// Rigidbody 的物理状态(位置、速度、加速度、接触点等)和你直接改的 Transform 脱节
// Unity 必须在下一次物理更新时发现这种不一致并修正
//2.强制触发物理同步
// Unity 会把刚体视作瞬间传送到新位置,这会让引擎
// 重新计算 碰撞体包围盒(AABB)、更新 Broadphase(物体空间划分结构,如四叉树/八叉树/动态 BVH)
// 丢弃上一帧的速度、动量,接触点全部失效,需要重新生成
//3.昂贵的接触点重建
// 原本只需做 连续碰撞检测 + 轻量接触 更新,现在要从零开始做 Broadphase → Narrowphase → 新的接触点解算
// 如果物体多,会造成 CPU 峰值开销
//4.丢失物理连续性
// 因为是瞬移,物理不会计算中间路径、高速穿透检测全部失效
#endregion
#region 知识点四 岛屿效应
//什么是岛屿
//在物理引擎(比如 Unity 底层的 PhysX)里
//刚体之间如果 通过碰撞接触点(Contacts)或关节(Joints)相连,就会被分到同一个岛屿
//每个岛屿是一个 独立的连通子系统,内部的刚体相互作用,但与其他岛屿无关
//举例:
//1.地上有一堆接触在一起的箱子,那么箱子们和地面就形成一个岛屿
//2.天空里独立漂浮的球,这个球就是单独的一个岛屿
//3.远处另一堆完全不接触的物体,它们是另一个岛屿
//为什么有岛屿的概念?
//1.为了性能优化
// 岛屿之间完全独立,解算时可以分开算,甚至多线程并行
// 相当于每个岛屿都是一个独立的解算单元
// 引擎会逐岛屿进行刚体运动学解算,岛屿之间互不干扰
// 它是一种解算时的分治结构
//2.为了休眠管理
// 引擎以岛屿为单位判断是否可以休眠,从而批量降低 CPU 开销
//什么是岛屿效应
//如果一个岛屿里的 所有刚体都满足休眠条件(速度低于阈值并保持一段时间),则整个岛屿会休眠,这样可以节省性能
//但是如果岛屿里 有一个刚体被唤醒(比如施加了力、被碰撞撞到、Transform 被改动),则整个岛屿都会被唤醒
//这就是所谓的 岛屿效应,相当于 一荣俱荣,一损俱损
//可能带来的性能风险
//1.大型链条、布娃娃
// 如果一个关节还在晃动,整条链都会醒着,CPU 会一直算
//2.成堆的物体
// 玩家踢动一个箱子,整堆箱子都醒了,计算量会突增
//优化方向
//1.避免无效的关节绑定,不要把不必要的刚体用关节绑定
//2.能做成一个刚体+多个子碰撞体的,就不要做成N个独立刚体互相堆叠
//3.利用碰撞层矩阵过滤,避免无效碰撞
//4.使用关节时,将不受到力传导的连接点换成运动学刚体,利用位移或者动画来驱动
// 注意:运动学刚体不被力学解算,它可以有效的将岛屿切断为两部分,特别是关节链条中
//5.在物理系统基础设置中的 适当提高休眠阈值、反弹阈值等等参数,让对象物理表现不那么敏感
//6.减少动态碰撞体,能用静态碰撞体的就用静态碰撞体
//7.减少关节、布娃娃系统的使用
//等等
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com