Unity 之 物理引擎中三种刚体力施加方式详解
-
- [1. 概述](#1. 概述)
- [2. 核心概念对比](#2. 核心概念对比)
- [3. 方式一:直接设置速度 (`velocity = v3`)](#3. 方式一:直接设置速度 (
velocity = v3)) -
- [3.1 原理](#3.1 原理)
- [3.2 关键特性](#3.2 关键特性)
- [3.3 使用场景与示例](#3.3 使用场景与示例)
- [3.4 注意事项](#3.4 注意事项)
- [4. 方式二:施加力 (`AddForce`)](#4. 方式二:施加力 (
AddForce)) -
- [4.1 原理](#4.1 原理)
- [4.2 ForceMode 详解](#4.2 ForceMode 详解)
- [4.3 使用场景与示例](#4.3 使用场景与示例)
- [5. 方式三:在指定点施加力 (`AddForceAtPosition`)](#5. 方式三:在指定点施加力 (
AddForceAtPosition)) -
- [5.1 原理](#5.1 原理)
- [5.2 关键特性](#5.2 关键特性)
- [5.3 使用场景与示例](#5.3 使用场景与示例)
- [6. 总结与最佳实践](#6. 总结与最佳实践)
1. 概述
在 Unity 物理系统中,对刚体施加力或改变其运动状态是游戏交互的基础。根据不同的物理模拟需求和效果目标,开发者主要有三种核心方式可以影响刚体的运动。理解它们的原理、差异和适用场景,对于实现预期且稳定的物理行为至关重要。
2. 核心概念对比
下表从核心原理上对比了三种方式的关键差异:
| 特性 | velocity = v3 |
AddForce(v3) |
AddForceAtPosition(v3, position) |
|---|---|---|---|
| 作用原理 | 直接赋值,覆盖速度 | 施加力,由物理引擎计算加速度 | 在特定点施加力,可能产生扭矩 |
| 物理真实性 | 低(类似"瞬移") | 高(遵循 F=ma) | 高(模拟真实力矩) |
| 质量影响 | 无视质量 | 受 质量影响(Force/Impulse模式) |
受质量和作用点影响 |
| 是否产生旋转 | 否 | 否(力的作用线通过质心) | 是(作用点偏离质心时) |
| 性能开销 | 最低(直接赋值) | 中等(引擎计算) | 最高(计算力与扭矩) |
| 主要用途 | 重置状态、精确控制 | 通用物理驱动(推进、跳跃等) | 复杂物理效应(旋转、转向等) |
3. 方式一:直接设置速度 (velocity = v3)
3.1 原理
通过直接修改 Rigidbody.velocity 属性,立即、无条件地设定刚体的线速度矢量。此操作完全绕过物理引擎的力计算过程。
3.2 关键特性
- 瞬时生效:速度在赋值帧立即改变。
- 违反物理直觉 :不遵循牛顿第二定律(
F=ma),忽略物体的质量和现有动量。 - 无旋转效应:仅改变线速度,不改变角速度。
3.3 使用场景与示例
适用于需要"结果导向"而非"过程模拟"的控制。
csharp
// 场景1:将玩家速度立即归零(如碰到障碍物)
playerRigidbody.velocity = Vector3.zero;
// 场景2:实现平台角色的恒定水平移动(不受斜坡等影响)
void FixedUpdate() {
if (isGrounded) {
// 直接赋予一个水平速度,实现稳定移动
playerRigidbody.velocity = new Vector3(moveInput * moveSpeed, playerRigidbody.velocity.y, 0);
}
}
// 场景3:太空游戏中无阻尼环境下的推进
void ApplyThrust() {
// 在太空中,简单直接的速度增量可能更符合直觉
playerRigidbody.velocity += transform.forward * thrustIncrement;
}
3.4 注意事项
滥用此方式会导致运动生硬、不自然,并可能引发诡异的碰撞行为。通常仅在 FixedUpdate 中使用,以保证与物理更新的同步。
4. 方式二:施加力 (AddForce)
4.1 原理
调用 Rigidbody.AddForce 方法,对刚体的质心施加一个力矢量。物理引擎会根据力的大小、方向、作用模式以及刚体的质量,计算出加速度并积分得到速度变化。这是模拟真实受力最常用的方法。
4.2 ForceMode 详解
力的行为通过 ForceMode 参数精确控制。
| 模式 | 计算公式(概念) | 质量影响 | 时间影响 | 典型场景 |
|---|---|---|---|---|
| ForceMode.Force (默认) | force (牛顿) |
是 (a = F/m) | 持续 (力作用于整个固定更新周期) | 持续推力(如火箭发动机)、风力、缓坡重力 |
| ForceMode.Impulse | force (牛顿秒) |
是 | 瞬时 (力在单个固定更新周期内全部施加) | 爆炸冲击、子弹击中、瞬间跳跃 |
| ForceMode.VelocityChange | Δv (米/秒) |
否 | 瞬时 | 直接改变速度,忽略质量(如角色空中突然变向、台球击打*) |
| ForceMode.Acceleration | a (米/秒²) |
否 | 持续 | 提供恒定加速度,无视质量(如特定游戏中的重力场) |
*注:对于你之前的台球问题,使用
VelocityChange是确保不同设备、不同质量的球获得相同初始速度的推荐方案。
4.3 使用场景与示例
适用于绝大多数需要物理真实感的场景。
csharp
// 场景1:车辆前进(持续力)
void FixedUpdate() {
float engineForce = inputVertical * enginePower;
carRigidbody.AddForce(transform.forward * engineForce, ForceMode.Force);
}
// 场景2:玩家跳跃(瞬间冲量)
void Jump() {
if (isGrounded) {
playerRigidbody.AddForce(Vector3.up * jumpPower, ForceMode.Impulse);
}
}
// 场景3:解决设备差异的台球击打(瞬时速度改变)
void StrikeCueBall(Vector3 direction, float power) {
cueBallRigidbody.AddForce(direction * maxSpeed * power, ForceMode.VelocityChange);
}
5. 方式三:在指定点施加力 (AddForceAtPosition)
5.1 原理
调用 Rigidbody.AddForceAtPosition(Vector3 force, Vector3 position)。此方法不仅会在刚体上施加一个力(导致线性运动),还会因为力的作用点 (position) 可能不在质心而产生一个扭矩(导致旋转运动)。
5.2 关键特性
- 力与扭矩 :效果等价于在
position点施加force,物理引擎会自动分解为作用于质心的力和一个使物体旋转的力矩。 - 位置敏感:施力点离质心越远、力的方向与质心连线方向越垂直,产生的旋转效果越强。
- 计算复杂:开销高于前两种方式。
5.3 使用场景与示例
适用于需要模拟真实世界中"推一个点"导致物体既移动又旋转的效果。
csharp
// 场景1:台球加塞(制造旋转球)
void StrikeWithSpin(Vector3 hitDirection, float power, Vector3 localSpinOffset) {
// 计算世界空间下的击打点(偏离球心)
Vector3 hitPointWorld = cueBallTransform.position + cueBallTransform.TransformDirection(localSpinOffset);
// 在偏离点施加力,球将一边前进一边旋转
cueBallRigidbody.AddForceAtPosition(hitDirection * power, hitPointWorld, ForceMode.VelocityChange);
}
// 场景2:开门(在门把手处推拉)
void InteractWithDoor(Vector3 playerPushDirection) {
// doorHandlePosition 是门把手的世界坐标
doorRigidbody.AddForceAtPosition(playerPushDirection * pushStrength, doorHandlePosition, ForceMode.Force);
}
// 场景3:物理破坏(让物体被击中后旋转着飞出去)
void OnCollisionEnter(Collision collision) {
if (collision.relativeVelocity.magnitude > breakThreshold) {
// 在碰撞点施加一个力
foreach (ContactPoint contact in collision.contacts) {
debrisRigidbody.AddForceAtPosition(collision.impulse * 0.5f, contact.point, ForceMode.Impulse);
break;
}
}
}
6. 总结与最佳实践
- 追求物理真实性与交互 :优先使用
AddForce,并根据力是持续还是瞬时选择合适的ForceMode(Force/Impulse)。 - 解决一致性与控制问题 :当需要确保不同质量的物体获得相同速度变化 时,使用
AddForce(..., ForceMode.VelocityChange)。这是解决你之前遇到的"设备间力度不一致"问题的核心方案之一。 - 实现复杂物理效果 :当需要物体受力后同时产生移动和旋转 时(如推一个箱子的一角),使用
AddForceAtPosition。 - 进行状态重置或顶级控制 :当物理真实性不重要,或者需要绝对、即时的速度控制 (如将物体速度归零、设定传送后的初始速度)时,可以直接设置
velocity。 - 性能考量 :在性能敏感的场景(如大量物理对象),优先选择更简单的方式。
velocity赋值性能最优,AddForceAtPosition开销最大。 - 更新时机 :所有对刚体的操作(除变换
Transform)都应在FixedUpdate中进行,以保证与物理引擎的同步,避免因帧率波动带来的行为不一致。
结合桌球项目 :对于主击打逻辑,强烈推荐使用 AddForce 配合 ForceMode.VelocityChange。若要实现加塞(旋转球)的高级技巧,则需使用 AddForceAtPosition 并精心计算偏离质心的击打点。同时,通过锁定游戏帧率(如Application.targetFrameRate = 60)和控制物理时间步长(Time.fixedDeltaTime),可以最大程度地消除不同设备间的表现差异。