如何消除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:保留碰撞反馈但重置位置
如果需要敌人保留受击的视觉反馈(比如动画)但不能位移,可在碰撞前记录位置,碰撞后强行恢复:
- 先在
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()); } }
- 然后在
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
相关产品推荐
相关产品推荐

