☰
Unity物理材质调参指南:从Bounciness到Combine模式掌握弹跳手感
2026/10/16 3:27:54 网站建设 项目流程

很多人第一次接触Unity物理材质,都是从“把Bounciness调成1,球弹起来”这种尝试开始的。我也一样。最初在某个Demo里做一个弹跳球,随手调了个数值,发现球确实能弹,但总感觉不对——要么弹起来像气球飘,要么落地就黏住,要么弹了两下就“死”了。于是开始反复调整参数,拖动滑块,换组合模式,折腾了一整天,最后才摸明白:Unity物理材质不是一个“调一个数”就能完事的开关,它是一套由多个参数互相牵制的系统。

这篇文章就围绕“物理材质到底怎么用”这个事,把从创建材质、配置参数、到真正让球“弹得不一样”的完整过程拆开讲。适合刚接触Unity物理系统、或者已经开始用Physic Material但总觉得哪里不对劲的开发者。内容不涉及高深原理,但每一步都尽量讲清楚“为什么”。

1. 物理材质的本质:一个只负责“表面属性”的组件

1.1 材质不是“力”,而是“接触规则”

Unity里的Physic Material,官方文档描述是“用于调整碰撞对象的摩擦和弹跳效果”。注意它的核心职责只有两个方向:摩擦和弹跳。换句话说,物理材质本身不产生力,不推动物体,也不改变重力。它只是告诉物理引擎“当两个物体接触时,它们的表面应该表现出怎样的行为”。

这样理解就清晰了:球从高处落下,决定它能不能弹起来的直接因素是它落地时速度、重力、以及材质上的Bounciness(弹力系数)。而决定它落地后是原地弹起还是带着旋转滑行,则要看Dynamic Friction(动摩擦)、Static Friction(静摩擦)以及Friction Combine(摩擦合并模式)。

很多新手会把“物理材质”和“渲染材质(Material)”搞混。渲染材质决定物体“看起来”是什么颜色、是否透明,物理材质决定“碰上去”是什么手感。两者可以同时使用,互不干扰。

1.2 Unity物理引擎的默认行为

Unity默认的物理引擎是PhysX(旧版Built-in Pipeline和3D物理),它处理碰撞响应时,会综合两个碰撞体的材质参数,而非只取其中一个。这里的“综合”并不是简单的相加或取平均,而是由每个材质上的Combine模式决定。如果两个物体都有物理材质,引擎会先看各自的Combine模式,再按模式计算最终结果。如果一个物体没有物理材质,则使用工程默认值。

这里的“默认物理材质”很关键。Unity允许你在Project窗口右键创建Physic Material,也允许你在Project Settings的Physics面板里设置默认材质。如果场景里大量物体都没挂物理材质,那么它们的交互结果全部由这个默认值决定——很多时候球弹不起来,不是因为你的Bounciness不够,而是地面物体压根没有物理材质,二者合并后最终弹力被拉低了。

这类问题在后续排查时会反复出现,建议从一开始就养成“每个参与碰撞的Collider都尽量挂物理材质”的习惯,哪怕只是挂一个默认的。

2. 参数逐一拆解:Bounciness、Friction和Combine模式

2.1 Bounciness(弹力系数)

Bounciness取值范围是0到1。0表示完全不弹,碰撞后速度几乎全部损失;1表示理想弹性碰撞,几乎能完全恢复速度。但需要明确两点:

第一,这里的“1”并不是绝对完美的弹性,它还受物理引擎的精度、步长、碰撞检测模式影响。第二,Bounciness只决定“速度恢复比例”,不决定“物体会不会弹”。物体落地时如果速度低于某个阈值,即使Bounciness是1,也不会弹——这个阈值就是Bounce Threshold(弹跳阈值),在Project Settings里可以设置,默认值是2。

听起来有点绕,实际手感就是:如果把小球从很低的高度放手,它不会弹起来,而是直接停在原地;从高一点的地方落下,才开始出现弹跳。这个临界点不是靠Bounciness控制的,是Bounce Threshold在起作用。

2.2 Dynamic Friction与Static Friction

这两个参数控制“滑动”和“静止”的摩擦表现。Dynamic Friction是物体滑动时受到的阻力,Static Friction是物体从静止到开始移动时需要克服的阻力。举例来说,想让一个箱子在冰面上滑得远,就把Dynamic Friction调低;想让它启动时很费劲,就把Static Friction调高。

这两个参数和弹跳没有直接关系,但会严重影响“弹起来之后”的观感。一个球如果Dynamic Friction为0,落地后会像冰球一样平滑地滑走;如果Dynamic Friction很高,则每一次接触都会消耗大量水平速度,球很快就停住,看起来像粘了胶水。

2.3 Combine模式

Combine模式有四种:Average(平均值)、Minimum(取最小值)、Maximum(取最大值)、Multiply(相乘)。它决定了两个碰撞体的摩擦系数和弹跳系数如何合并。

默认是Average。意思是,如果球的Bounciness是0.8,地面是0.2,最终弹力就是0.5。这种模式下,单方面调高一个物体的弹力,效果会被稀释。

如果希望球的弹跳行为完全由球自己的材质主导,可以把球的Bounce Combine改成Maximum,或者把地面的Bounce Combine改成Minimum。这样算出来的弹力系数不会因为地面的“拖累”而下降。这是个非常实用的技巧,在制作角色脚底摩擦、小球弹跳时都很有用。

2.4 参数干扰与“调一个数”的误区

很多教程会说“把Bounciness调到0.8球就能弹”,这句话在特定条件下成立,但省略了大量前提。我踩过的一个很典型的坑是:球确实挂了Rigidbody,也挂了物理材质,Bounciness调到1,但球落地后只是轻轻跳了一下就再也没弹起来。排查了半天,发现问题出在质量与速度上——球本身质量太大,落地速度又不够快,单次碰撞速度小于Bounce Threshold时物理引擎直接不计算弹跳。

这个误区值得单独说:物理材质只是“表面规则”,实际表现还要叠加Rigidbody的质量、速度、重力、阻力,甚至碰撞器的形状。只有把所有因素放在一起看,才能得到预期效果。

3. 实操过程:从零搭一个“有手感”的弹跳测试台

3.1 场景搭建与基本配置

我建议直接用Unity默认的3D模板,不需要额外导入资源。场景里放三样东西:一个地面(空物体加Box Collider)、一个小球(Sphere加Sphere Collider和Rigidbody)、一个空物体用来挂脚本动态生成小球。

地面的Box Collider不需要挂Rigidbody,因为静态碰撞体足够计算碰撞响应。球的Rigidbody使用默认配置,重力开启,质量和阻力先不改。这里有一个很容易忽略的点:如果地面挂了一个Rigidbody且Is Kinematic勾选了,球撞上去的物理表现会和普通静态Collider略有不同,演示结果会失真。所以测试台尽量保持最简单。

然后创建两个物理材质:

  • Bouncy_Ground:Bounciness = 1,Friction = 0
  • Bouncy_Ball:Bounciness = 0.8,Dynamic Friction = 0.2,Static Friction = 0.2

把两个材质分别拖到地面和球的Collider组件上。

3.2 第一次测试:默认Combine模式下的表现

运行场景,把球从高度5米的位置放下。预期结果:球落地后弹起约3米左右。原因是两个材质的Bounciness合并默认取平均,(1 + 0.8)/ 2 = 0.9,所以大约能恢复90%的速度。但因为还叠加了球的阻力和地面摩擦,实际恢复高度会略低于理论值。

这个阶段观察的重点不是弹得多高,而是弹跳是否自然。如果球弹起来的时候带着明显的不规则旋转,说明动摩擦和静摩擦设置不对称,地面或球在碰撞时产生了额外的角速度。要消除旋转,可以把球的Angular Drag稍微调大,比如从默认的0.05调到0.1。但Angular Drag不要调得太大,否则旋转衰减过快,球看起来像被空气刹车拖住了。

3.3 改变Combine模式观察差异

保持Bouncy_Ball的Bounce Combine改为Maximum,地面的Bounce Combine保持Average不变。再运行一次,会发现球弹得比第一次高。原因是Maximum模式下,两个材质取0.8和1中的较大值,也就是1,于是碰撞恢复接近理想弹性,球落地后会弹到接近原始高度。

这个操作验证了一件事:在同一个场景里,如果你想让球“弹得不一样”,不一定要把Bounciness改成0.9或者0.7,直接调整Combine模式就能制造巨大的手感差异。这个技巧在做游戏角色踩到不同地面(蹦床、水泥地)时特别有用——角色材质可以设置成Maximum,然后地面材质控制弹力的上限,角色的弹跳手感就不会被地面的材质拖乱。

3.4 用代码动态控制物理材质

除了在Inspector里挂材质,也可以用代码在运行时创建和修改物理材质。这个方式在制作“地面突然变成冰面”“角色吃到增强Buff手感变灵敏”这类需求时很实用。示例代码很简单:

using UnityEngine; public class DynamicMaterialChanger : MonoBehaviour { private Collider groundCollider; void Start() { groundCollider = GetComponent<Collider>(); } public void MakeGroundBouncy() { PhysicMaterial bouncyMat = new PhysicMaterial("DynamicBouncy"); bouncyMat.bounciness = 0.9f; bouncyMat.bounceCombine = PhysicMaterialCombine.Maximum; bouncyMat.dynamicFriction = 0f; bouncyMat.staticFriction = 0f; groundCollider.material = bouncyMat; } public void ResetFriction() { PhysicMaterial normalMat = new PhysicMaterial("NormalGround"); normalMat.bounciness = 0.2f; normalMat.dynamicFriction = 0.6f; normalMat.staticFriction = 0.6f; groundCollider.material = normalMat; } }

记得在修改材质后,如果有多个Collider共用了同一个PhysicMaterial实例,修改会同时影响所有使用这个材质的Collider。如果你只想改一个物体的表面,直接给它赋一个新的材质实例。这个细节很容易导致“改了一个,别的地方也变了”的诡异现象。

4. 参数组合实验:球真的“弹得不一样”了

4.1 摩擦力和弹跳的搭配设计

做了一批实验,用同一颗球、同一个场景,只改地面物理材质的参数,对比球的落地表现。整理成表格方便直接参照:

地面物理材质配置球的整体表现适合场景
Bounciness=1, Friction=0弹起很高,且落地后继续滑行,几乎没有减速蹦床、弹性平台
Bounciness=1, Friction=0.6弹起很高,但落地后被摩擦拽住,滑行距离很短橡胶球、弹性地板
Bounciness=0.2, Friction=0弹不太起来,但会滑很远冰面、光滑斜坡
Bounciness=0.8, Friction=0.4弹起中等高度,落地后滚动一段后停住普通皮球、篮球
Bounciness=0, Friction=0.9完全不弹,落地立刻静止沙袋、水泥地面

这里有一点值得注意:摩擦参数不决定“能不能弹”,但非常影响“弹起来之后的姿态”。同样是做“弹跳球”,如果只调Bounciness,不调摩擦,球会越弹越远,像一只失控的跳跳球。要做一个在固定区域内反复弹跳的球,摩擦参数必须高一些。

4.2 Bounce Threshold的实战调参

Bounce Threshold(弹跳阈值)在Project Settings的Physics面板里设置。这个数值直接影响低高度弹跳的“死区”。默认值是2。意思就是当碰撞速度小于2 m/s时,引擎不触发弹跳。

想让球从很低的高度也能弹起来,就把这个阈值调小,比如0.2。但要注意,调小之后所有物理材质都会受影响——球弹跳会变得“过度灵敏”,哪怕轻轻碰一下也能弹起来,物理表现反而失真。所以一般建议保持默认,反而是在需要用代码强制弹跳时,让它匹配实际游戏逻辑。

我在做某个跳跃平台原型时,角色跳得很低,落地后经常“粘住”不弹,排查了很久才发现是Bounce Threshold在捣乱。把阈值从2改成0.5以后问题解决,但代价是子弹击中地面时的反弹效果也变得明显——因为子弹的入射速度很高,弹不弹其实与阈值无关,真正受影响的只有低速接触。

4.3 质量与速度对弹跳的放大作用

物理材质不可能脱离Rigidbody单独工作。两颗完全一样的球,把其中一颗质量从1改成10,同样的高度落下,弹跳高度会不同。原因是质量影响了动量和碰撞速度,碰撞速度又决定了Bounce Threshold是否生效以及Bounciness恢复的能量绝对值。

所以如果你调了Bounciness但球的弹跳还是不够“有劲”,先别急着重力或者材质,试着把Rigidbody的质量调大,或者把初始下落高度提高。很多时候球弹不起来,只是输入的能量不够,不是材质的问题。反过来,如果球弹得停不下来,可以适当增加球的Drag(阻力),让它每次弹跳都损失一点能量。

5. 常见问题与排查技巧实录

5.1 球完全不弹

排查顺序这么来:先确认物理材质有没有挂上Collider,而不是挂在Transform或者Rigidbody上。然后确认两个接触物体的材质是否有Bounciness值,以及Combine模式是否把最终值压成了0。再看Rigidbody的Constraints是不是锁了Y轴位移——真的有人会为了“防止角色乱动”锁掉刚体位移,然后球自然弹不起来。最后检查Project Settings里的Bounce Threshold,如果球的落地速度低于阈值,弹跳被直接禁止。

这里要特别提醒:如果球的Rigidbody上勾选了“Is Kinematic”,碰撞发生时物理引擎不会为这个物体计算弹跳。Kinematic刚体只驱动运动,不参与动态响应。很多新手把“Is Kinematic”当成“不重力”选项来用,结果球落地就穿过去了。

5.2 球弹起来后不受控制乱飞

这个现象一般有两类原因。一类是摩擦参数不匹配,常见于动摩擦为0、静摩擦很大,接触时产生额外的旋转力矩。另一类是碰撞器形状问题——如果球的Sphere Collider很小、地面是Box Collider,物体边缘接触时容易发生卡顿或弹飞。

处理方式:先把两个材质设成相同的摩擦值,再检查Collider的尺寸是否合理。如果问题依旧,把Fixed Timestep从默认的0.02改成0.01,物理模拟精度翻倍,很多“莫名弹飞”都会消失。代价是CPU开销上升,所以这只是排查手段,不是长期方案。

5.3 球落地后滑行不止

想做一个足球在草地上滚动的效果,结果球像在光滑冰面上一样滑行。原因很简单:动摩擦和静摩擦都设成了0,或者Combine模式选了Minimum。把地面材质的动摩擦、静摩擦改回0.4左右,同时把Friction Combine改为Multiply,会发现球的滚动衰减显著改善。

记住:球在滚动时,动摩擦也在持续起作用。只有角速度低到一定程度,静摩擦才接管。如果你的游戏只需要“速滑”效果,不需要滚动细节,可以直接冻结Rigidbody的旋转——但这不是物理材质能解决的。

5.4 穿模问题与碰撞检测模式

球飞太快或者太小,可能穿过薄地面。物理材质完全无法解决穿透——穿透是碰撞检测频率不够造成的。这时需要把Rigidbody的Collision Detection改成Continuous Dynamic或者Continuous Speculative。但Continuous Dynamic消耗较高,有大量小球时慎用。

一个替代方案是:保持高速物体的Rigidbody检测模式为Discrete,但将Bounce Threshold调高(比如10),这样高速接触会被当成弹跳而更早触发。效果比改检测模式更轻量,但会有一些物理上的“提前反应”。

6. 一些长期好用的调试习惯

最后分享几个我在项目里长期使用的习惯。

第一,不要直接在场景里右键创建Physic Material后随手改参数,建议在项目里建一个“PhysicsMaterials”文件夹,每种材质命名带用途,比如“Bouncy_Floor_Ice”“Bouncy_Ball_Rubber”。因为材质是资源,可以反复复用,命名清晰能省大量排查时间。

第二,在场景中做一个固定弹跳测试台。每次调节完物理材质,用同一个高度、同一个速度的“测试球”跑一遍,确认手感变化是否符合预期。这样不会因为换了一个场景、换了一个球的尺寸,就误判材质参数的问题。

第三,用Gizmos画出碰撞速度的矢量线。在调试脚本里,可以在OnCollisionEnter时输出球的velocity.magnitude,把数值显示在屏幕上。这能帮你快速判断是材质问题还是速度阈值的问题。

第四,如果物理表现和预期差距太大,优先怀疑刚体设置,而不是材质参数。物理材质只在正确的基础运动设置之上才能产生稳定效果。Rigidbody的质量、阻力、碰撞检测模式,任何一个不对,材质参数再准都白搭。

物理材质这个系统不复杂,但参数之间相互影响,才让“调一个数”变成了“完整旅程”。把这个底层的规则理清了,后面做蹦床、冰面、沙地、摩擦滑轨之类的手感,都会顺手很多。

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

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

立即咨询