2D物理效果代码实现思路(9)各种力

By pocaster

详解物理模拟中的核心力模型与代码实现

在物理模拟中,力的本质是通过 牛顿动力学方程组 驱动物体运动状态改变。以下分类解析游戏开发和物理引擎中常用的七大力学模型(含原理、公式、代码),并结合典型案例说明其应用场景。


I. 驱动力(Driving Force)

1. 基本原理

驱动力是人为定义的 “主动推力”,常用于模拟 发动机、推进器 等运动源。其方向由设计者控制,形式灵活(恒定、脉冲、随时间变化等)。

2. 经典公式

\(F_{\text{drive}} = \text{方向向量} \times \text{推力强度}\) (若物体有反冲效应,需通过质量分配合适加速度)

3. 代码实现

案例:操控一艘2D飞船的主引擎

// 船舶类扩展方法
void SpaceShip::ApplyThrust(Vector2 direction, float strength) {
    Vector2 thrust = direction.Normalized() * strength;
    body->ApplyForce(thrust); // body为飞船的刚体组件
}

4. 应用场景

  • 星际飞船的加速飞行
  • 赛车的油门控制系统
  • 角色跳跃的腿部推动力

II. 线性阻力(Linear Damping)

1. 基本原理

线性阻力的强度与速度 呈正比,常用于模拟 低速 环境下的粘滞介质(如水中移动)的能量损耗。是简化后的阻尼模型。

2. 阻力公式

\(F_{\text{damp}} = -k_{\text{damp}} \cdot v\) 其中\(k_{\text{damp}}\)为阻尼系数,由介质特性决定。

3. 代码实现

void ApplyLinearDamping(RigidBody& body, float k_damp) {
    Vector2 damping_force = body.velocity * (-k_damp);
    body.ApplyForce(damping_force);
}

4. 参数权衡

  • 低 \(k_{\text{damp}}\)(0.01~0.1):空气等稀薄介质(粒子特效)
  • 高 \(k_{\text{damp}}\)(0.5~2):水下或高粘性液体

III. 流体阻力(Fluid Drag)

1. 基本原理

适用于 高速或高雷诺数 流体,阻力与速度 平方 成正比(参考牛顿阻力定律)。常用于模拟空气阻力、快速落体运动等。

2. 阻力公式

\(F_{\text{drag}} = -\frac{1}{2} \rho C_d A |v|\cdot v\) 其中:

  • \(\rho\):流体密度
  • \(C_d\):阻力系数(形状相关)
  • \(A\):物体投影面积

3. 代码简化实现

忽略环境参数,通过调节整体比例系数 \(k_{\text{drag}}\) 适配场景:

void ApplyFluidDrag(RigidBody& body, float k_drag) {
    Vector2 drag_force = body.velocity.Normalized() * 
                       (body.velocity.LengthSquared() * -k_drag);
    body.ApplyForce(drag_force);
}

4. 物理特征

  • 阻力随速度指数级增长
  • 与速度方向相反(作用向量中保留方向分量)

IV. 弹簧力(Spring Force)

1. 胡克定律(Hooke’s Law)

弹簧力是弹性形变的恢复力,满足 位移-力线性关系: \(F_{\text{spring}} = -k \cdot (x - x_{\text{rest}})\) 其中 \(k\) 为弹性系数,\(x_{\text{rest}}\) 为自然长度。

2. 阻尼修正

实际弹簧存在能量损耗(如空气摩擦、材料内耗),需增加速度相关阻尼项: \(F_{\text{spring}} = -k \cdot \Delta x - b \cdot v\) 其中 \(b\) 为阻尼系数,防止弹簧系统无限震荡。

3. 代码实现(双粒子弹簧)

void ApplySpringForce(Particle& p1, Particle& p2, 
                     float k, float b, float rest_length) {
    Vector2 delta_pos = p2.position - p1.position;
    float curr_length = delta_pos.Length();
    Vector2 dir = delta_pos / curr_length; // 单位向量
  
    // 胡克定律计算弹性力
    float stretch = curr_length - rest_length;
    Vector2 spring_force = dir * (k * stretch);
  
    // 计算相对速度(阻尼项)
    Vector2 rel_velocity = p2.velocity - p1.velocity;
    Vector2 damping_force = dir * (b * Dot(rel_velocity, dir));
  
    // 总弹簧力
    Vector2 total_force = spring_force + damping_force;
  
    p1.ApplyForce(total_force);
    p2.ApplyForce(-total_force);
}

4. 应用模式

  • 布料模拟(粒子间的弹性连接)
  • 软体动物的肌肉动力学
  • 悬挂系统的减震器建模

V. 静摩擦力与动摩擦力

1. 静摩擦(Static Friction)

  • 定义:阻碍物体间 相对滑动趋势 的力。
  • 最大静摩擦:\(F_{\text{static\_max}} = \mu_s \cdot N\),其中 \(\mu_s\) 为静摩擦系数,\(N\) 为法向压力。
  • 触发条件:外部切向力 \(F_{\text{ext}} < F_{\text{static\_max}}\) 时生效。

2. 动摩擦(Kinetic Friction)

  • 定义:物体已发生滑动时的阻抗力。
  • 公式:\(F_{\text{kinetic}} = \mu_k \cdot N\),\(\mu_k\) 通常小于 \(\mu_s\)。

3. 碰撞响应中的摩擦力模型(示例)

void ResolveCollisionWithFriction(RigidBody& body, 
                                  Vector2 collision_normal,
                                  float mu_static, float mu_kinetic) {
    // 1. 计算法向冲量(假设恢复系数已在冲量中处理)
    Vector2 normal_impulse = ... ; 

    // 2. 计算切向速度
    Vector2 tangent_vel = body.velocity - 
                         Dot(body.velocity, normal) * normal;

    // 3. 确定摩擦模式
    float max_friction = mu_static * normal_impulse.Length();
    if (tangent_vel.Length() < 1e-5) { 
        // 静摩擦:完全抵消切向速度
        body.velocity -= tangent_vel; 
    } else {
        // 动摩擦:施加反向力
        Vector2 friction_force = -tangent_vel.Normalized() * 
                               (mu_kinetic * normal_impulse.Length());
        body.ApplyForce(friction_force);
    }
}

4. 关键参数参考


| 材质组合 | \(\mu_{\text{static}}\) | \(\mu_{\text{kinetic}}\) | |——————|————————-|————————–| | 橡胶-混凝土 | 1.0 | 0.8 | | 钢铁-钢铁 | 0.6 | 0.4 | | 冰-冰 | 0.1 | 0.03 |


VI. 场力(Field Forces)—— 引力、磁力

1. 万有引力(Newtonian Gravity)

\(F_{\text{gravity}} = G \frac{m_1 m_2}{r^2} \cdot \hat{r}\) 在多体系统中,需循环遍历所有可能组合:

void ComputeGravityBetweenParticles(vector<Particle>& particles, 
                                    float G) {
    for (int i = 0; i < particles.size(); ++i) {
        for (int j = i+1; j < particles.size(); ++j) {
            Vector2 delta = particles[j].position - particles[i].position;
            float r_sq = delta.LengthSquared();
            if (r_sq < 0.01f) continue; // 避免奇点

            float force_mag = G * particles[i].mass * particles[j].mass / r_sq;
            Vector2 force = delta.Normalized() * force_mag;

            particles[i].ApplyForce(force);
            particles[j].ApplyForce(-force);
        }
    }
}

2. 洛伦兹磁力(Lorentz Force)

\(F_{\text{magnetic}} = q (v \times B)\)

void ApplyMagneticForce(Particle& p, Vector2 B_field) {
    Vector2 velocity = p.velocity;
    float q = p.charge;

    // 叉积结果(z分量的矢量形式)
    Vector2 force = q * Vector2(velocity.y * B_field.z - velocity.z * B_field.y,
                               velocity.z * B_field.x - velocity.x * B_field.z);

    p.ApplyForce(force);
}

VII. 冲击力(Impulse Forces)

1. 瞬态力的数学模型

冲击力表示短时间内的高强度力,数学上用 冲量(Impulse, J) 描述: \(J = \int F \, dt \quad \Rightarrow \quad \Delta v = \frac{J}{m}\)

2. 代码直接改变动量

void ApplyImpulse(RigidBody& body, Vector2 impulse) {
    body.velocity += impulse * body.inv_mass; // 使用逆质量优化
}

3. 典型应用

  • 子弹击中目标时传递冲量
  • 跳跃瞬间的速度突变
  • 爆炸冲击波的离散力作用

代码实战:综合力场演示

模拟一颗弹珠受 重力、空气阻力、地面摩擦与弹簧弹射 的物理效果:

class MarbleSimulation {
    vector<Particle> marbles;
    vector<Spring> springs; // 弹射弹簧系统

    void Update(float dt) {
        // 清除力
        for (auto& m : marbles) m.force = Vector2(0,0);

        // 应用全局力:重力 + 空气阻力
        Vector2 gravity(0, 9.8f);
        for (auto& m : marbles) {
            m.ApplyForce(gravity * m.mass);
            ApplyFluidDrag(m, 0.05f);
        }

        // 应用弹簧力(例如弹弓装置)
        for (auto& s : springs) {
            s.ApplyForceToMarbles();
        }

        // 地面摩擦计算(假设地面为y=0)
        for (auto& m : marbles) {
            if (m.position.y <= 0) {
                ResolveGroundFriction(m, 0.4f, 0.2f);
            }
        }

        // 更新所有状态
        for (auto& m : marbles) m.Update(dt);
    }

    void ResolveGroundFriction(Particle& m, float mu_s, float mu_k) {
        // 法向力(假设无弹跳)
        float normal_force = m.mass * 9.8f;

        // 切向速度
        Vector2 tangent_vel(m.velocity.x, 0);
        if (tangent_vel.Length() < 0.01f) { // 静摩擦
            m.velocity.x = 0;
        } else {                            // 动摩擦
            Vector2 friction = -tangent_vel.Normalized() * (mu_k * normal_force);
            m.ApplyForce(friction);
        }
    }
};

性能优化与误差控制

优化策略

  1. 力的作用域分区:将频繁计算的力(如粒子间引力)通过空间划分(四叉树/网格)减少遍历次数。
  2. 逆质量预计算:所有 mass 在初始化阶段转换为 inv_mass ,避免逐帧除法。
  3. SIMD指令加速:使用SSE/AVX并行处理多个粒子的矢量运算。

通过精确建模各类力并合理组合,可创造出从逼真机械系统到奇幻魔法效果的全方位物理交互体验。实际开发中需在 物理准确性计算效率视觉表现力 之间寻找平衡。

Tags: Gamedev Public