You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

2D物理引擎多边形碰撞恢复系数异常及弹性碰撞失效问题求助

问题分析与修复方案

核心问题

  • 多接触点冲量独立计算错误:当前实现对每个接触点单独计算冲量并直接应用,未考虑多个接触点的约束耦合,导致冲量叠加后能量失衡,出现反弹高度随接触点间距波动的问题。
  • 错误的提前终止逻辑:第一个接触点的numerator < 0时直接return,会忽略其他有效接触点的处理,导致碰撞响应不完整。
  • 冲量分配未遵循物理规律:双接触点时,冲量未按接触点的有效质量比例分配,而是独立计算,违反能量守恒。

修复后的代码实现

public void Resolve_collision_wiki(in Physics_manifold contact)
{
    Physics_body body_a = contact.body_a;
    Physics_body body_b = contact.body_b;
    Physics_vector normal = contact.normal;
    int contact_count = contact.contact_count;
    float e = 1.0f;

    Physics_vector[] ra = new Physics_vector[2];
    Physics_vector[] rb = new Physics_vector[2];
    float[] effectiveMass = new float[2];
    float[] j = new float[2];

    // 计算每个接触点的基础数据
    for (int i = 0; i < contact_count; i++)
    {
        var contactPoint = i == 0 ? contact.contact_point_1 : contact.contact_point_2;
        ra[i] = contactPoint - body_a.Position;
        rb[i] = contactPoint - body_b.Position;

        // 计算接触点相对法向速度
        Physics_vector relativeVelocity = body_b.Linear_velocity - body_a.Linear_velocity 
            + Physics_math.Cross_product(body_b.Angular_velocity, rb[i]) 
            - Physics_math.Cross_product(body_a.Angular_velocity, ra[i]);
        float normalVelocity = Physics_math.Dot_product(relativeVelocity, normal);

        // 物体正在分离,跳过该接触点
        if (normalVelocity > 0)
        {
            j[i] = 0;
            continue;
        }

        // 计算接触点有效质量
        float raCrossN = Physics_math.Cross_product(ra[i], normal);
        float rbCrossN = Physics_math.Cross_product(rb[i], normal);
        effectiveMass[i] = body_a.inverse_mass + body_b.inverse_mass 
            + (raCrossN * raCrossN) * body_a.inverse_inertia 
            + (rbCrossN * rbCrossN) * body_b.inverse_inertia;

        effectiveMass[i] = effectiveMass[i] == 0 ? float.MaxValue : 1.0f / effectiveMass[i];

        // 初步计算冲量
        j[i] = -(1 + e) * normalVelocity * effectiveMass[i];
    }

    // 双接触点时按有效质量比例分配总冲量
    if (contact_count == 2)
    {
        float totalJ = j[0] + j[1];
        float massRatio = effectiveMass[0] / (effectiveMass[0] + effectiveMass[1]);
        j[0] = totalJ * massRatio;
        j[1] = totalJ * (1 - massRatio);
    }

    // 应用冲量到物体
    for (int i = 0; i < contact_count; i++)
    {
        if (j[i] <= 0) continue;

        Physics_vector impulse = normal * j[i];
        // 更新线性速度
        body_a.Linear_velocity -= impulse * body_a.inverse_mass;
        body_b.Linear_velocity += impulse * body_b.inverse_mass;

        // 更新角速度
        float raCrossImpulse = Physics_math.Cross_product(ra[i], impulse);
        float rbCrossImpulse = Physics_math.Cross_product(rb[i], impulse);
        body_a.Angular_velocity -= raCrossImpulse * body_a.inverse_inertia;
        body_b.Angular_velocity += rbCrossImpulse * body_b.inverse_inertia;
    }
}

关键修改说明

  • 移除错误提前终止:将return改为跳过当前接触点,确保所有有效接触点都被处理。
  • 有效质量计算优化:单独计算每个接触点的有效质量,避免重复计算,提升准确性。
  • 双接触点冲量分配:按有效质量比例分配总冲量,确保冲量分布符合物理规律,解决反弹高度波动问题。
  • 分离接触点过滤:跳过正在分离的接触点,避免无效冲量施加。

额外注意事项

  • 转动惯量正确性:方形的转动惯量需按公式 (mass * (width² + height²)) / 12 计算,否则会导致角动量响应异常。
  • 碰撞 manifold 准确性:确保接触点和法向量计算无误,双接触点的法向量需保持一致。
  • 迭代求解优化:若仍存在能量损失,可引入Gauss-Seidel迭代,多次调整冲量直到约束满足,这是多接触点碰撞的标准处理方式。

内容的提问来源于stack exchange,提问作者Lprogrammer20

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 21:57:35