Unity物理系统由浅入深第一节:Unity 物理系统基础与应用
Unity物理系统由浅入深第二节:物理系统高级特性与优化
Unity物理系统由浅入深第三节:物理引擎底层原理剖析
Unity物理系统由浅入深第四节:物理约束求解与稳定性
Unity 引擎内置了一套强大且易用的物理系统,它基于 NVIDIA 的 PhysX 物理引擎。这个系统能让你在游戏中模拟真实的物理效果,比如重力、碰撞、摩擦、弹跳等,极大地提升游戏的沉浸感和交互性。本篇教程将带你了解 Unity 物理系统的核心组件和常用 API,让你能够快速地在项目中应用物理效果。
1. Rigidbody(刚体)组件:赋予物体物理属性
在 Unity 中,任何想要受到物理系统影响的 GameObject 都必须附加一个 Rigidbody 组件。Rigidbody 赋予了 GameObject 质量、速度、角速度等物理属性,并使其能够响应重力、力、碰撞等物理事件。
核心属性:
- Mass (质量):物体的质量,以千克(kg)为单位。质量越大,受到相同力的作用时加速度越小。
- Drag (阻力):线性阻力,模拟空气阻力或液体阻力。值越大,物体运动减速越快。
- Angular Drag (角阻力):角阻力,模拟物体旋转时的阻力。值越大,物体旋转减速越快。
- Use Gravity (使用重力):勾选后,物体将受到 Unity 全局重力设置的影响(默认向下)。
- Is Kinematic (是否运动学):
- 如果勾选,Rigidbody 将不再受物理引擎控制,你需要通过代码(
Transform.position
或Transform.rotation
)手动控制它的位置和旋转。运动学刚体通常用于门、电梯等受脚本控制的物体。 - 重要提示: 运动学刚体不会受到碰撞影响而移动或旋转,但它仍然可以影响其他非运动学刚体,并且会触发碰撞回调。
- 如果勾选,Rigidbody 将不再受物理引擎控制,你需要通过代码(
- Collision Detection (碰撞检测模式):
- Discrete (离散):默认模式,在每个物理步长(Fixed Update)只检测一次碰撞。适用于大多数情况,性能开销低,但高速移动的物体可能会“穿透”其他物体。
- Continuous (连续):适用于高速移动的物体,会进行更频繁的检测以防止穿透。性能开销比 Discrete 高。
- Continuous Dynamic (连续动态):适用于高速移动且相互之间都需要进行连续碰撞检测的 Rigidbody。性能开销最高。
- Constraints (约束):可以冻结刚体的特定轴向上的位置或旋转,例如,只允许物体在 X 轴上移动,或者不允许它旋转。
常用操作:
Rigidbody.AddForce(Vector3 force, ForceMode mode = ForceMode.Force)
:给刚体施加一个力。ForceMode.Force
: 持续力,以物体的质量计算加速度。ForceMode.Impulse
: 瞬间冲量,不考虑时间,直接改变物体的速度。常用于爆炸或打击效果。ForceMode.VelocityChange
: 瞬间速度变化,忽略质量。ForceMode.Acceleration
: 持续加速度,忽略质量。
Rigidbody.AddTorque(Vector3 torque, ForceMode mode = ForceMode.Force)
:给刚体施加一个扭矩,使其旋转。Rigidbody.velocity
: 获取或设置刚体的线性速度。Rigidbody.angularVelocity
: 获取或设置刚体的角速度。
using UnityEngine;public class BallController : MonoBehaviour
{public float moveForce = 10f;public float jumpForce = 5f;private Rigidbody rb;void Start(){rb = GetComponent<Rigidbody>();}void FixedUpdate() // 物理计算应该在 FixedUpdate 中进行{// 施加持续力,模拟移动if (Input.GetKey(KeyCode.W)){rb.AddForce(Vector3.forward * moveForce, ForceMode.Force);}if (Input.GetKey(KeyCode.S)){rb.AddForce(Vector3.back * moveForce, ForceMode.Force);}// 施加瞬间冲量,模拟跳跃if (Input.GetKeyDown(KeyCode.Space)){rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);}}
}
2. Collider(碰撞体)组件:定义物体的形状
Collider 组件定义了 GameObject 在物理世界中的形状,用于检测与其他物体的碰撞。没有 Collider 的 Rigidbody 无法与其他物体发生物理交互(除了重力)。
常见碰撞体类型:
- Box Collider (盒碰撞体):最常用的碰撞体,适用于立方体、矩形等规则形状。性能开销低。
- Sphere Collider (球碰撞体):适用于球体或近似球体的物体。性能开销低。
- Capsule Collider (胶囊碰撞体):适用于人物角色、圆柱体等。性能开销低。
- Mesh Collider (网格碰撞体):
- 使用物体的网格数据作为碰撞形状。
- 优点: 可以完美匹配复杂模型的形状。
- 缺点: 性能开销高,特别是对于高多边形模型。
- Convex (凸包) 选项:勾选后,Mesh Collider 将创建一个凸包形状作为碰撞体。凸包的性能远高于非凸包,并且可以与其他 Mesh Collider 发生碰撞。非凸包 Mesh Collider 只能与 Box、Sphere、Capsule 等原始碰撞体碰撞,不能与其他非凸包 Mesh Collider 碰撞。
- 通常不建议用于移动的 Rigidbody,除非设置为 Convex。
- Terrain Collider (地形碰撞体):专门用于 Unity 地形系统,自动与地形的形状匹配。
核心属性:
- Is Trigger (是否触发器):
- 如果勾选,这个 Collider 将不会产生物理碰撞响应(如反弹、滑动),而是仅仅检测到与其他 Collider 的重叠。
- 触发器常用于检测区域进入/离开,例如,当玩家进入一个区域时触发剧情或打开门。
- 两个触发器之间不会产生碰撞回调,除非其中一个至少带有 Rigidbody。
- 一个触发器和一个非触发器 Collider 之间的交互会触发回调。
- Material (物理材质):关联一个 Physic Material(详见下一节)。
最佳实践: 尽量使用简单的原始碰撞体(Box、Sphere、Capsule)组合来近似复杂模型的形状,而不是直接使用 Mesh Collider,以获得更好的性能。
3. Physic Material(物理材质):定义交互特性
Physic Material 是一种资源(Asset),用于定义碰撞体之间的摩擦力和弹性(弹跳)。你可以创建不同的物理材质并将其分配给 Collider 组件。
核心属性:
- Dynamic Friction (动态摩擦):当物体相对运动时产生的摩擦力。
- Static Friction (静态摩擦):当物体静止时抵抗初始运动的摩擦力。通常应该略高于 Dynamic Friction。
- Bounciness (弹跳):物体的弹性,0 表示完全不弹跳,1 表示完全弹性碰撞。
- Friction Combine (摩擦组合模式):
- Average (平均):取两个碰撞体物理材质的摩擦力平均值。
- Minimum (最小):取两个碰撞体物理材质摩擦力的最小值。
- Maximum (最大):取两个碰撞体物理材质摩擦力的最大值。
- Multiply (相乘):取两个碰撞体物理材质摩擦力的乘积。
- Bounce Combine (弹跳组合模式):同 Friction Combine,但作用于弹跳。
创建和使用:
在 Project 窗口右键 -> Create -> Physic Material,然后调整其属性,并将其拖拽到 Collider 组件的 “Material” 字段上。
4. 触发器与碰撞回调:响应物理事件
当 Rigidbody 与 Collider 发生交互时,Unity 会调用特定的回调函数。理解这些回调函数是实现游戏逻辑的关键。
碰撞回调 (Collision Callbacks):
适用于两个非触发器 Collider(至少一个带 Rigidbody)之间的物理碰撞。
OnCollisionEnter(Collision collision)
:当物体开始与其他物体碰撞时调用一次。collision
参数包含了碰撞的详细信息,如碰撞点 (collision.contacts
)、碰撞法线 (collision.contacts[0].normal
)、对方的 GameObject (collision.gameObject
) 和 Rigidbody (collision.rigidbody
) 等。
OnCollisionStay(Collision collision)
:当物体持续与其他物体碰撞时,每物理帧调用一次。OnCollisionExit(Collision collision)
:当物体停止与其他物体碰撞时调用一次。
触发器回调 (Trigger Callbacks):
适用于至少有一个 Collider 设置为 Is Trigger
的情况下,检测到物体进入/离开/停留在区域内。
OnTriggerEnter(Collider other)
:当物体开始进入触发器区域时调用一次。other
参数是进入或离开触发器区域的 Collider。
OnTriggerStay(Collider other)
:当物体持续停留在触发器区域内时,每物理帧调用一次。OnTriggerExit(Collider other)
:当物体离开触发器区域时调用一次。
重要规则:
- 只有当一个 Collider 附加了 Rigidbody,或者它是一个 Kinematic Rigidbody 上的 Collider 时,它才能接收到碰撞/触发器回调。
- 注意: 如果是静态(不带 Rigidbody)的 Collider 之间发生碰撞,虽然物理系统会处理它们之间的交互,但不会调用任何回调函数。
using UnityEngine;public class CollisionDetector : MonoBehaviour
{// 当发生碰撞时void OnCollisionEnter(Collision collision){Debug.Log("OnCollisionEnter: " + gameObject.name + " 撞到了 " + collision.gameObject.name);// 可以在这里获取碰撞点、法线等信息foreach (ContactPoint contact in collision.contacts){Debug.Log("碰撞点: " + contact.point + ", 法线: " + contact.normal);}}// 当进入触发器区域时void OnTriggerEnter(Collider other){Debug.Log("OnTriggerEnter: " + gameObject.name + " 进入了触发器区域 " + other.gameObject.name);if (other.CompareTag("Collectible")) // 假设我们有一个Tag为"Collectible"的物体{Debug.Log("拾取了物品!");Destroy(other.gameObject); // 销毁被拾取的物品}}
}
5. 射线检测(Raycast)与形状检测:非碰撞交互
除了碰撞体之间的物理交互,Unity 也提供了用于检测特定方向或形状是否存在物体的 API,这在很多游戏逻辑中非常有用,例如射击游戏中的子弹检测、角色控制器中的地面检测等。
射线检测 (Raycast):
从一个点发射一条射线,检测是否击中物体。
Physics.Raycast(Vector3 origin, Vector3 direction, out RaycastHit hitInfo, float maxDistance)
:origin
: 射线的起点。direction
: 射线的方向。hitInfo
: 一个RaycastHit
结构体,用于存储击中物体的信息(如击中点、法线、被击中的 Collider 和 GameObject 等)。maxDistance
: 射线的最大检测距离。
- 还可以指定
LayerMask
来过滤检测的层级。
using UnityEngine;public class RaycastExample : MonoBehaviour
{public LayerMask hitLayer; // 设置一个层级,只检测这个层级的物体void Update(){// 从摄像机向鼠标点击位置发射射线if (Input.GetMouseButtonDown(0)){Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);RaycastHit hit;if (Physics.Raycast(ray, out hit, 100f, hitLayer)) // 检测最远100米,只检测hitLayer层的物体{Debug.Log("射线击中了: " + hit.collider.gameObject.name + " 在位置: " + hit.point);// 可以在这里对被击中的物体进行操作hit.collider.gameObject.GetComponent<Renderer>().material.color = Color.red;}else{Debug.Log("射线未击中任何物体。");}}}
}
形状检测 (Shape Cast):
除了射线,你还可以发射一个球体、胶囊体或盒子来检测是否与场景中的 Collider 发生重叠或碰撞。这比 Raycast 更适合检测更大范围的障碍物。
Physics.SphereCast(Vector3 origin, float radius, Vector3 direction, out RaycastHit hitInfo, float maxDistance, LayerMask layerMask)
:发射一个球体。Physics.BoxCast(Vector3 center, Vector3 halfExtents, Vector3 direction, out RaycastHit hitInfo, Quaternion orientation, float maxDistance, LayerMask layerMask)
:发射一个盒子。Physics.CapsuleCast(...)
:发射一个胶囊体。
这些方法与 Raycast
类似,只是将线替换成了对应的形状,用于更精确或范围更大的检测。
总结
至此,你已经掌握了 Unity 物理系统的核心基础:
- Rigidbody 是物体参与物理模拟的必备组件。
- Collider 定义了物体的碰撞形状。
- Physic Material 决定了碰撞时的摩擦和弹跳特性。
- 碰撞回调和触发器回调 让你能够响应物理事件并执行游戏逻辑。
- 射线检测和形状检测 提供了在不发生物理交互的情况下查询物理世界的能力。
熟练运用这些基础功能,你就能在 Unity 中构建出丰富多样的物理交互和游戏玩法。在下一篇文章中我们将进入更深层次的探索,了解如何利用高级特性实现复杂交互,并关注性能优化,让你的物理模拟既强大又流畅
Unity物理系统由浅入深第一节:Unity 物理系统基础与应用
Unity物理系统由浅入深第二节:物理系统高级特性与优化
Unity物理系统由浅入深第三节:物理引擎底层原理剖析
Unity物理系统由浅入深第四节:物理约束求解与稳定性