【ECS框架】专业级游戏架构 + 可视化调试插件,5年持续更新!附完整教程

不懂就问,这种最佳实践 ,不消耗性能吗?一个bool搞定的事情,搞一个类实例

至少割草(塔防都算吧)这种抠性能的游戏我是不太敢用

不消耗,割草性能会更高

啥原理?1234

ECS框架和Cocos Creator使用了不同的导入路径,所以用法上完全没有影响

    import { Component as CocosComponent, _decorator } from 'cc';

    import { Component as ECSComponent } from '@esengine/ecs-framework';

几个问题我一起回答把,性能上一定比传统开发是要好的

1. 批量处理 > 逐个处理

传统游戏开发是每个敌人单独调用update(),函数调用开销巨大,但是ECS做法一个MovementSystem处理1000个敌人,循环开销几乎为0,游戏1000个单位,ECS比传统方式快5倍以上

2. 智能缓存 > 重复计算

框架有位掩码缓存、查询结果缓存、原型系统缓存, 组件查询从O(n)降到O(1), 并且对象池避免频繁new/delete,割草游戏就算10000个敌人依然60fps稳定,瓶颈完全在你的渲染性能上。

3. 按需计算 > 全量更新

一般正常游戏所有对象每帧都要检查所有逻辑,而 ECS做法只有包含特定组件的实体才参与特定系统,1000个敌人,只有200个在移动?只会处理200个

2赞

没有demo吗

赞!求一个Cocos的Demo
我已经买了插件啦

传统写法


// 最开始认为的代码
class Enemy {
    isDead = false;
}

// 实际项目3个月后

class Enemy {
    isDead = false;
    isPlayingDeathAnim = false;
    deathAnimTime = 0;
    deathEffectType = "explosion";
    hasPlayedDeathEffect = false;
    canRevive = true;
    reviveTime = 5.0;
    isReviving = false;
    deathCause = "";
    killerPlayerId = -1;

    update(deltaTime) {
        // 一堆复杂的if-else逻辑
        if (this.isDead && !this.isPlayingDeathAnim && !this.hasPlayedDeathEffect) {
            this.startDeathAnimation();
            this.playDeathEffect();
            this.hasPlayedDeathEffect = true;
        }

        if (this.isPlayingDeathAnim) {
            this.deathAnimTime += deltaTime;
            if (this.deathAnimTime > 2.0) {
                this.isPlayingDeathAnim = false;
                if (this.canRevive) {
                    this.startReviveCountdown();
                } else {
                    this.destroy();
                }
            }

        }

        // ... 更多复杂逻辑

    }

}

ECS写法

class DeadComponent extends Component {
    deathTime = Time.totalTime;
    cause = "";
    killerPlayerId = -1;
}

class DeathAnimationComponent extends Component {
    effectType = "explosion";
    duration = 2.0;
    currentTime = 0;
    hasPlayedEffect = false;
}

class RevivableComponent extends Component {
    reviveTime = 5.0;
    canRevive = true;
}

// 系统:专门处理一种逻辑
class DeathAnimationSystem extends EntitySystem {
    process(entities) {
        // 只处理正在播放死亡动画的实体
        for (const entity of entities) {
            const anim = entity.getComponent(DeathAnimationComponent);
            anim.currentTime += Time.deltaTime;
            
            if (anim.currentTime >= anim.duration) {
                entity.removeComponent(DeathAnimationComponent);
                if (!entity.hasComponent(RevivableComponent)) {
                    entity.destroy();
                }
            }
        }
    }
}

class ReviveSystem extends EntitySystem {
    process(entities) {
        // 只处理可复活的死亡实体
        for (const entity of entities) {
            const revive = entity.getComponent(RevivableComponent);
            revive.reviveTime -= Time.deltaTime;
            
            if (revive.reviveTime <= 0) {
                entity.removeComponent(DeadComponent);
                entity.removeComponent(RevivableComponent);
                entity.addComponent(new HealthComponent(100));
            }
        }
    }
}

传统方式的问题

  • 所有逻辑混在一个update()里:每个敌人每帧都要检查所有状态
  • 无法按需处理:死了的敌人还在检查移动逻辑,活着的敌人还在检查复活逻辑
  • 扩展困难:新增一种死亡效果,要改Enemy类的update方法

ECS的优势

  • 按需处理:只有正在播放死亡动画的实体才进入DeathAnimationSystem
  • 职责分离:死亡动画逻辑和复活逻辑完全独立
  • 性能更好:1000个敌人,可能只有50个在播放死亡动画,只处理这50个

添加新死亡效果:

  • 传统方式: 修改Enemy类的update方法,增加新的if-else分支

  • ECS方式: 新增PoisonDeathComponent + PoisonDeathSystem,不动现有代码

ECS不是组件更少,而是逻辑更清晰,性能更好。组件多一点不是问题,关键是避免了"所有实体每帧检查所有逻辑"的性能杀手

塔防、割草游戏正是ECS的强项,大量同类对象是ECS最擅长优化的场景。

2赞

已购买,支持

1赞

购买支持一下,等下个项目再试试好用不

1赞

感谢各位支持,最近也会写一个类割草的demo,这两天忙着结婚婚宴来不及回复

新婚快乐 :partying_face: :partying_face: :partying_face:

1赞

更想你出个插件开发教程 :grinning:

ECS结合一下行为树?

这个已经在做了,已经结合了一部分

ecs插件v1.0.3已更新发布
![]


优化调试面板(更新后需要再欢迎界面更新ecs框架版本至v2.1.23才能使用)

  • 新增实体节点查看器
  • 新增组件属性查看器
  • 新增内存快照功能
  • 优化调试性能

看你帖子标记的是creator2.x的。结果进去是3.8.6以上的哈。
还没研究过ecs模式的开发。unity之前看过别人用过。但总感觉挺麻烦的一直没用。

论坛里没找到可以发布3.x的插件的位置所在发在这了,商店里写了正确的版本要求3.8.6的,因为考虑到了易用性所以没有和unity架构一样,他们那个写起来确实很不舒服,我也不太喜欢unity的ecs那套,可以看一下文档的示例,写法还是很好懂的 :heart:

橘子大佬,这个一定要继承 Component 吗?
我想结合fairygui一起开发的话能用上么?不打算使用cocos原本的ui来开发。但看到你例子上都是继承Component的。
然后都是通过getComponent和addComponent来获取组件。那么我继承使用的话是否就不可以了?