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

如何消除LibGDX中Box2D子弹与敌人碰撞产生的冲量?

LibGDX + Box2D 子弹击敌后人仍偏移的解决办法

问题背景

用LibGDX结合Box2D开发游戏,存在敌人和子弹两类物体。子弹击中敌人后会被标记删除,但敌人会因碰撞冲量产生微小却明显的位置偏移——即使在postSolve中把敌人的线速度设为0,偏移问题依然存在。

子弹的Fixture用户数据标记为3,当前postSolve实现代码:

public void postSolve(Contact contact, ContactImpulse contactImpulse) {

    Fixture A = contact.getFixtureA();
    Fixture B = contact.getFixtureB();

    if (A.getUserData().equals(3)) {   
        deleteList.add(A.getBody());
        B.getBody().setLinearVelocity(0,0);
    }
    if (B.getUserData().equals(3)) {   
        deleteList.add(B.getBody());
        A.getBody().setLinearVelocity(0,0);
    }
}

问题效果:
子弹击敌后人位置偏移示例

核心原因

postSolve是在碰撞完成、冲量已经作用到物体之后才触发的,这时候再重置线速度,碰撞已经改变了敌人的位置,所以偏移依然会出现。

实用解决方法

方法1:提前阻断碰撞冲量(推荐)

在preSolve阶段直接关闭碰撞的冲量传递,从根源上避免敌人被推动:

public void preSolve(Contact contact, Manifold oldManifold) {
    Fixture fixtureA = contact.getFixtureA();
    Fixture fixtureB = contact.getFixtureB();

    boolean isBulletCollision = fixtureA.getUserData().equals(3) || fixtureB.getUserData().equals(3);
    if (isBulletCollision) {
        // 禁用当前碰撞,阻止冲量传递
        contact.setEnabled(false);
        // 标记子弹待删除
        Body bulletBody = fixtureA.getUserData().equals(3) ? fixtureA.getBody() : fixtureB.getBody();
        deleteList.add(bulletBody);
    }
}

方法2:保留碰撞反馈但重置位置

如果需要敌人保留受击的视觉反馈(比如动画)但不能位移,可在碰撞前记录位置,碰撞后强行恢复:

  1. 先在beginContact中记录敌人碰撞前的位置:
private HashMap<Body, Vector2> preCollisionPositions = new HashMap<>();

public void beginContact(Contact contact) {
    Fixture fixtureA = contact.getFixtureA();
    Fixture fixtureB = contact.getFixtureB();

    if (fixtureA.getUserData().equals(3)) {
        preCollisionPositions.put(fixtureB.getBody(), fixtureB.getBody().getPosition().cpy());
    }
    if (fixtureB.getUserData().equals(3)) {
        preCollisionPositions.put(fixtureA.getBody(), fixtureA.getBody().getPosition().cpy());
    }
}
  1. 然后在postSolve中重置速度并恢复位置:
public void postSolve(Contact contact, ContactImpulse contactImpulse) {
    Fixture fixtureA = contact.getFixtureA();
    Fixture fixtureB = contact.getFixtureB();

    if (fixtureA.getUserData().equals(3)) {   
        deleteList.add(fixtureA.getBody());
        Body enemyBody = fixtureB.getBody();
        enemyBody.setLinearVelocity(0, 0);
        // 恢复到碰撞前的位置
        Vector2 prePos = preCollisionPositions.get(enemyBody);
        enemyBody.setTransform(prePos, enemyBody.getAngle());
        preCollisionPositions.remove(enemyBody);
    }
    if (fixtureB.getUserData().equals(3)) {   
        deleteList.add(fixtureB.getBody());
        Body enemyBody = fixtureA.getBody();
        enemyBody.setLinearVelocity(0, 0);
        Vector2 prePos = preCollisionPositions.get(enemyBody);
        enemyBody.setTransform(prePos, enemyBody.getAngle());
        preCollisionPositions.remove(enemyBody);
    }
}

方法3:检查敌人Body类型

如果敌人不需要物理模拟移动,直接将其Body类型设置为BodyDef.BodyType.StaticBody或KinematicBody,这类Body不会受到碰撞冲量影响,自然不会偏移。

内容的提问来源于stack exchange,提问作者Happy Trash Bin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 10:23:15