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

ECS实体移除问题求助:游戏循环删实体触发并发修改异常

关于ECS中安全移除实体的解决方案

嘿,很高兴看到你在尝试ECS架构——这玩意儿上手确实有不少小坑,特别是实体生命周期这块儿!你用toRemove标记的思路完全是正确的方向,直接在遍历实体列表时删除元素确实会触发ConcurrentModificationException,因为Java的普通迭代器不允许在遍历过程中修改集合结构。下面给你完善几种可行的实现方案:

方案一:标记+统一阶段清理(最常用)

核心思路是:所有系统只遍历未标记的实体,在游戏循环的固定阶段统一清理标记的实体,这样就完全避免了遍历和修改的冲突。

示例代码:

首先给实体类添加标记属性:

public class Entity {
    private boolean markedForRemoval = false;

    public void markForRemoval() {
        this.markedForRemoval = true;
    }

    public boolean isMarkedForRemoval() {
        return markedForRemoval;
    }
}

然后调整游戏主循环的流程,把清理步骤放在所有系统更新之后:

// 游戏主循环
while (gameRunning) {
    // 1. 处理输入系统
    inputSystem.update();

    // 2. 运行所有逻辑系统(AI、物理、碰撞等)
    for (GameSystem system : systems) {
        // 过滤掉标记要删除的实体,传递给系统处理
        List<Entity> activeEntities = entities.stream()
                .filter(e -> !e.isMarkedForRemoval())
                .collect(Collectors.toList());
        system.update(activeEntities);
    }

    // 3. 运行渲染系统(同样只处理活跃实体)
    drawingSystem.update(entities.stream()
            .filter(e -> !e.isMarkedForRemoval())
            .collect(Collectors.toList()));

    // 4. 统一清理标记的实体
    entities.removeIf(Entity::isMarkedForRemoval);
}

方案二:延迟删除队列(更灵活)

如果需要在删除实体前执行一些额外逻辑(比如播放死亡动画、释放组件资源),可以维护一个专门的删除队列,把要移除的实体先加入队列,在清理阶段批量处理:

示例代码:

// 在ECS管理器中维护删除队列
private Queue<Entity> entitiesToRemove = new LinkedList<>();

// 对外提供标记删除的方法
public void scheduleEntityRemoval(Entity entity) {
    if (!entitiesToRemove.contains(entity)) {
        // 执行销毁前的钩子方法(比如实体的死亡回调)
        entity.onDestroy();
        entitiesToRemove.add(entity);
    }
}

// 清理阶段的处理
private void cleanupEntities() {
    while (!entitiesToRemove.isEmpty()) {
        Entity entity = entitiesToRemove.poll();
        // 从实体列表中移除
        entities.remove(entity);
        // 清理实体关联的组件资源(比如纹理、音效)
        for (Component component : entity.getComponents()) {
            component.dispose();
        }
    }
}

然后在游戏主循环的清理步骤调用cleanupEntities()即可。

关键注意点

  • 绝对不要让任何系统在遍历实体列表时直接调用remove()方法,所有删除操作必须通过标记或队列延迟处理。
  • 如果你的ECS中有依赖实体的组件(比如引用其他实体的组件),清理时要注意解除引用,避免内存泄漏。
  • 很多成熟的ECS框架(比如Artemis-ODB)也是用类似的延迟删除机制,你的思路完全符合行业实践,继续完善就行!

内容的提问来源于stack exchange,提问作者JohnnyGat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:43:32