3.4 碰撞器 (Collider) 与刚体 (Rigidbody) 三、Unity 核心模块 - 3.4 碰撞器 (Collider) 与刚体 (Rigidbody) 3.4.1 碰撞器 (Collider):定义形状,感知世界 碰撞器 (Collider) 是附加到游戏对象上的组件,用于定义该对象的物理形状,并使其能够参与碰撞检测。 简单来说,碰撞器就像是物体的“物理外壳”,它告诉 Unity 的物理引擎,这个物体占据了空间,可以与其他物体发生碰撞。 为什么需要碰撞器? 在 Unity 中,并非所有游戏对象都默认参与物理模拟。即使一个物体在视觉上看起来像是一个实体,如果它没有附加碰撞器组件,那么物理引擎就会忽略它,它将无法与其他物体发生碰撞或触发任何物理事件。
碰撞器 (Collider) 是附加到游戏对象上的组件,用于定义该对象的物理形状,并使其能够参与碰撞检测。 简单来说,碰撞器就像是物体的“物理外壳”,它告诉 Unity 的物理引擎,这个物体占据了空间,可以与其他物体发生碰撞。
为什么需要碰撞器?
在 Unity 中,并非所有游戏对象都默认参与物理模拟。即使一个物体在视觉上看起来像是一个实体,如果它没有附加碰撞器组件,那么物理引擎就会忽略它,它将无法与其他物体发生碰撞或触发任何物理事件。 因此,碰撞器是物体参与物理世界交互的先决条件。
碰撞器的主要作用:
定义碰撞形状: 碰撞器决定了物体的碰撞体积,可以是简单的几何形状,也可以是复杂的网格形状。
碰撞检测: 碰撞器负责检测物体与其他物体是否发生碰撞。
触发事件: 碰撞器可以配置为触发 "触发器 (Trigger)" 事件,用于非物理碰撞的交互,例如进入某个区域、拾取道具等。
3.4.1.1 常见的碰撞器类型
Unity 提供了多种类型的碰撞器,以适应不同形状和性能需求的游戏对象。 以下是几种最常用的碰撞器类型:
Box Collider (盒子碰撞器): 最常用的碰撞器之一,适用于立方体、长方体等方正形状的物体。可以调整其尺寸 (Size) 和中心 (Center) 属性来精确匹配物体的形状。
Sphere Collider (球形碰撞器): 适用于球体或近似球体形状的物体。可以调整其半径 (Radius) 和中心 (Center) 属性。
Capsule Collider (胶囊碰撞器): 适用于角色、圆柱体等形状的物体。由一个圆柱体和两个半球体组成,可以调整其半径 (Radius)、高度 (Height) 和方向 (Direction) 以及中心 (Center) 属性。
Mesh Collider (网格碰撞器): 适用于任意复杂形状的物体,使用物体的网格 (Mesh) 作为碰撞形状。网格碰撞器可以非常精确地匹配物体的形状,但性能消耗相对较高,特别是对于复杂的网格。
Terrain Collider (地形碰撞器): 专门用于地形 (Terrain) 对象,基于地形数据自动生成碰撞形状。
Wheel Collider (车轮碰撞器): 专门用于模拟车辆的车轮,除了碰撞检测外,还包含车辆控制和物理特性,例如悬挂、摩擦力等,属于更高级的物理组件,超出本章节核心讨论范围,但需要了解其也基于碰撞器的概念。
3.4.1.2 碰撞器的属性
不同的碰撞器类型有其特定的属性,但一些核心属性是通用的:
Is Trigger (是否为触发器): 这是一个重要的布尔属性。
勾选 (true): 碰撞器变为触发器。触发器不会产生物理碰撞响应,而是用于检测物体是否进入或离开触发区域,并触发相应的事件 (例如 OnTriggerEnter, OnTriggerStay, OnTriggerExit)。触发器通常用于非物理交互,例如检测玩家是否进入某个区域、拾取道具、触发剧情等。
不勾选 (false): 碰撞器为标准碰撞器。标准碰撞器会参与物理碰撞,与其他标准碰撞器和刚体发生物理交互,并触发碰撞事件 (例如 OnCollisionEnter, OnCollisionStay, OnCollisionExit)。
Material (材质): 指定碰撞器的物理材质 (Physic Material)。物理材质定义了物体表面的摩擦力 (Friction) 和弹性 (Bounciness) 等物理属性,影响碰撞时的物理响应。Unity 默认提供了一个 Default-Physic-Material,你也可以创建自定义的物理材质来调整物体的物理特性。
Center (中心): 调整碰撞器相对于物体局部坐标系的中心位置。可以微调碰撞器位置,使其更精确地匹配物体的视觉形状或实现特定的碰撞效果。
Size / Radius / Height 等尺寸属性: 根据碰撞器类型,会提供相应的尺寸属性来调整碰撞器的形状大小。例如,Box Collider 有 Size 属性,Sphere Collider 有 Radius 属性,Capsule Collider 有 Radius 和 Height 属性。
3.4.1.3 代码实践:添加和配置碰撞器
在 Unity 中,可以通过以下几种方式添加和配置碰撞器:
1. 在 Inspector 面板中添加:
选中需要添加碰撞器的游戏对象。
在 Inspector 面板中,点击 "Add Component" 按钮。
在弹出的菜单中,选择 "Physics",然后选择需要的碰撞器类型 (例如 "Box Collider", "Sphere Collider" 等)。
在 Inspector 面板中调整碰撞器的属性,例如 Size, Radius, Is Trigger, Material 等。
2. 通过脚本代码添加和配置:
可以使用 C# 脚本动态地添加和配置碰撞器。 以下代码示例演示了如何为一个游戏对象添加 Box Collider 并设置为触发器:
using UnityEngine; public class AddColliderExample : MonoBehaviour { void Start() { // 获取或添加 Box Collider 组件 BoxCollider boxCollider = gameObject.AddComponent<BoxCollider>(); // 设置为触发器 boxCollider.isTrigger = true; // 调整碰撞器尺寸 (可选) boxCollider.size = new Vector3(2f, 2f, 2f); // 调整碰撞器中心 (可选) boxCollider.center = new Vector3(0f, 1f, 0f); } }
代码详解:
gameObject.AddComponent<BoxCollider>(): 使用 AddComponent<T>() 方法向当前游戏对象添加 Box Collider 组件。如果对象已经存在 Box Collider 组件,则会添加一个新的组件,通常我们希望先检查是否已存在,或者使用 GetComponent<BoxCollider>() 获取已有的组件。
boxCollider.isTrigger = true;: 将 Box Collider 设置为触发器。
boxCollider.size = new Vector3(2f, 2f, 2f);: 设置 Box Collider 的尺寸为 (2, 2, 2)。
boxCollider.center = new Vector3(0f, 1f, 0f);: 设置 Box Collider 的中心相对于物体局部坐标系偏移 (0, 1, 0)。
3.4.2 刚体 (Rigidbody):赋予质量,响应物理
刚体 (Rigidbody) 是 Unity 物理引擎的核心组件,它赋予游戏对象物理属性,使其能够受到力的作用,参与物理模拟,例如重力、碰撞、力、扭矩等。 只有附加了刚体的游戏对象才能真正参与物理模拟和物理碰撞响应。
为什么需要刚体?
仅仅有碰撞器只能进行碰撞检测,物体之间不会发生物理交互,例如互相推动、反弹、掉落等。 刚体是实现物理交互的关键。 它告诉 Unity 的物理引擎,这个物体具有质量,可以受到力的影响,并与其他刚体和碰撞器发生物理碰撞响应。
刚体的主要作用:
物理模拟: 使物体能够受到重力、力、扭矩等物理力的作用,模拟真实的物理运动。
物理碰撞响应: 与其他刚体和标准碰撞器发生碰撞时,产生物理响应,例如反弹、推动、形变 (如果使用了形变物理) 等。
控制物体运动: 可以通过代码施加力、扭矩、速度等方式来控制刚体的运动。
3.4.2.1 刚体的属性
刚体组件提供了丰富的属性来控制物体的物理行为:
Mass (质量): 物体的质量,单位为千克 (kg)。质量越大,物体越难加速,惯性越大。质量会影响物体在物理交互中的行为,例如碰撞时的反弹力度、受力时的加速度等。
Drag (阻力): 线性阻力,模拟物体在空气或液体中运动时受到的阻力。阻力越大,物体速度衰减越快。
Angular Drag (角阻力): 角阻力,模拟物体旋转时受到的阻力。角阻力越大,物体旋转速度衰减越快。
Use Gravity (使用重力): 布尔属性,控制是否受重力影响。勾选后,物体会受到 Unity 全局重力设置的影响 (默认为 -9.81 m/s²)。
Is Kinematic (是否为运动学刚体): 布尔属性,控制刚体是否受物理引擎的控制。
勾选 (true): 刚体变为运动学刚体。运动学刚体不受物理引擎的力、碰撞等影响,其运动完全由脚本或动画控制。运动学刚体仍然可以进行碰撞检测,并与其他刚体和碰撞器发生碰撞事件,但不会产生物理响应。 常用于角色控制器、动画驱动的物体等。
不勾选 (false): 刚体为动力学刚体。动力学刚体完全受物理引擎的控制,会受到力、碰撞、重力等影响。
Interpolate (插值): 用于平滑刚体运动的插值模式。
None: 不进行插值,可能会出现抖动现象。
Interpolate: 根据上一帧的变换来平滑当前帧的运动。适用于大部分情况,可以减少抖动。
Extrapolate: 根据下一帧的变换来平滑当前帧的运动。适用于高速运动的物体,可以进一步减少抖动,但可能会引入轻微的延迟。
Collision Detection (碰撞检测模式): 控制刚体碰撞检测的精度和性能。
Discrete (离散): 默认模式,性能较高,但可能在高速运动时漏检碰撞。
Continuous (连续): 精度较高,可以检测高速运动时的碰撞,但性能消耗较高。
Continuous Dynamic (连续动态): 精度最高,可以检测高速运动物体与动态物体之间的碰撞,性能消耗最高。 通常用于高速运动的物体,例如子弹。
Continuous Speculative (连续推测): 尝试预测碰撞并进行处理,性能和精度介于 Continuous 和 Continuous Dynamic 之间。
Constraints (约束): 可以约束刚体在特定轴向上的运动和旋转。例如,可以冻结刚体在 Y 轴上的位置,使其只能在 XZ 平面上运动;或者冻结其在 X 轴上的旋转,使其不能绕 X 轴旋转。 约束可以用于限制物体的运动范围,创建特定的运动效果。
3.4.2.2 代码实践:添加和控制刚体
1. 在 Inspector 面板中添加:
选中需要添加刚体的游戏对象。
在 Inspector 面板中,点击 "Add Component" 按钮。
在弹出的菜单中,选择 "Physics",然后选择 "Rigidbody"。
在 Inspector 面板中调整刚体的属性,例如 Mass, Drag, Use Gravity, Is Kinematic 等。
2. 通过脚本代码添加和控制:
可以使用 C# 脚本动态地添加刚体,并控制刚体的运动。 以下代码示例演示了如何为一个游戏对象添加 Rigidbody 并施加一个向上的力:
using UnityEngine; public class RigidbodyExample : MonoBehaviour { private Rigidbody rb; void Start() { // 获取或添加 Rigidbody 组件 rb = GetComponent<Rigidbody>(); if (rb == null) { rb = gameObject.AddComponent<Rigidbody>(); } // 确保不使用运动学刚体 rb.isKinematic = false; } void FixedUpdate() // 物理相关的操作通常在 FixedUpdate 中进行 { // 施加一个向上的力 rb.AddForce(Vector3.up * 10f); // 向上施加 10 牛顿的力 } }
代码详解:
GetComponent<Rigidbody>(): 尝试获取游戏对象上已有的 Rigidbody 组件。
gameObject.AddComponent<Rigidbody>(): 如果游戏对象上没有 Rigidbody 组件,则添加一个新的 Rigidbody 组件。
rb.isKinematic = false;: 确保刚体不是运动学刚体,使其受物理引擎控制。
FixedUpdate(): FixedUpdate 函数用于物理相关的更新,它的执行频率与物理引擎的更新频率一致,更适合进行物理操作,例如施加力、扭矩等。
rb.AddForce(Vector3.up * 10f);: 使用 AddForce() 方法向刚体施加一个力。 Vector3.up * 10f 表示一个方向为向上 (Y 轴正方向),大小为 10 牛顿的力。
3.4.3 碰撞器与刚体的相互作用:物理世界的基石
碰撞器 (Collider) 和 刚体 (Rigidbody) 必须协同工作才能构建完整的物理交互场景。 它们之间的关系可以总结如下:
碰撞检测需要碰撞器: 无论是物理碰撞还是触发器事件,都需要至少一个物体附加了碰撞器组件。
物理碰撞响应需要刚体: 要使物体之间发生物理碰撞响应 (例如反弹、推动),至少有一个物体必须附加刚体组件。 如果两个物体都只有碰撞器而没有刚体,它们只能进行碰撞检测,触发碰撞事件,但不会发生物理交互。
触发器事件不需要刚体: 触发器事件 (使用 Is Trigger 属性) 只需要碰撞器即可触发,不需要刚体。 但为了方便脚本控制和管理,触发器物体通常也会附加刚体,并设置为 Is Kinematic = true,使其不受物理影响,只用于触发事件。
更详细的交互规则:
| 物体 A 组件 | 物体 B 组件 | 碰撞类型 | 物理响应 | 触发事件 |
|---|---|---|---|---|
| Collider | Collider | 静态碰撞 | 无 | 否 (除非都是触发器) |
| Collider | Rigidbody + Collider | 物理碰撞 | 有 | 是 (标准碰撞器事件) |
| Rigidbody + Collider | Collider | 物理碰撞 | 有 | 是 (标准碰撞器事件) |
| Rigidbody + Collider | Rigidbody + Collider | 物理碰撞 | 有 | 是 (标准碰撞器事件) |
| Trigger Collider | Collider | 触发器碰撞 | 无 | 是 (触发器事件) |
| Trigger Collider | Rigidbody + Collider | 触发器碰撞 | 无 | 是 (触发器事件) |
| Collider | Trigger Collider | 触发器碰撞 | 无 | 是 (触发器事件) |
| Rigidbody + Collider | Trigger Collider | 触发器碰撞 | 无 | 是 (触发器事件) |
mermaid graph TD 图示:
图示解释:
静态碰撞: 两个物体都只有 Collider,会发生静态碰撞,但不会有物理响应,除非其中一个是触发器。
触发器事件: 只要有一个物体是触发器 (Is Trigger = true),就会触发触发器事件 (OnTriggerEnter, OnTriggerStay, OnTriggerExit),不会发生物理响应。
物理碰撞: 至少有一个物体附加了 Rigidbody,且都不是触发器,则会发生物理碰撞,产生物理响应,并触发标准碰撞器事件 (OnCollisionEnter, OnCollisionStay, OnCollisionExit)。
3.4.4 碰撞事件和触发器事件:响应交互
Unity 提供了一系列事件函数,用于在碰撞发生或触发器被触发时执行代码逻辑。 这些事件函数需要在附加了 Collider 或 Rigidbody 的脚本中定义。
3.4.4.1 碰撞事件 (标准碰撞器)
OnCollisionEnter(Collision collision): 当碰撞开始时调用一次。 Collision 对象包含了碰撞相关的信息,例如碰撞点、法线、碰撞强度等。
OnCollisionStay(Collision collision): 当碰撞持续期间,每一帧都会调用。
OnCollisionExit(Collision collision): 当碰撞结束时调用一次。
代码示例:碰撞事件处理
using UnityEngine; public class CollisionEventHandler : MonoBehaviour { void OnCollisionEnter(Collision collision) { Debug.Log("碰撞开始!与物体: " + collision.gameObject.name); // 在这里可以添加碰撞发生时的逻辑,例如播放音效、生成特效、扣血等 } void OnCollisionStay(Collision collision) { // Debug.Log("碰撞持续中... 与物体: " + collision.gameObject.name); // 在这里可以添加碰撞持续期间的逻辑,例如持续伤害、摩擦效果等 } void OnCollisionExit(Collision collision) { Debug.Log("碰撞结束!与物体: " + collision.gameObject.name); // 在这里可以添加碰撞结束时的逻辑,例如停止音效、清除特效等 } }
3.4.4.2 触发器事件 (触发器 Collider)
OnTriggerEnter(Collider other): 当触发器被另一个 Collider 进入时调用一次。 Collider other 对象是被触发的 Collider 组件。
OnTriggerStay(Collider other): 当 Collider 持续停留在触发器内时,每一帧都会调用。
OnTriggerExit(Collider other): 当 Collider 离开触发器时调用一次。
代码示例:触发器事件处理
using UnityEngine; public class TriggerEventHandler : MonoBehaviour { void OnTriggerEnter(Collider other) { Debug.Log("触发器进入!进入物体: " + other.gameObject.name); // 在这里可以添加触发器进入时的逻辑,例如显示提示信息、打开门、拾取道具等 } void OnTriggerStay(Collider other) { // Debug.Log("触发器持续停留... 进入物体: " + other.gameObject.name); // 在这里可以添加触发器持续停留的逻辑,例如持续回复生命值、区域效果等 } void OnTriggerExit(Collider other) { Debug.Log("触发器离开!离开物体: " + other.gameObject.name); // 在这里可以添加触发器离开时的逻辑,例如隐藏提示信息、关闭门等 } }
3.4.5 最佳实践和性能优化
选择合适的碰撞器类型: 根据物体的形状选择最合适的碰撞器类型。例如,对于立方体使用 Box Collider,对于球体使用 Sphere Collider,避免使用 Mesh Collider 处理简单的形状,Mesh Collider 性能消耗相对较高。
减少网格碰撞器的使用: Mesh Collider 的性能消耗较高,特别是对于复杂的网格。尽量使用简单的碰撞器组合来近似复杂形状,或者使用凸网格 (Convex Mesh Collider) 来优化 Mesh Collider 的性能。
优化碰撞检测模式: 对于不需要高精度碰撞检测的物体,可以使用 Discrete 碰撞检测模式,以提高性能。对于高速运动的物体,才考虑使用 Continuous 或 Continuous Dynamic 模式。
合理设置物理材质: 物理材质会影响物理计算的复杂度。尽量使用默认的物理材质,或者创建少量的自定义物理材质,避免过度使用复杂的物理材质。
启用碰撞层 (Collision Layers) 和碰撞矩阵 (Collision Matrix): 使用碰撞层和碰撞矩阵可以精确控制哪些物体之间可以发生碰撞,减少不必要的碰撞检测,提高性能。
注意运动学刚体的使用: 运动学刚体虽然不受物理引擎控制,但仍然会参与碰撞检测,也会消耗一定的性能。 如果物体不需要进行碰撞检测,可以考虑移除碰撞器或刚体组件。
物理操作在 FixedUpdate 中进行: 物理相关的操作 (例如施加力、扭矩、速度等) 应该在 FixedUpdate 函数中进行,以确保物理模拟的稳定性和准确性。
总结
碰撞器 (Collider) 和 刚体 (Rigidbody) 是 Unity 物理系统的核心组件,理解它们的原理、属性和相互作用对于构建具有真实感和互动性的游戏至关重要。 本章节详细介绍了碰撞器和刚体的各种类型、属性、使用方法以及代码实践,并结合图文详解,帮助你深入理解 Unity 物理系统的基础,为更高级的物理效果和游戏机制打下坚实的基础。 掌握碰撞器和刚体的精髓,你将能够创建出更加生动、有趣和富有沉浸感的游戏世界。