力与粒子运动
粒子运动的本质是 牛顿第二定律(F = ma) 的精确应用。本章节详细解析力的合成、累积机制,以及其在2D物理模拟中的代码实现。
1. 力与加速度的关系
物理原理
根据牛顿定律: \(\text{加速度} \ (a) = \frac{\text{合力} \ (F_{\text{net}})}{\text{质量} \ (m)}\) 每条帧中,需计算作用在粒子上的所有力的 矢量和,再通过该公式转换为加速度,从而更新粒子状态。
代码建模
- 粒子类(Particle)基础属性:
class Particle { public: Vector2 position; // 位置 Vector2 velocity; // 速度 Vector2 force; // 作用于粒子的净力 float mass; // 质量(需 > 0) Particle(float m) : mass(m) { force = Vector2(0, 0); // 初始无外力 } void ApplyForce(Vector2 f) { force += f; // 累加力 } void Update(float dt) { // Step 1: 计算加速度 Vector2 acceleration = force / mass; // Step 2: 更新速度(半隐式欧拉) velocity += acceleration * dt; // Step 3: 更新位置 position += velocity * dt; // Step 4: 清除力(下一帧重新累加) force = Vector2(0, 0); } };
2. 不同类型的力
力可分为 全局力(Global Forces) 和 粒子间力(Inter-Particle Forces):
(1) 全局力
作用于所有粒子的外力(无需特定来源):
- 重力(Gravity)
Vector2 gravity(0, 9.8f); // Y轴向下加速度 void ApplyGlobalForces(Particle& p) { p.ApplyForce(gravity * p.mass); // F = m * g }
- 风力(Wind)
Vector2 wind(5.0f, 0); // X轴向右风力 void ApplyWind(Particle& p) { p.ApplyForce(wind); }
(2) 粒子间力
因其他粒子的存在而产生(需遍历粒子系统):
- 弹簧力(Hooke’s Law)
void ApplySpringForce(Particle& p1, Particle& p2, float k, float rest_length) { Vector2 delta = p2.position - p1.position; float current_length = delta.Length(); float stretch = current_length - rest_length; // Hooke定律: F = -k * stretch * 方向 Vector2 force = delta.Normalized() * (-k * stretch); p1.ApplyForce(force); p2.ApplyForce(-force); // 反作用力 }
- 万有引力(Gravitational Attraction)
void ApplyGravityBetween(Particle& p1, Particle& p2, float G) { Vector2 delta = p2.position - p1.position; float distance_sq = delta.LengthSquared(); if (distance_sq < 0.001f) return; // 避免除以零 // 引力公式: F = G * (m1*m2) / r² * 方向 float strength = G * (p1.mass * p2.mass) / distance_sq; Vector2 force = delta.Normalized() * strength; p1.ApplyForce(force); p2.ApplyForce(-force); }
(3) 阻尼力(Damping)
模拟能量损耗(如空气阻力): \(F_{\text{damp}} = -k_d \cdot v\)
void ApplyDamping(Particle& p, float k_damping) {
Vector2 damping_force = p.velocity * (-k_damping);
p.ApplyForce(damping_force);
}
3. 实践关键点
(1) 力的生命周期
- 每一帧开始时清空净力:通过
force = Vector2(0,0)
避免未释放的残留力。 - 遍历作用顺序无关结果:力的合成是矢量加法交换律的,但实现时应注意遍历逻辑。
(2) 性能优化
- 全局力的批量应用:
for (auto& particle : particles) { particle.ApplyForce(gravity * particle.mass); }
- 粒子间力的空间分割算法(如四叉树):减少不必要的遍历(例如仅邻近粒子计算引力)。
(3) 质量的保护
质量的 mass
应始终 >0。可添加保护:
void Particle::SetMass(float m) {
mass = (m > 0.001f) ? m : 0.001f; // 防止除以零
}
4. 多力叠加示例
模拟同时受重力、风力和阻尼的粒子:
void SimulationLoop(float dt) {
// 应用全局力
for (auto& p : particles) {
ApplyGlobalForces(p); // 重力
ApplyWind(p); // 风力
ApplyDamping(p, 0.1f); // 阻尼
}
// 计算粒子间力
for (int i=0; i<particles.size(); i++) {
for (int j=i+1; j<particles.size(); j++) {
ApplyGravityBetween(particles[i], particles[j], 0.05f);
}
}
// 更新所有粒子状态
for (auto& p : particles) {
p.Update(dt);
}
}
5. 粒子运动方程的代码流程
以下为力驱动粒子运动的完整步骤:
- 清空当前净力(准备重新累积)
- 叠加所有作用力(全局力、粒子间力、阻尼等)
- 计算加速度(a = F_net / m)
- 用积分方法更新速度(如半隐式欧拉)
- 更新位置(同一步骤)
6. 扩展:约束力(Constraint Forces)
除外力外,还需处理 约束条件(例如粒子不可穿过地面):
void HandleGroundCollision(Particle& p, float ground_y) {
if (p.position.y > ground_y) {
p.position.y = ground_y;
p.velocity.y *= -0.5f; // 反弹阻尼
}
}
总结:力的游戏物理控制法则
- 力的应用层级:全局力→粒子间力→阻尼→约束力。
- 核心代码:
ApplyForce()
与Update()
方法分离,保留清晰的物理逻辑。 - 相对论式精度取舍:低复杂度场景用简单力模型,高精度需求(如布料)加入弹簧-阻尼系统。