LibGDX Box2D碰撞触发时删除物体导致游戏崩溃问题求助
LibGDX Box2D抛射物碰撞敌人后崩溃问题修复
问题描述
在LibGDX的Box2D模块中处理抛射物与敌人的碰撞逻辑,期望抛射物击中敌人后消失,但碰撞发生时游戏直接崩溃。尝试过在update方法调用world.destroyBody()、body.destroyFixture(),也试过在beginContact中添加物体到删除列表,均未解决问题。
原代码
CollisionProcessing类
public class CollisionProcessing implements ContactListener { World world; public static ArrayList<Body> deleteList = new ArrayList<>(); public CollisionProcessing(World world) { super(); this.world = world; } @Override public void beginContact(Contact contact) {} @Override public void endContact(Contact contact) {} @Override public void preSolve(Contact contact, Manifold manifold) {} @Override public void postSolve(Contact contact, ContactImpulse contactImpulse) { Fixture A = contact.getFixtureA(); Fixture B = contact.getFixtureB(); if (A == null || B == null) return; if (A.getUserData() == null || B.getUserData() == null) return; if (A.getUserData().equals(3)) { System.out.println("Bullet Body collision"); deleteList.add(A.getBody()); } if (B.getUserData().equals(3)) { System.out.println("Body Bullet collision"); deleteList.add(B.getBody()); } } public static ArrayList<Body> getDeleteList() { return deleteList; } }
update方法
public void update() { deleteList = CollisionProcessing.getDeleteList(); deleteList.forEach(obj -> { obj.setActive(false); world.destroyBody(obj); }); world.step(1/60f, 6, 2); inputUpdate(); cameraUpdate(); enemies.forEach(enemy -> enemyUpdate(enemy)); bullets.forEach(bullet -> bulletUpdate(bullet.getPoint().x, bullet.getPoint().y)); }
问题原因与修复方案
核心问题
- 销毁时机错误:在
world.step()前销毁Body,此时Box2D仍在处理当前帧物理计算,直接修改世界结构会触发崩溃。 - 删除列表未清空:重复使用同一静态列表,导致已销毁的Body被重复操作。
- 静态列表风险:静态
deleteList若存在多实例会引发数据混乱,且无法保证线程安全。
具体修复步骤
1. 修改CollisionProcessing类
- 移除
deleteList的静态修饰符,改为实例变量 - 移动物体检测逻辑到
beginContact(比postSolve更适合触发碰撞销毁逻辑) - 添加重复添加检查,避免同一Body被多次加入删除列表
public class CollisionProcessing implements ContactListener { World world; public ArrayList<Body> deleteList = new ArrayList<>(); public CollisionProcessing(World world) { this.world = world; } @Override public void beginContact(Contact contact) { Fixture A = contact.getFixtureA(); Fixture B = contact.getFixtureB(); if (A == null || B == null) return; if (A.getUserData() == null || B.getUserData() == null) return; checkAndAddToDeleteList(A); checkAndAddToDeleteList(B); } private void checkAndAddToDeleteList(Fixture fixture) { if (fixture.getUserData().equals(3)) { System.out.println("Bullet collision detected"); Body body = fixture.getBody(); if (!deleteList.contains(body)) { deleteList.add(body); } } } @Override public void endContact(Contact contact) {} @Override public void preSolve(Contact contact, Manifold manifold) {} @Override public void postSolve(Contact contact, ContactImpulse contactImpulse) {} public ArrayList<Body> getDeleteList() { return deleteList; } }
2. 调整update方法逻辑
- 先执行
world.step()完成物理计算,再处理Body销毁 - 销毁完成后清空删除列表,避免重复操作
- 增加Body活跃状态检查,防止重复销毁
public void update() { // 先完成当前帧物理模拟 world.step(1/60f, 6, 2); // 处理Body销毁(需持有CollisionProcessing实例引用) ArrayList<Body> deleteList = collisionProcessing.getDeleteList(); for (Body body : deleteList) { if (body.isActive()) { body.setActive(false); world.destroyBody(body); } } deleteList.clear(); // 清空列表,避免重复操作 inputUpdate(); cameraUpdate(); enemies.forEach(this::enemyUpdate); bullets.forEach(bullet -> bulletUpdate(bullet.getPoint().x, bullet.getPoint().y)); }
额外注意事项
- 禁止在
ContactListener回调中直接销毁Body/Fixture,必须延迟到world.step()之后 - 确保抛射物Fixture的
UserData是Integer类型的3,避免类型不匹配导致逻辑失效 - 若使用多个
CollisionProcessing实例,需保证每个实例持有独立的删除列表
内容的提问来源于stack exchange,提问作者Happy Trash Bin
相关产品推荐
相关产品推荐

