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