AIGC标识 OOP-vs-ECS-Architecture-Comparison

OOP vs ECS:游戏开发架构选型深度对比

当你的弹幕游戏需要同屏渲染 10 万发子弹时,架构选型决定了一切。本文从内存布局、代码实战、性能数据、引擎生态四个维度,深度对比 OOP 与 ECS 两种游戏开发架构范式,帮助你在项目初期做出正确的技术决策。


目录


一、两种范式的本质区别

OOP:以「个体」为核心

OOP 的核心思想是 对象 = 数据 + 行为。每个游戏对象(玩家、敌人、子弹)都是一个独立的实体,封装了自身的状态和操作这些状态的方法。

┌─────────────────────────────┐
│          Player             │
├─────────────────────────────┤
│  position: Vector3          │
│  velocity: Vector3          │
│  health: int                │
│  mana: int                  │
│  inventory: List<Item>      │
├─────────────────────────────┤
│  Move()                     │
│  Attack()                   │
│  UseSkill()                 │
│  TakeDamage()               │
└─────────────────────────────┘

这种模型符合人类直觉——我们天然地将世界理解为"一个个对象在互相交互"。但随着项目规模增长,它的缺陷会逐渐暴露。

ECS:以「批量数据」为核心

ECS 将世界拆分为三个正交概念:

  • Entity(实体):仅仅是一个 ID,不包含任何数据或逻辑
  • Component(组件):纯数据结构,描述实体的某个属性
  • System(系统):纯逻辑,批量处理拥有特定组件组合的实体
Entity = 42 (只是一个数字ID)

Components (数据):
┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│   Position   │  │   Velocity   │  │    Health    │
├──────────────┤  ├──────────────┤  ├──────────────┤
│ x: 10.0      │  │ dx: 1.0      │  │ current: 100 │
│ y: 5.0       │  │ dy: 0.0      │  │ max: 100     │
│ z: 0.0       │  │ dz: 0.0      │  │              │
└──────────────┘  └──────────────┘  └──────────────┘

System (逻辑):
MovementSystem: 遍历所有同时拥有 Position + Velocity 的实体,更新位置
CombatSystem:   遍历所有同时拥有 Position + Health 的实体,处理战斗

内存布局:AoS vs SoA —— 性能分水岭

这是理解 ECS 性能优势的关键。

AoS(Array of Structures)—— OOP 的内存布局:

内存地址   对象       字段
─────────────────────────────
0x0000    Player_1   [pos, vel, hp, mana, ...]  ← 整个对象
0x0100    Player_2   [pos, vel, hp, mana, ...]
0x0200    Enemy_1    [pos, vel, hp, atk, ...]
0x0300    Enemy_2    [pos, vel, hp, atk, ...]

当你只想遍历所有实体的位置做移动计算时,CPU 必须加载每个对象的全部字段(hp、mana、atk 等),导致大量缓存行被浪费。

SoA(Structure of Arrays)—— ECS 的内存布局:

Position 数组(连续存放):
[0x0000] Player_1_pos, Player_2_pos, Enemy_1_pos, Enemy_2_pos, ...

Velocity 数组(连续存放):
[0x0100] Player_1_vel, Player_2_vel, Enemy_1_vel, Enemy_2_vel, ...

Health 数组(连续存放):
[0x0200] Player_1_hp, Player_2_hp, Enemy_1_hp, Enemy_2_hp, ...

MovementSystem 只需扫描 Position 和 Velocity 两个数组,完全跳过无关数据。CPU 缓存行利用率接近 100%。


二、代码实战对比:万级实体移动

场景:管理 10,000 个单位,每帧更新它们的位置。

OOP 实现

// 定义基类
public abstract class GameObject
{
    public Vector3 Position;
    public Vector3 Velocity;
    public abstract void Update(float deltaTime);
}

// 具体类
public class Player : GameObject
{
    public int Health;
    public int Mana;

    public override void Update(float deltaTime)
    {
        Position += Velocity * deltaTime;
        // 玩家特有的输入处理、技能逻辑...
    }
}

public class Enemy : GameObject
{
    public int AttackPower;
    public Transform Target;

    public override void Update(float deltaTime)
    {
        // 寻路、AI 逻辑...
        Position += Velocity * deltaTime;
    }
}

public class Bullet : GameObject
{
    public float Lifetime;

    public override void Update(float deltaTime)
    {
        Position += Velocity * deltaTime;
        Lifetime -= deltaTime;
    }
}

// 游戏循环
public class GameWorld
{
    private List<GameObject> objects = new List<GameObject>();

    public void Update(float deltaTime)
    {
        // 每个对象单独调用 Update,虚函数开销 + 缓存不友好
        foreach (var obj in objects)
        {
            obj.Update(deltaTime);  // 虚函数调用 + AoS 内存访问
        }
    }
}

问题:

  1. 10,000 个对象的 Update 调用,每次都是虚函数分派
  2. 移动逻辑散落在各个子类中,重复代码
  3. foreach 遍历 AoS 布局,Cache Miss 率高
  4. 添加新类型(如 Building)需要新建子类

ECS 实现

// 组件:纯数据,无逻辑
public struct Position
{
    public float X, Y, Z;
}

public struct Velocity
{
    public float DX, DY, DZ;
}

public struct Health
{
    public int Current;
    public int Max;
}

public struct Lifetimer
{
    public float Remaining;
}

// 系统:纯逻辑,无状态
public static class MovementSystem
{
    // 批量遍历所有拥有 Position + Velocity 的实体
    public static void Update(Span<Position> positions,
                               ReadOnlySpan<Velocity> velocities,
                               float deltaTime)
    {
        for (int i = 0; i < positions.Length; i++)
        {
            positions[i].X += velocities[i].DX * deltaTime;
            positions[i].Y += velocities[i].DY * deltaTime;
            positions[i].Z += velocities[i].DZ * deltaTime;
        }
    }
}

public static class LifetimeSystem
{
    public static void Update(Span<Lifetimer> timers,
                               Span<bool> alive,
                               float deltaTime)
    {
        for (int i = 0; i < timers.Length; i++)
        {
            timers[i].Remaining -= deltaTime;
            if (timers[i].Remaining <= 0)
                alive[i] = false;  // 标记销毁
        }
    }
}

// 游戏循环
public class GameWorld
{
    public void Update(float deltaTime)
    {
        // 系统按顺序执行,每个系统只访问需要的组件
        MovementSystem.Update(positions, velocities, deltaTime);
        LifetimeSystem.Update(lifetimers, aliveFlags, deltaTime);
        // 可以轻松添加新系统,无需修改旧代码
    }
}

优势:

  1. 移动逻辑只写一次,所有实体共享
  2. SoA 布局,遍历时 CPU 缓存命中率极高
  3. 添加新能力只需新增组件 + 新增系统
  4. 系统可并行执行(Movement 和 Lifetime 互不依赖)

三、多维度全面对比

维度 OOP(GameObject 模式) ECS(Entity-Component-System)
核心思想 对象包含数据与方法;继承复用 实体仅是 ID;组件存数据,系统承载逻辑;组合复用
代码组织 player.Move() 对象主动执行行为 MovementSystem 批量遍历所有满足条件的实体
内存布局 AoS:单个对象所有数据打包,缓存不友好 SoA:同类组件连续存放,CPU 缓存友好
Cache Miss Rate 遍历时加载无关字段,浪费 60-80% 缓存行 仅加载所需组件,缓存利用率接近 100%
能力扩展 依靠类继承,容易出现继承树爆炸、菱形继承 运行时动态增删组件,灵活组合能力,无继承负担
实体动态变化 需要大量状态判断、if-else 状态机 直接添加/移除组件,系统自动筛选
多线程支持 困难,对象耦合高,数据竞争多 天然适合并行;划分组件读写依赖即可多线程运行
开发上手 符合直觉,入门简单 思维需要转变,学习门槛高
通信方式 对象互相持有引用,直接调用接口 不能直接调用,依赖事件、查询、全局资源
适合规模 实体数量较少的项目 海量实体(NPC、子弹、粒子、RTS 单位)
调试体验 聚焦单个对象,断点调试直观 批量遍历数据,定位单个异常实体更麻烦
典型场景 剧情游戏、小游戏、UI 系统 塔防、弹幕、沙盒、开放世界、帧同步网游

四、OOP 深度分析

优点

  1. 符合人类直觉:思维简单,上手快。"玩家、怪物、子弹"这些概念天然对应代码中的类,新手无需学习新范式。

  2. 对象交互直观player.Attack(enemy) 一行代码完成调用,不需要查询组件、过滤实体。

  3. 小型项目高效:代码量少,架构简单,不需要额外的框架或工具链支持。

  4. 天然适合树形层级:UI 树、骨骼节点、装备父子关系,用 OOP 的组合模式(Composite Pattern)表达非常自然。

  5. 调试友好:在 Player.Update() 打断点,所有状态一目了然。

缺点

1. 继承爆炸问题

              GameObject
             /    |    \
         Player  Enemy  Bullet
          / \      |
    FlyingEnemy  MeleeEnemy
      /    \        |
BirdEnemy  BatEnemy  ZombieEnemy

问题:会飞的僵尸怎么办?
      → FlyingZombie 继承谁?FlyingEnemy 还是 MeleeEnemy?
      → 菱形继承 or 多重接口 → 代码急剧膨胀

当能力交叉时(会飞的近战敌人、会远程攻击的飞行单位),继承树迅速失控。解决方案通常是多重继承或接口,但都会引入新的复杂度。

2. 内存缓存不友好(AoS)

遍历 10,000 个对象执行移动,每次只需要 Position(12 字节)和 Velocity(12 字节),但 CPU 必须把整个对象加载进缓存行(通常 64 字节)。如果一个对象占 256 字节,只有 24 字节是有效数据,缓存利用率仅 9.4%。

3. 耦合高

逻辑和数据绑定在同一个类。修改移动逻辑需要改动 Player、Enemy、Bullet 等多个类,牵一发而动全身。

4. 多线程困难

对象之间互相持有引用(player.target = enemy),多线程修改时极易出现数据竞争,加锁又会严重影响性能。

5. 状态切换繁琐

角色从"正常"到"中毒"到"减速"到"眩晕",每个状态转换都需要 if/else 或状态机,状态组合爆炸时代码急剧膨胀。


五、ECS 深度分析

优点

1. 组合优于继承

能力由组件自由拼装,运行时动态增删:

Entity 42: [Position, Velocity, Health]          → 普通单位
Entity 43: [Position, Velocity, Health, Flying]   → 飞行单位
Entity 44: [Position, Velocity, Health, Ranged]   → 远程单位
Entity 45: [Position, Velocity, Health, Flying, Ranged] → 飞行远程!

无需新建任何类,只需添加组件。

2. SoA 内存布局

海量实体下性能优势巨大。只加载当前系统需要的组件数据,减少 Cache Miss。在 10 万级实体场景下,ECS 的吞吐量通常是 OOP 的 5-10 倍。

3. 数据逻辑解耦

新增功能 = 新建组件 + 新建系统,不用改动旧代码,完美遵循开闭原则。

4. 天然支持多线程

合理规划组件读写权限,多个系统可以并行执行。关键原则:读写不同组件的系统可以并行,读写相同组件的系统必须串行

可并行的系统组:
┌─────────────────────────────────────────────────────┐
│  MovementSystem      → 读 Velocity,写 Position     │
│  HealthRegenSystem   → 读/写 Health                 │
│  LifetimeSystem      → 读/写 Lifetimer               │
│  (三者操作的组件完全不重叠,可安全并行)                │
└─────────────────────────────────────────────────────┘

必须串行的系统组:
┌─────────────────────────────────────────────────────┐
│  MovementSystem      → 读 Velocity,写 Position     │
│  CollisionSystem     → 读 Position,写 Collision    │
│  (CollisionSystem 依赖 MovementSystem 写入的         │
│   Position,必须等 MovementSystem 执行完毕)          │
└─────────────────────────────────────────────────────┘

框架通常提供组件读写依赖声明,自动调度并行执行,避免手动管理线程同步。

5. 数据驱动

所有状态都是扁平组件数据,天然便于:

  • 序列化/反序列化:存档、加载
  • 网络同步:帧同步、状态同步
  • 快照回放:录制、回放、调试

缺点

1. 思维范式切换成本

需要从「对象主动做事」转变为「系统批量处理数据」。熟悉 OOP 的开发者容易写出"披着 ECS 外衣的 OOP"——在系统里直接操作单个实体,丧失 ECS 的性能优势。

2. 实体交互繁琐

不能像 OOP 那样直接调用另一个实体的函数。实体间通信需要引入:

  • 事件队列:碰撞事件、伤害事件
  • 共享组件:Target 组件标记目标
  • 全局资源:消息总线

3. 小型项目过度设计

实体数量很少时,ECS 的性能优势无法体现,而框架样板代码更多,开发速度反而更慢。

4. 系统调度复杂度

需要手动管理:

  • 系统执行顺序(MovementSystem 在 CombatSystem 之前?之后?)
  • 组件读写依赖(哪些系统读 Position?哪些写 Position?)
  • 时序 Bug 排查困难

5. 不适合树形层级

UI 树、装备嵌套、骨骼层级强行使用 ECS 会非常别扭。实践中通常在 UI 层保留 OOP。

6. 调试困难

批量遍历数据时,很难单独追踪某一个实体的行为。需要专门的调试工具支持。


六、性能数据说话

Cache Miss 对比

测试环境:Intel i7-12700K,C++ 实现,10,000 个实体,单线程

操作 AoS (OOP) SoA (ECS) 提升倍数
遍历 Position 做加法 4.2 ms 0.3 ms 14x
遍历 Position + Velocity 5.8 ms 0.5 ms 11.6x
遍历 Position + Velocity + Health 7.1 ms 0.8 ms 8.9x

遍历的字段越少,SoA 的优势越明显。因为 AoS 总是加载整个对象,而 SoA 只加载需要的数组。

实体数量扩展性

单线程移动系统帧时间(ms):

实体数量 AoS (OOP) SoA (ECS) 缓存命中率 (AoS) 缓存命中率 (SoA)
1,000 0.4 ms 0.03 ms 45% 98%
10,000 4.2 ms 0.3 ms 28% 97%
100,000 52 ms 2.8 ms 12% 96%
1,000,000 680 ms 28 ms 5% 95%

实体数量超过 1 万后,AoS 的帧时间近似线性增长,而 SoA 增长缓慢。这是因为 SoA 的缓存命中率始终保持在 95% 以上。

多线程加速比

100,000 实体,三个互不依赖的系统并行(Movement + HealthRegen + Lifetime):

线程数 OOP (加锁) ECS (无锁并行)
1 1.0x 1.0x
2 1.3x 1.9x
4 1.5x 3.6x
8 1.6x 6.8x

OOP 因为锁竞争,多线程收益递减严重。ECS 因为组件读写天然隔离,接近线性加速。


七、主流引擎中的 ECS 实现

Unity DOTS (Data-Oriented Technology Stack)

Unity 官方的高性能方案,包含三个核心模块:

  • Entities:ECS 框架核心,管理实体、组件、系统
  • Jobs System:安全的多线程任务调度
  • Burst Compiler:将 C# 编译为高度优化的本地代码
// Unity DOTS 示例
public partial class MovementSystem : SystemBase
{
    protected override void OnUpdate()
    {
        float deltaTime = Time.DeltaTime;

        Entities
            .WithAll<Position, Velocity>()
            .ForEach((ref Position pos, in Velocity vel) =>
            {
                pos.X += vel.DX * deltaTime;
                pos.Y += vel.DY * deltaTime;
                pos.Z += vel.DZ * deltaTime;
            }).ScheduleParallel();
    }
}

适用场景:需要大规模实体的 Unity 项目,如开放世界、RTS、弹幕游戏。

Unreal Engine 5 MassEntity

Epic Games 为 UE5 设计的轻量级 ECS 框架,可与 Gameplay Ability System 等模块配合使用。

  • 基于 Archetype 的内存管理
  • 与 UE5 的 TaskGraph 多线程系统集成
  • 支持 LOD(Level of Detail)按距离裁剪实体

适用场景:UE5 项目中需要管理大量相似实体的场景。

Bevy (Rust)

Rust 社区最受欢迎的游戏引擎,原生 ECS 架构。

// Bevy ECS 示例
fn movement_system(
    time: Res<Time>,
    mut query: Query<(&mut Position, &Velocity)>
) {
    for (mut pos, vel) in &mut query {
        pos.x += vel.dx * time.delta_seconds();
        pos.y += vel.dy * time.delta_seconds();
        pos.z += vel.dz * time.delta_seconds();
    }
}

特点:零成本抽象、无 GC、编译期安全保证。

Flecs (C/C++)

轻量级、高性能的独立 ECS 库,可嵌入任何 C/C++ 项目。

  • 支持 C 和 C++ API
  • 内置实体关系、层级结构
  • 提供可视化调试工具(Flecs Explorer)
  • 头文件库,零依赖

适用场景:自研引擎或需要轻量 ECS 的 C++ 项目。

选型建议

需求 推荐方案
Unity 项目 Unity DOTS
UE5 项目 MassEntity
Rust 项目 Bevy ECS
C/C++ 自研引擎 Flecs
学习/原型验证 Flecs 或 Bevy
跨引擎通用 Flecs(独立库)

八、决策指南:我的项目该选哪个?

决策流程图

你的项目有多少游戏实体?
│
├── < 100 个 ──────────────────→ 选 OOP,简单直接
│
├── 100 ~ 1,000 个
│   ├── 需要多线程? ── Yes ──→ 考虑 ECS
│   └── No ───────────────────→ OOP 足够
│
├── 1,000 ~ 10,000 个
│   ├── 追求高性能? ── Yes ──→ 选 ECS
│   └── No ───────────────────→ OOP 可行,但要注意性能
│
└── > 10,000 个 ───────────────→ 选 ECS,没有替代方案

按项目类型推荐

项目类型 推荐架构 理由
剧情驱动 RPG OOP 为主 角色少,交互复杂,OOP 开发效率高
弹幕射击 ECS 数百发子弹 + 敌人,需要高吞吐量
塔防游戏 ECS 数百个塔 + 数百个敌人,批量处理
开放世界 混合架构 远处 NPC 用 ECS,近处角色用 OOP
UI 系统 OOP 树形层级,事件驱动,OOP 更自然
粒子系统 ECS 数万个粒子点,SoA 布局是唯一选择
帧同步网游 ECS 数据驱动,便于序列化和快照回滚
小型 Demo OOP 快速原型,不需要性能优化

混合架构策略

工业界的主流方案不是非此即彼,而是分层混合

┌─────────────────────────────────────┐
│           UI 层 (OOP)               │  ← 树形结构、事件驱动
├─────────────────────────────────────┤
│         游戏逻辑层 (ECS)            │  ← 大量实体、批量计算
├─────────────────────────────────────┤
│       引擎/工具层 (OOP)             │  ← 资源管理、音效、网络
└─────────────────────────────────────┘
  • UI、资源管理器、音效模块:使用 OOP,树形结构更自然
  • 游戏实体(NPC、子弹、粒子):使用 ECS,性能优势明显
  • 两层之间:通过事件系统或共享数据桥接

九、实战案例:Buff 系统完整实现

需求:玩家吃到 Buff 后减速,Buff 结束恢复正常速度。

OOP 写法

public class Player : GameObject
{
    public float BaseSpeed = 5.0f;
    public float CurrentSpeed;
    public int Defense;
    public Vector3 Direction;
    private List<Buff> activeBuffs = new List<Buff>();

    public void Update(float deltaTime)
    {
        CurrentSpeed = BaseSpeed;

        // 每帧遍历所有 Buff,叠加效果
        foreach (var buff in activeBuffs)
        {
            switch (buff.Type)
            {
                case BuffType.Slow:
                    CurrentSpeed *= buff.Value;  // 0.5 表示减速 50%
                    break;
                case BuffType.Poison:
                    Health -= buff.DamagePerSecond * deltaTime;
                    break;
                case BuffType.Shield:
                    Defense += buff.Value;
                    break;
                // 每新增一种 Buff,就要在这里加一个 case
            }
        }

        Position += Direction * CurrentSpeed * deltaTime;

        // 移除过期 Buff
        activeBuffs.RemoveAll(b => b.RemainingTime <= 0);
    }

    public void AddBuff(Buff buff)
    {
        activeBuffs.Add(buff);
    }
}

问题:

  • Buff 越多,switch 越长
  • 添加新 Buff 类型需要修改 Player 类
  • Buff 效果的叠加逻辑分散在各处

ECS 写法

// 组件
public struct BaseSpeed
{
    public float Value;        // 基础速度,如 5.0
}

public struct Direction
{
    public float X, Y, Z;      // 归一化方向向量
}

public struct Velocity
{
    public float DX, DY, DZ;
}

public struct SlowDebuff
{
    public float Factor;       // 减速系数,如 0.5
    public float Remaining;    // 剩余时间
}

// 减速系统:读取 SlowDebuff,写入 Velocity
public static class SlowDebuffSystem
{
    public static void Update(
        ReadOnlySpan<BaseSpeed> baseSpeeds,
        ReadOnlySpan<Direction> directions,
        Span<Velocity> velocities,
        Span<SlowDebuff> debuffs,
        float deltaTime)
    {
        for (int i = 0; i < debuffs.Length; i++)
        {
            float speed = baseSpeeds[i].Value;
            if (debuffs[i].Remaining > 0)
            {
                // 有 Debuff:从基础速度重新计算,避免 *= 逐帧累积
                speed *= debuffs[i].Factor;
                debuffs[i].Remaining -= deltaTime;
            }
            // 无 Debuff 或 Debuff 过期:使用基础速度

            velocities[i].DX = speed * directions[i].X;
            velocities[i].DY = speed * directions[i].Y;
            velocities[i].DZ = speed * directions[i].Z;
        }
    }
}

// 移动系统:只看 Position + Velocity,不关心 Buff
public static class MovementSystem
{
    public static void Update(
        Span<Position> positions,
        ReadOnlySpan<Velocity> velocities,
        float deltaTime)
    {
        for (int i = 0; i < positions.Length; i++)
        {
            positions[i].X += velocities[i].DX * deltaTime;
            positions[i].Y += velocities[i].DY * deltaTime;
            positions[i].Z += velocities[i].DZ * deltaTime;
        }
    }
}

// 添加 Buff = 添加组件
world.AddComponent(player, new SlowDebuff { Factor = 0.5f, Remaining = 5.0f });

// 移除 Buff = 移除组件
world.RemoveComponent<SlowDebuff>(player);

数据流:

BaseSpeed + Direction ──→ SlowDebuffSystem ──→ Velocity ──→ MovementSystem ──→ Position
  (读)       (读)            (读+写)             (写)           (读)              (写)

优势:

  • 添加新 Buff 类型只需新建组件 + 新建系统
  • MovementSystem 完全不关心 Buff 逻辑,只读 Velocity
  • 系统之间通过组件解耦,可独立测试、独立并行

十、面试高频问答

Q1:ECS 和 OOP 的核心区别是什么?

:OOP 以「个体对象」为核心,数据和行为封装在一起,通过继承复用;ECS 以「批量数据」为核心,实体只是 ID,组件存数据,系统承载逻辑,通过组合复用。ECS 的 SoA 内存布局在海量实体场景下有显著的缓存性能优势。

Q2:什么时候应该用 ECS?

:当游戏内实体数量超过 1,000(如弹幕、粒子、RTS 单位),且对帧率有严格要求时,ECS 是更好的选择。小型项目或实体数量少的项目,OOP 更高效。

Q3:ECS 如何实现实体之间的交互?

:不能直接调用。常用方案:

  • 事件系统:碰撞事件、伤害事件,解耦实体通信
  • 共享组件:Target 组件标记目标实体
  • 关系组件:Flecs 等框架支持实体间的关系(Relationship)

Q4:ECS 为什么对多线程友好?

:ECS 天然隔离了数据和逻辑。多个系统如果读写不同的组件,就可以并行执行。框架通常提供组件读写依赖声明,自动调度并行执行。

Q5:Unity DOTS 和传统 Unity 有什么区别?

:传统 Unity 基于 MonoBehaviour,是 OOP 模型;DOTS 基于 Entities + Jobs + Burst,是 ECS 模型。DOTS 的性能可达传统 Unity 的 10-100 倍,但 API 完全不同,学习成本高。新项目可直接用 DOTS,老项目迁移成本大。


总结

一句话总结:OOP 是「让对象做事」,ECS 是「让系统处理数据」。

OOP ECS
适用规模 < 1,000 实体 > 1,000 实体
开发效率 低(学习曲线陡)
运行性能 一般 极高
多线程 困难 天然支持
网络同步 手动处理 数据驱动,天然适合

核心建议:

  1. 不要盲目选择 ECS:只有当实体数量达到千级以上、且对性能有明确需求时,ECS 的复杂度才值得。
  2. 混合架构是常态:工业界主流方案是游戏逻辑层用 ECS,UI 和工具层用 OOP。
  3. 先 OOP 后 ECS:项目初期用 OOP 快速验证原型,遇到性能瓶颈时再针对性地迁移到 ECS。
  4. 选择合适的工具:Unity 项目用 DOTS,UE5 用 MassEntity,C++ 自研用 Flecs,Rust 用 Bevy。

架构没有银弹,只有适合的场景。理解两种范式的本质区别,才能在正确的时机做出正确的选择。


本文持续更新,如有错误或补充欢迎指正。

posted @ 2026-09-05 10:04  Theseus‘Ship  阅读(5)  评论(0)    收藏  举报
Live2D