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

LibGDX移除Enemy实体时崩溃求助:碰撞检测后移除致游戏崩溃

问题分析与解决方案

移除Enemy时崩溃大概率是ConcurrentModificationError,核心原因及修复方案如下:

核心问题

  1. 多线程并发冲突:EntityManager中的EnemyList可能被游戏其他线程(比如渲染线程、实体更新线程)同时遍历或修改,你在碰撞检测逻辑里用Iterator移除元素时触发了并发修改异常。
  2. 列表引用直接暴露:如果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的操作都加同步锁:

  1. 在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
  1. 修改碰撞检测代码:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 12:05:25