讲师中心 微信公众号

工具推荐

视频效率加速
立刻MV
立刻MV

低成本歌曲 MV、视频生成

updream

一键生成高质量推广成片

Seko
Seko

AI短剧一键生成成片

AniShort
AniShort

AI短剧协同制作平台

如何在C++中实现ECS架构_游戏开发设计模式

轻萱酱_3005

轻萱酱_3005

发布时间:2025-06-23 22:45:02

|

983人浏览过

|

来源于php中文网

原创

ecs架构通过分离数据、逻辑和行为提升代码灵活性和维护性。其核心是定义entity(实体)、component(组件)和system(系统)三个部分,其中entity为标识符,component为数据容器,system处理逻辑。entitymanager用于管理实体与组件关系,实现组件的添加、删除与访问。性能优化可通过稀疏集、simd指令、数据局部性、避免虚函数及使用编译时多态等方式实现。相比oop,ecs强调数据与逻辑分离,适用于大型项目,而dod则关注数据布局以提升性能。在大型项目中应用ecs应逐步采用、定义清晰组件、使用事件系统与代码生成,并可借助entt等高级库简化开发。

如何在C++中实现ECS架构_游戏开发设计模式

ECS架构,简单来说,就是在C++中把游戏对象的数据、逻辑和行为分离开来,让代码更灵活、更容易维护。它不是银弹,但用对了地方,能大幅提升开发效率。

如何在C++中实现ECS架构_游戏开发设计模式

数据驱动的未来,从ECS开始。

如何在C++中实现ECS架构_游戏开发设计模式

解决方案

立即学习“C++免费学习笔记(深入)”;

在C++中实现ECS架构,核心在于定义三个关键部分:Entity(实体)、Component(组件)和 System(系统)。

如何在C++中实现ECS架构_游戏开发设计模式
  • Entity (实体): 仅仅是一个ID,用来标识游戏中的对象。它本身不包含任何数据或逻辑。在C++中,你可以简单地使用一个整数或者一个类来表示Entity。

    typedef unsigned int Entity;
  • Component (组件): 包含实体的数据。例如,位置、速度、生命值等等。组件是纯粹的数据容器,不包含任何逻辑。

    struct Position {
        float x;
        float y;
    };
    
    struct Velocity {
        float x;
        float y;
    };
  • System (系统): 包含处理组件的逻辑。例如,移动系统会根据位置和速度组件来更新实体的位置。

    class MovementSystem {
    public:
        void update(float deltaTime, std::vector<Entity>& entities,
                    std::unordered_map<Entity, Position>& positions,
                    std::unordered_map<Entity, Velocity>& velocities) {
            for (Entity entity : entities) {
                if (positions.count(entity) && velocities.count(entity)) {
                    positions[entity].x += velocities[entity].x * deltaTime;
                    positions[entity].y += velocities[entity].y * deltaTime;
                }
            }
        }
    };

接下来,你需要一个EntityManager来管理实体和组件之间的关系。EntityManager负责创建、销毁实体,以及添加、删除组件。

一个简单的EntityManager的实现可能是这样的:

class EntityManager {
public:
    Entity createEntity() {
        Entity entity = nextEntityId++;
        entities.push_back(entity);
        return entity;
    }

    void destroyEntity(Entity entity) {
        // TODO: Remove entity from all component maps
        // For simplicity, we'll just remove it from the entity list for now
        entities.erase(std::remove(entities.begin(), entities.end(), entity), entities.end());
    }

    template <typename T>
    void addComponent(Entity entity, T component) {
        componentStore[typeid(T)][entity] = component;
    }

    template <typename T>
    T& getComponent(Entity entity) {
        return std::any_cast<T&>(componentStore[typeid(T)][entity]);
    }

    template <typename T>
    bool hasComponent(Entity entity) {
        return componentStore.count(typeid(T)) && componentStore[typeid(T)].count(entity);
    }

private:
    Entity nextEntityId = 0;
    std::vector<Entity> entities;
    std::unordered_map<std::type_index, std::unordered_map<Entity, std::any>> componentStore;
};

这个EntityManager使用std::any来存储组件,这允许存储任何类型的组件,但需要进行类型转换。这是一种简单的方法,但在性能方面可能不是最优的。更高级的实现可能会使用类型擦除或者模板元编程来避免运行时的类型转换。

3D 游戏开发(Godot 4)
3D 游戏开发(Godot 4)

用 Godot 4 + GDScript 开发 3D 游戏的端到端工作流。触发场景:"做一个 3D 游戏"、 "第一人称/第三人称/FPS/TPS/3D 平台跳跃"、"用 Godot 做 3D"、"加个 3D 角色控制器/ 相机/敌人寻路/关卡"、"导入 glb 模型不显示"、"3D 场景太暗/太卡"、"make a 3D game"、 "first-per

下载

最后,你需要将所有的系统连接起来,在游戏循环中调用它们。

int main() {
    EntityManager entityManager;
    MovementSystem movementSystem;

    Entity player = entityManager.createEntity();
    entityManager.addComponent(player, Position{0.0f, 0.0f});
    entityManager.addComponent(player, Velocity{1.0f, 1.0f});

    // Game loop
    while (true) {
        float deltaTime = 0.1f; // Example delta time

        // Update systems
        movementSystem.update(deltaTime, entityManager.entities,
                              entityManager.getComponent<Position>(),
                              entityManager.getComponent<Velocity>());

        // Example: Print player's position
        std::cout << "Player Position: " << entityManager.getComponent<Position>(player).x << ", "
                  << entityManager.getComponent<Position>(player).y << std::endl;

        // ... other game logic ...
    }

    return 0;
}

这个例子非常简单,但它展示了ECS架构的基本思想。你可以根据自己的需求扩展它,例如添加更多的组件和系统,或者使用更高级的技术来优化性能。

如何优化C++ ECS架构的性能?

性能优化是ECS架构的关键。一些方法包括:

  • 使用稀疏集: 使用稀疏集来存储组件,可以提高组件的访问速度。
  • 使用SIMD指令: 使用SIMD指令可以并行处理多个实体的数据,从而提高系统的执行速度。
  • 数据局部性: 尽量将相关的数据存储在连续的内存中,可以提高缓存命中率。
  • 避免虚函数: 虚函数会带来性能开销,尽量避免在ECS架构中使用虚函数。
  • 使用编译时多态: 使用模板元编程来实现编译时多态,可以避免运行时的虚函数调用开销。
template <typename ComponentType>
class ComponentArray {
public:
    void addComponent(Entity entity, ComponentType component) {
        components[entity] = component;
    }

    ComponentType& getComponent(Entity entity) {
        return components[entity];
    }

    bool hasComponent(Entity entity) const {
        return components.count(entity) > 0;
    }

private:
    std::unordered_map<Entity, ComponentType> components;
};

template <typename... ComponentTypes>
class Archetype {
public:
    template <typename Entity>
    std::tuple<ComponentTypes&...> getComponents(Entity entity) {
        return std::tie(componentArrays[typeid(ComponentTypes)]->getComponent(entity)...);
    }

private:
    std::unordered_map<std::type_index, ComponentArrayBase*> componentArrays;
};

ECS架构与其他游戏开发模式有什么区别?

ECS架构与传统的面向对象编程(OOP)有很大的区别。OOP通常将数据和行为封装在一个对象中,而ECS将数据和行为分离开来。这使得ECS架构更加灵活和可扩展。

此外,ECS架构也与数据导向设计(Data-Oriented Design, DOD)密切相关。DOD强调数据的组织方式对性能的影响,而ECS架构正是DOD的一个实践。

  • OOP: 对象包含数据和方法,强调封装和继承。
  • ECS: 数据和逻辑分离,强调组合和系统。
  • DOD: 关注数据在内存中的布局和访问模式,以优化性能。

选择哪种架构取决于项目的具体需求。对于小型项目,OOP可能更简单易用。对于大型项目,ECS架构可能更灵活和可维护。

如何在大型游戏项目中应用ECS架构?

在大型游戏项目中应用ECS架构需要仔细的规划和设计。一些建议包括:

  • 逐步采用: 不要试图一次性将整个项目都迁移到ECS架构。可以先从一个小的模块开始,逐步扩展到整个项目。
  • 定义清晰的组件: 定义清晰的组件是ECS架构的关键。组件应该尽可能的小而简单,只包含必要的数据。
  • 使用事件系统: 使用事件系统来解耦系统之间的依赖关系。
  • 使用代码生成: 使用代码生成工具可以自动生成大量的样板代码,从而提高开发效率。

在大型项目中,可以使用更高级的ECS库,例如EnTT或者flecs。这些库提供了更多的功能和优化,可以帮助你更好地构建ECS架构。

例如,使用EnTT,你可以这样定义组件和系统:

#include <entt/entt.hpp>

struct Position {
    float x;
    float y;
};

struct Velocity {
    float x;
    float y;
};

void movementSystem(entt::registry& registry, float deltaTime) {
    registry.view<Position, Velocity>().each([&](auto entity, auto& position, auto& velocity) {
        position.x += velocity.x * deltaTime;
        position.y += velocity.y * deltaTime;
    });
}

int main() {
    entt::registry registry;

    auto entity = registry.create();
    registry.emplace<Position>(entity, 0.0f, 0.0f);
    registry.emplace<Velocity>(entity, 1.0f, 1.0f);

    // Game loop
    while (true) {
        float deltaTime = 0.1f;
        movementSystem(registry, deltaTime);

        // Example: Print player's position
        auto& position = registry.get<Position>(entity);
        std::cout << "Player Position: " << position.x << ", " << position.y << std::endl;
    }

    return 0;
}

EnTT提供了一个简洁的API,可以方便地创建实体、添加组件和定义系统。它还提供了许多优化,例如基于类型的存储和快速的迭代器。

总而言之,ECS架构是一种强大的游戏开发模式,可以提高代码的灵活性、可扩展性和性能。虽然学习曲线可能比较陡峭,但掌握它将使你成为一个更优秀的C++游戏开发者。

热门AI工具

更多
函子Zion

一款支持AI搭建、可视化编辑和多端发布的全栈无代码开发平台。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

创客贴AI

创客贴AI是一款AI图像与设计工具,AI辅助的智能在线设计工具。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
go语言 面向对象
go语言 面向对象

本专题整合了go语言面向对象相关内容,阅读专题下面的文章了解更多详细内容。

7735

2025.09.05

java面向对象
java面向对象

本专题整合了java面向对象相关内容,阅读专题下面的文章了解更多详细内容。

382

2025.11.27

C++ 面向对象编程与设计模式实践
C++ 面向对象编程与设计模式实践

本专题整合了 C++ 面向对象编程的核心机制,涵盖类的定义与访问控制、构造函数与析构函数、拷贝语义与移动语义、继承与虚函数多态、抽象类与接口、运算符重载、友元函数等关键知识点,同时结合单例模式、工厂模式、观察者模式、策略模式等常用设计模式的 C++ 实现,帮助开发者写出结构清晰、易于维护的面向对象代码。

384

2026.04.09

Java 面向对象与核心编程机制
Java 面向对象与核心编程机制

系统讲解 Java 面向对象编程的核心知识体系,涵盖类的定义与封装、构造方法与 this/super 关键字、继承与方法重写、多态与向上向下转型、抽象类与接口的区别与设计原则、异常处理机制(try-catch-finally / 自定义异常)、泛型的类型参数与通配符、集合框架(List/Set/Map)的使用与遍历方式,帮助开发者全面掌握 Java 程序设计的核心机制与面向对象思维。

263

2026.04.17

java多态详细介绍
java多态详细介绍

本专题整合了java多态相关内容,阅读专题下面的文章了解更多详细内容。

375

2025.11.27

mysql标识符无效错误怎么解决
mysql标识符无效错误怎么解决

mysql标识符无效错误的解决办法:1、检查标识符是否被其他表或数据库使用;2、检查标识符是否包含特殊字符;3、使用引号包裹标识符;4、使用反引号包裹标识符;5、检查MySQL的配置文件等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

17900

2023.12.04

Python标识符有哪些
Python标识符有哪些

Python标识符有变量标识符、函数标识符、类标识符、模块标识符、下划线开头的标识符、双下划线开头、双下划线结尾的标识符、整型标识符、浮点型标识符等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1263

2024.02.23

java标识符合集
java标识符合集

本专题整合了java标识符相关内容,想了解更多详细内容,请阅读下面的文章。

9610

2025.06.11

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
阶跃Al官方手册
阶跃Al官方手册

共0课时 | 0人学习

Webman中文手册
Webman中文手册

共0课时 | 0人学习

C++教程
C++教程

共115课时 | 44.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn