Unity Term Book
物理

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 CombineBounce 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.materialcollider.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默认,取平均
Minimummin(A, B)冰面(一侧为 0 则取胜)
Maximummax(A, B)橡胶(取最大弹性)
MultiplyA × B黏性表面,相乘

动手步骤

1

创建 Physics Material

Project 面板 → 右键 → Create → Physics Material(3D)或 Physics Material 2D。按表面命名(Rubber、Ice…)。

2

设置 Friction 与 Bounciness

Dynamic Friction(0–1)、Static Friction(0–1)、Bounciness(0–1)。可查预设表或自行试调。

3

选择 Combine Mode

Friction/Bounce Combine:默认 Average。冰面用 Minimum,弹跳橡胶用 Maximum。

4

赋给 Collider

选中 GameObject → Collider → "Material" 槽 → 从 Project 拖入 Physics Material。

5

运行并微调

进入 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