Physics Material
Physics Material 资源控制 Unity 表面物理表现——两个 Collider 接触时的摩擦与弹性,从冰面滑动到橡皮球弹跳。通过调整 Dynamic/Static Friction 与 Bounciness,关卡与玩法设计师能快速塑造手感,而不必为每种表面单独编写自定义碰撞响应脚本。
想象一下...
Physics Material 就是操场地面。粗糙水泥 → 球很快停下(高摩擦)。抛光石面 → 滑得很远(低摩擦)。橡胶 → 弹得高(高弹性)。沙子 → 闷响落地(低弹性)。在 Unity 里你把 Physics Material 做成独立资源,再赋给任意 Collider,改变它与世界的交互。
概念详解
Physics Material(3D 为 .physicMaterial,2D 为 .physicsMaterial2D
)是类似 ScriptableObject 的资源,有四个主要字段:
Dynamic Friction(滑动时,0–1)、
Static Friction(从静止开始滑动,通常
高于 Dynamic)、Bounciness(0 = 不弹,1 =
全弹),以及两种合成模式——
Friction Combine 与
Bounce Combine(Average/Min/Max/Multiply)。
当两个带不同 Physics Material 的 Collider 接触时,Unity 会合成它们的值。用 Average:friction = (frictionA + frictionB) / 2。用 Minimum:friction = min(frictionA, frictionB)—— 适合冰面(一侧为 0 时总是赢)。合成优先级:Average < Minimum < Maximum < Multiply。
重要:Unity 中 Bounciness = 1 并不等于
无损弹跳。弹跳之间仍有阻尼与重力。要接近完美弹跳,
用 Bounciness = 1、Bounce Combine = Maximum,并把 Rigidbody drag 设为 0。
运行时可经 collider.material 或
collider.sharedMaterial 切换 Physics Material。若改动应影响共享同一资源的所有实例,
用 sharedMaterial(更省内存)。
现实材质预设
🧱 砖块
Dynamic Friction: 0.6
Static Friction: 0.6
Bounciness: 0.0
🧊 冰
Dynamic Friction: 0.02
Static Friction: 0.02
Bounciness: 0.0
🏀 橡胶
Dynamic Friction: 0.8
Static Friction: 0.8
Bounciness: 0.8
🪵 木头
Dynamic Friction: 0.48
Static Friction: 0.48
Bounciness: 0.0
| Combine Mode | 公式 | 适用场景 |
|---|---|---|
| Average | (A + B) / 2 | 默认,取平均 |
| Minimum | min(A, B) | 冰面(一侧为 0 则取胜) |
| Maximum | max(A, B) | 橡胶(取最大弹性) |
| Multiply | A × B | 黏性表面,相乘 |
动手步骤
创建 Physics Material
Project 面板 → 右键 → Create → Physics Material(3D)或 Physics Material 2D。按表面命名(Rubber、Ice…)。
设置 Friction 与 Bounciness
Dynamic Friction(0–1)、Static Friction(0–1)、Bounciness(0–1)。可查预设表或自行试调。
选择 Combine Mode
Friction/Bounce Combine:默认 Average。冰面用 Minimum,弹跳橡胶用 Maximum。
赋给 Collider
选中 GameObject → Collider → "Material" 槽 → 从 Project 拖入 Physics Material。
运行并微调
进入 Play Mode,观察物体运动并调数值。播放中也可编辑 Physics Material。
交互模拟器
调节 Friction 与 Bounciness,然后放下球体观察效果。
设置参数后按下“放下球体”
代码示例
基础角色站在不同表面时,在运行时切换 Physics Material。
using UnityEngine;
public class SurfaceMaterialSwapper : MonoBehaviour
{
public PhysicMaterial normalMat, iceMat, rubberMat;
Collider col;
void Start() => col = GetComponent<Collider>();
void OnCollisionStay(Collision collision)
{
// Read the ground tag
string tag = collision.gameObject.tag;
col.material = tag switch
{
"Ice" => iceMat,
"Rubber" => rubberMat,
_ => normalMat,
};
}
void OnCollisionExit(Collision _)
{
col.material = normalMat; // Restore when leaving the ground
}
}代码示例
进阶在运行时创建 Physics Material 并从游戏状态调整——无需资源文件。
using UnityEngine;
// Create and own a PhysicMaterial at runtime — no Project asset
public class DynamicPhysicsMaterial : MonoBehaviour
{
Collider col;
PhysicMaterial dynMat;
[Range(0f, 1f)] public float friction = 0.4f;
[Range(0f, 1f)] public float bounciness = 0f;
void Awake()
{
col = GetComponent<Collider>();
// Runtime PhysicMaterial, no Project asset needed
dynMat = new PhysicMaterial("Dynamic")
{
dynamicFriction = friction,
staticFriction = friction * 1.2f,
bounciness = bounciness,
frictionCombine = PhysicMaterialCombine.Average,
bounceCombine = PhysicMaterialCombine.Maximum
};
col.material = dynMat;
}
// Call from elsewhere to change live (e.g. character frozen)
public void SetIce()
{
dynMat.dynamicFriction = 0.01f;
dynMat.staticFriction = 0.01f;
col.material = dynMat; // Re-assign so Unity picks up the new values
}
}📌 快速记忆
- ▸0 = 无摩擦 / 不弹,1 = 最大
- ▸Static Friction > Dynamic Friction(符合真实物理)
- ▸Minimum Combine:一侧为 0 → 结果为 0(理想冰面)
- ▸col.material 会做私有副本;sharedMaterial 是共享的
- ▸运行时改完 PhysicMaterial 后要重新赋值 col.material
⚠️ 常见错误
❌ 只设 collider.material.bounciness = X 却不重新赋值
Unity 检测不到内部改动——数值仍是旧的
✅ 改完后赋值:col.material = updatedMat;
❌ Bounciness = 1 但球仍在损耗能量
Rigidbody.drag > 0 与重力在吃能量
✅ 设 drag = 0,且 Bounce Combine = Maximum