LibGDX移除Enemy实体时崩溃求助:碰撞检测后移除致游戏崩溃
问题分析与解决方案
移除Enemy时崩溃大概率是ConcurrentModificationError,核心原因及修复方案如下:
核心问题
- 多线程并发冲突:EntityManager中的EnemyList可能被游戏其他线程(比如渲染线程、实体更新线程)同时遍历或修改,你在碰撞检测逻辑里用Iterator移除元素时触发了并发修改异常。
- 列表引用直接暴露:如果
entityManager.getEnemyList()返回的是原列表的直接引用,而非隔离副本,迭代过程中移除元素会干扰其他正在遍历该列表的逻辑。
修复方案
方案1:收集待移除实体,统一批量删除
这种方式从根源上避免遍历过程中修改列表,既解决并发问题,也更高效:
public void collideAlien(Player player) { // 获取实体列表 List<Enemy> enemyList = entityManager.getEnemyList(); List<PlayerBullet> playerBulletList = entityManager.getPlayerbulletList(); // 收集需要移除的实体 List<Enemy> enemiesToRemove = new ArrayList<>(); List<PlayerBullet> bulletsToRemove = new ArrayList<>(); for (Enemy alien : enemyList) { for (PlayerBullet bullet : playerBulletList) { if (alien.isCollide(bullet)) { // 播放音效 alienHitSound.setVolume(audioSettings.getSoundVolume()); if (audioSettings.isSoundEnabled()) { alienHitSound.play(); } // 更新分数 switch (alien.getEnemyType()) { case "empty": player.setScore(player.getScore() + 10); break; case "space": player.setScore(player.getScore() + 20); break; case "nonSpace": player.setScore(Math.max(0, player.getScore() - 10)); break; } // 标记待移除实体 enemiesToRemove.add(alien); bulletsToRemove.add(bullet); break; // 一个子弹只命中一个外星人 } } } // 批量移除实体 enemyList.removeAll(enemiesToRemove); playerBulletList.removeAll(bulletsToRemove); }
方案2:同步所有对EnemyList的访问
如果必须在遍历过程中移除元素,要确保所有访问EnemyList的操作都加同步锁:
- 在EntityManager中添加锁机制:
// EntityManager内部代码 private final List<Enemy> enemyList = new ArrayList<>(); private final Object enemyLock = new Object(); // 返回不可变视图,避免外部直接修改原列表 public List<Enemy> getEnemyList() { return Collections.unmodifiableList(enemyList); } // 提供同步的移除方法 public void removeEnemy(Enemy enemy) { synchronized (enemyLock) { enemyList.remove(enemy); } } // 其他修改EnemyList的方法都要加synchronized
- 修改碰撞检测代码:
public void collideAlien(Player player) { List<PlayerBullet> playerBulletList = entityManager.getPlayerbulletList(); // 锁定EnemyList,确保遍历和修改操作独占 synchronized (entityManager.getEnemyLock()) { Iterator<Enemy> enemyIterator = entityManager.getEnemyList().iterator(); while (enemyIterator.hasNext()) { Enemy alien = enemyIterator.next(); for (int j = 0; j < playerBulletList.size(); j++) { PlayerBullet bullet = playerBulletList.get(j); if (alien.isCollide(bullet)) { // 音效与分数逻辑... enemyIterator.remove(); playerBulletList.remove(j); break; } } } } }
额外优化点
- 用
switch替代多个if判断敌人类型,代码更简洁高效。 - 处理
nonSpace敌人扣分时,用Math.max(0, player.getScore() - 10)简化边界判断。 - 子弹遍历也建议用收集待移除列表的方式,避免索引遍历移除时的元素偏移问题(比如移除j后,j++会跳过下一个子弹)。
内容的提问来源于stack exchange,提问作者GTKJ
相关产品推荐
相关产品推荐

