关于 unity-physics
面向 Unity 开发者的物理系统参考技能,整理 3D 与 2D 物理、刚体、碰撞体、射线检测、关节及常见实现模式。
主要功能
- 013D 与 2D 物理概览
- 02刚体配置与力的施加
- 03碰撞体、触发器与碰撞回调
- 04射线、形状投射与重叠查询
- 05关节类型与常见用途
适用场景
- 01选择合适的刚体移动方式
- 02排查触发器事件未触发
- 03为高速物体配置碰撞检测
- 04使用非分配查询减少 GC
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nice-wolf-studio/ unity-claude-skills --skill unity-physics安装后在 Claude Code 中输入/unity-physics确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/character-controller.mdphysics2d-api.mdphysics3d-api.md详细介绍
Unity Physics
物理系统概览
Unity 针对不同项目需求提供了不同的物理引擎集成:
- 3D 物理(PhysX):适用于面向 3D 对象项目的 Nvidia PhysX 引擎集成
- 2D 物理(Box2D):经过优化的 2D 物理系统,配备专用组件
- 两种系统都会模拟重力、碰撞、力和约束
物理时序
物理系统通过 FixedUpdate 以固定时间步运行,与渲染帧率分开:
void FixedUpdate()
{
// All physics code belongs here, not in Update()
rb.AddForce(Vector3.forward * speed);
}
关键时序规则:
FixedUpdate按固定间隔运行(默认 0.02s / 50Hz)- 每帧可能调用多次
FixedUpdate,也可能一次都不调用 Time.fixedDeltaTime控制时间间隔- 使用
Update处理输入,使用FixedUpdate施加物理力
模拟模式
Physics.simulationMode 控制物理引擎何时执行步进:
- FixedUpdate(默认):每个固定时间步自动进行模拟
- Update:每帧模拟一次
- Script:通过
Physics.Simulate()手动控制
Rigidbody 配置
Rigidbody 组件会让 GameObject 受物理引擎控制。刚体不会在物理力作用下变形或改变形状。
关键属性
| 属性 | 描述 | 默认值 |
|---|---|---|
mass | 质量,单位为 kg;会影响力的相互作用 | 1 |
linearDamping | 对线性速度的阻力 | 0 |
angularDamping | 对角速度的阻力 | 0.05 |
useGravity | 此刚体是否受重力影响 | true |
isKinematic | 若为 true,则不受物理力驱动 | false |
interpolation | 平滑物理步进之间的视觉抖动 | None |
collisionDetectionMode | 碰撞检测算法 | Discrete |
移动方法
Rigidbody rb = GetComponent<Rigidbody>();
// Apply continuous force (call in FixedUpdate)
rb.AddForce(Vector3.forward * 10f);
rb.AddForce(Vector3.up * 5f, ForceMode.Impulse);
// Apply torque
rb.AddTorque(Vector3.up * 2f);
// Apply force at a world position (creates both force and torque)
rb.AddForceAtPosition(Vector3.forward * 10f, hitPoint);
// Kinematic movement (use for isKinematic=true bodies)
rb.MovePosition(rb.position + direction * speed * Time.fixedDeltaTime);
rb.MoveRotation(targetRotation);
ForceMode 选项
| 模式 | 描述 |
|---|---|
ForceMode.Force | 持续施加力,会使用质量(默认) |
ForceMode.Acceleration | 持续施加力,不考虑质量 |
ForceMode.Impulse | 瞬时施加力,会使用质量 |
ForceMode.VelocityChange | 瞬时施加力,不考虑质量 |
休眠状态
当 Rigidbody 的能量低于休眠阈值时,物理引擎会停止计算它。可使用 rb.Sleep() 和 rb.WakeUp() 手动控制。
缩放警告
Unity 假定 1 个世界单位 = 1 米。缩放不正确会导致不真实的物理行为。请保持 GameObject 的比例符合现实。
碰撞体与触发器
碰撞体是定义 GameObject 物理边界的不可见形状,无需与可视网格匹配。
碰撞体类别
| 类别 | 描述 |
|---|---|
| 静态碰撞体 | 仅有碰撞体,没有 Rigidbody。用于不可移动的几何体(墙壁、地板)。 |
| 动态 Rigidbody 碰撞体 | 碰撞体 + Rigidbody(isKinematic=false)。完全参与模拟。 |
| 运动学 Rigidbody 碰撞体 | 碰撞体 + Rigidbody(isKinematic=true)。通过脚本移动。 |
碰撞体形状
- 基本形状:BoxCollider、SphereCollider、CapsuleCollider——效率高,可自动缩放
- 复合形状:在子 GameObject 上组合多个基本形状,构成复杂形状
- MeshCollider:与精确网格几何形状匹配;开销较大,请谨慎使用
- WheelCollider:基于射线检测,内置车辆物理
- TerrainCollider:与地形高度图匹配
触发器碰撞体
触发器会检测重叠的碰撞体,但不会产生物理碰撞响应:
// Set via Inspector: Collider > Is Trigger = true
// Or via script:
GetComponent<BoxCollider>().isTrigger = true;
触发器事件的要求:
- 至少有一个 GameObject 必须带有 Rigidbody
- 碰撞体必须启用
isTrigger - 每个重叠对都需要各自的 Rigidbody,才能单独检测
物理材质
碰撞体支持 PhysicsMaterial,可调整摩擦力和弹性属性,以控制表面交互。
射线检测
射线检测会投射不可见的射线,以检测场景中的碰撞体。
Physics.Raycast
// Basic: check if anything is ahead
Vector3 fwd = transform.TransformDirection(Vector3.forward);
if (Physics.Raycast(transform.position, fwd, 10))
Debug.Log("Something ahead!");
// With hit info
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 100.0f))
Debug.Log("Distance to ground: " + hit.distance);
// With layer mask
int layerMask = 1 << LayerMask.NameToLayer("Enemies");
if (Physics.Raycast(origin, direction, out hit, maxDist, layerMask))
Debug.Log("Hit enemy: " + hit.collider.gameObject.name);
// Mouse-to-world raycast
Ray ray = Camera.main.ScreenPointToRay(Mouse.current.position.ReadValue());
if (Physics.Raycast(ray, out hit, 100))
Debug.DrawLine(ray.origin, hit.point);
RaycastHit 属性
| 属性 | 类型 | 说明 |
|---|---|---|
point | Vector3 | 世界空间中的命中位置 |
normal | Vector3 | 命中点处的表面法线 |
distance | float | 与射线起点的距离 |
collider | Collider | 被命中的碰撞体 |
transform | Transform | 被命中对象的 Transform |
其他投射方法
| 方法 | 说明 |
|---|---|
Physics.RaycastAll() | 返回射线上的所有交点 |
Physics.RaycastNonAlloc() | 填充预分配缓冲区(不分配 GC 内存) |
Physics.SphereCast() | 沿指定方向扫掠球体 |
Physics.BoxCast() | 沿指定方向扫掠盒体 |
Physics.CapsuleCast() | 沿指定方向扫掠胶囊体 |
Physics.Linecast() | 检查两点之间是否存在碰撞体 |
重叠查询(无方向,区域检查)
| 方法 | 说明 |
|---|---|
Physics.OverlapSphere() | 查找球体范围内的所有碰撞体 |
Physics.OverlapBox() | 查找盒体范围内的所有碰撞体 |
Physics.OverlapCapsule() | 查找胶囊体范围内的所有碰撞体 |
Physics.CheckSphere() | 如果有任何碰撞体与球体重叠,则返回 true |
Physics.CheckBox() | 如果有任何碰撞体与盒体重叠,则返回 true |
Physics.CheckCapsule() | 如果有任何碰撞体与胶囊体重叠,则返回 true |
QueryTriggerInteraction
控制投射/重叠查询是否检测触发器碰撞体:
QueryTriggerInteraction.UseGlobal-- 使用Physics.queriesHitTriggersQueryTriggerInteraction.Collide-- 始终检测触发器QueryTriggerInteraction.Ignore-- 从不检测触发器
碰撞检测
碰撞检测模式
| 模式 | 说明 | 使用场景 |
|---|---|---|
| Discrete | 在每个物理步结束时检查碰撞。效率高。 | 大多数对象的默认模式 |
| Continuous | 在整个物理步内检查碰撞。可防止穿透。 | 快速移动的对象撞击静态几何体 |
| Continuous Dynamic | 对静态和动态碰撞体都进行连续检测 | 快速移动的对象撞击其他快速移动的对象 |
| Continuous Speculative | 使用推测性接触进行预测性 CCD | 运动学刚体、通用 CCD |
碰撞回调
void OnCollisionEnter(Collision other) { // First frame of contact
if (other.gameObject.CompareTag("Player"))
Debug.Log("Player hit this object");
}
void OnCollisionStay(Collision other) { } // Every physics frame while touching
void OnCollisionExit(Collision other) { } // Contact ended
触发器回调
void OnTriggerEnter(Collider other) { // Entered trigger volume
if (other.CompareTag("Player"))
Debug.Log("Player entered trigger zone");
}
void OnTriggerStay(Collider other) { // Each frame inside trigger
other.attachedRigidbody?.AddForce(Vector3.up * 12f, ForceMode.Acceleration);
}
void OnTriggerExit(Collider other) { } // Left trigger volume
碰撞矩阵
| 对象对 | 碰撞事件 | 触发器事件 |
|---|---|---|
| 静态 + 动态 | 是 | -- |
| 静态 + 运动学 | 否 | -- |
| 静态 + 静态 | 否 | -- |
| 动态 + 动态 | 是 | -- |
| 动态 + 运动学 | 是 | -- |
| 运动学 + 运动学 | 否 | -- |
| 触发器 + 动态/运动学 | -- | 是 |
| 静态触发器 + 动态/运动学 | -- | 是 |
碰撞事件要求至少有一个动态(非运动学)Rigidbody。物理引擎只会对带有 Rigidbody 或 ArticulationBody 组件的 GameObject 施加力。
关节
关节用于将 Rigidbody 组件彼此连接,或连接到空间中的固定点。
| 关节类型 | 说明 | 使用场景 |
|---|---|---|
| FixedJoint | 将两个刚体锁定在一起(通过弹簧实现) | 连接对象、可断开的连接 |
| HingeJoint | 绕单一轴旋转 | 门、摆锤、链条 |
| SpringJoint | 保持距离的弹性连接 | 蹦极绳、系绳 |
| CharacterJoint | 带角度约束的球窝关节 | 布娃娃(髋部、肩部) |
| ConfigurableJoint | 完全可自定义的约束 | 各种特殊连接 |
关节属性
- Connected Body:另一个 Rigidbody(null = 固定到世界)
- Anchor / Connected Anchor:局部空间中的连接点
- Break Force / Break Torque:用于断开关节的阈值
- Enable Preprocessing:稳定关节模拟
对于要求精确关节运动的工业/机器人应用,请使用 ArticulationBody,而不是常规关节。
2D 物理差异
Unity 的 2D 物理使用独立的引擎,针对 2D 工作流进行了优化。
| 方面 | 3D | 2D |
|---|---|---|
| 引擎 | PhysX | Box2D |
| 刚体 | Rigidbody | Rigidbody2D |
| 碰撞体 | Collider | Collider2D |
| 物理类 | Physics | Physics2D |
| 向量 | Vector3 | Vector2 |
| 旋转 | 四元数(3 个轴) | float(仅 Z 轴) |
| 重力 | Vector3 | Vector2 |
| 力模式 | ForceMode | ForceMode2D |
Rigidbody2D 刚体类型
| 类型 | 说明 |
|---|---|
| Dynamic | 完全模拟,会对力和重力做出响应 |
| Kinematic | 通过脚本移动,可检测碰撞,但不受力影响 |
| Static | 不可移动,用于关卡几何体 |
2D 碰撞体类型
BoxCollider2D、CircleCollider2D、CapsuleCollider2D、PolygonCollider2D、EdgeCollider2D、CompositeCollider2D、TilemapCollider2D
2D 专属功能
- Effectors 2D:用于力交互的 Area、Buoyancy、Platform、Point、Surface effectors
- Constant Force 2D:施加持续的力 / 扭矩
- Physics Material 2D:摩擦和弹跳属性
- LowLevelPhysics2D API:独立的底层物理路径
完整的 2D API 详情请参阅参考文件 references/physics2d-api.md。
常见模式
使用 Rigidbody 移动玩家
public class PlayerMovement : MonoBehaviour
{
public float moveSpeed = 5f;
public float jumpForce = 7f;
private Rigidbody rb;
private bool jumpRequested;
void Awake()
{
rb = GetComponent<Rigidbody>();
}
void Update()
{
// Capture input in Update (legacy Input Manager; see unity-input for new Input System)
if (Input.GetButtonDown("Jump"))
jumpRequested = true;
}
void FixedUpdate()
{
// Apply physics in FixedUpdate (legacy Input; see unity-input)
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 move = new Vector3(h, 0f, v) * moveSpeed;
rb.linearVelocity = new Vector3(move.x, rb.linearVelocity.y, move.z);
if (jumpRequested)
{
rb.AddForce(Vector3.up * jumpForce, ForceMode.Impulse);
jumpRequested = false;
}
}
}
使用 SphereCast 检查是否接地
// Cache collider reference in Awake() -- avoid GetComponent in hot paths
private CapsuleCollider capsule;
void Awake() => capsule = GetComponent<CapsuleCollider>();
public bool IsGrounded()
{
float radius = 0.3f;
return Physics.SphereCast(transform.position, radius, Vector3.down,
out _, (capsule.height / 2f) - radius + 0.1f,
LayerMask.GetMask("Ground"));
}
使用触发器的悬浮垫
public class HoverPad : MonoBehaviour
{
public float hoverForce = 12f;
void OnTriggerStay(Collider other)
{
if (other.attachedRigidbody != null)
other.attachedRigidbody.AddForce(Vector3.up * hoverForce, ForceMode.Acceleration);
}
}
非分配型射线检测(零 GC)
private readonly RaycastHit[] hits = new RaycastHit[10];
void DetectEnemies()
{
int count = Physics.RaycastNonAlloc(transform.position, transform.forward,
hits, 50f, LayerMask.GetMask("Enemies"));
for (int i = 0; i < count; i++)
Debug.Log("Hit: " + hits[i].collider.name);
}
反模式
不要使用 Update 施加物理力
// BAD: Forces applied at variable frame rate cause inconsistent behavior
void Update()
{
rb.AddForce(Vector3.forward * 10f); // WRONG
}
// GOOD: Use FixedUpdate for physics
void FixedUpdate()
{
rb.AddForce(Vector3.forward * 10f); // CORRECT
}
不要使用 Transform 移动 Rigidbody
// BAD: Bypasses physics, breaks collision detection
transform.position += Vector3.forward * speed * Time.deltaTime; // WRONG
// GOOD: Use Rigidbody methods
rb.MovePosition(rb.position + Vector3.forward * speed * Time.fixedDeltaTime); // CORRECT
// Or apply forces:
rb.AddForce(Vector3.forward * speed);
基元足以满足需求时,不要使用 MeshCollider
在简单的盒状对象上使用 MeshCollider 很浪费。改用 BoxCollider,开销低得多,而且对于简单形状同样准确。仅在复杂的凹形几何体上使用 MeshCollider。
不要在物理回调中分配内存
// BAD: GC allocation every collision
void OnCollisionEnter(Collision other) {
var enemies = GameObject.FindGameObjectsWithTag("Enemy"); // WRONG
}
// GOOD: Cache in Start(), reuse in callbacks
不要在射线检测中忽略图层蒙版
Physics.Raycast(origin, direction, out hit); // BAD: hits everything
Physics.Raycast(origin, direction, out hit, 100f,
LayerMask.GetMask("Ground", "Obstacles")); // GOOD: filtered
核心 API 速查
| 类别 | 主要成员 |
|---|---|
| Physics(静态) | Raycast、RaycastAll、RaycastNonAlloc、SphereCast、BoxCast、CapsuleCast、Linecast、OverlapSphere、OverlapBox、CheckSphere、CheckBox、ClosestPoint、ComputePenetration、Simulate、SyncTransforms |
| Physics 属性 | gravity、defaultContactOffset、bounceThreshold、sleepThreshold、defaultSolverIterations、queriesHitTriggers、queriesHitBackfaces、simulationMode |
| Rigidbody | mass、linearDamping、angularDamping、useGravity、isKinematic、linearVelocity、angularVelocity、interpolation、collisionDetectionMode、AddForce()、AddTorque()、MovePosition()、MoveRotation()、Sleep()、WakeUp() |
| 回调 | OnCollisionEnter(Collision)、OnCollisionStay、OnCollisionExit、OnTriggerEnter(Collider)、OnTriggerStay、OnTriggerExit |
完整的方法签名、参数和重载请参阅 references/physics3d-api.md。
完整的 2D 物理 API 请参阅 references/physics2d-api.md。
CharacterController
对于非物理驱动的角色移动(FPS 控制器、NPC),请使用 CharacterController 而不是 Rigidbody。它无需进行物理模拟即可处理斜坡、台阶和碰撞滑动。完整的 API、模式以及 CharacterController 与 Rigidbody 的比较,请参阅 references/character-controller.md。
相关技能
- unity-foundations —— GameObject 层级、组件、变换、图层
- unity-scripting —— MonoBehaviour 生命周期(Update 与 FixedUpdate)、协程
- unity-2d —— 2D 游戏开发模式、精灵渲染