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
相关产品推荐
相关产品推荐

