如何用C++实现一个简单的ECS架构?C++游戏开发设计模式【游戏架构】
技术百科
尼克
发布时间:2026-01-13
浏览: 次 Entity是uint32_t ID,无数据无虚函数;Component为POD结构体,纯数据无逻辑;System只声明所需组件类型,由World按签名筛选实体并调用update;World统一管理生命周期、存储与调度。
用C++实现一个简单的ECS(Entity-Component-System)架构,核心是把数据(Component)和逻辑(System)彻底分离,Entity只作为ID存在。不需要复杂模板或宏,从零开始也能写出清晰、可扩展的版本。
Entity:轻量ID,不存任何数据
Entity本质就是一个整数ID(比如uint32_t),用于关联组件。避免用类包装,防止误加成员或虚函数破坏纯数据性。
- 用连续递增ID(配合回收池)管理生命周期,避免ID爆炸
- 不继承、不虚函数、不存储指针——只是一个“标签”
- 示例:using Entity = uint32_t;,配合Entity null_entity = 0;表示无效
Component:纯数据结构,无函数无继承
每个Component是POD(Plain Old
Data)结构体,只含字段,不带方法、不依赖其他组件、不涉及内存管理逻辑。
- 例如:struct Position { float x, y; };、struct Velocity { float dx, dy; };
- 所有Component类型在编译期注册(如用类型索引映射到数组下标),便于系统快速定位
- 组件存储推荐用SoA(Structure of Arrays)或紧密打包的AoS(Array of Structs),优先考虑缓存友好性
System:按需遍历,专注逻辑
System不持有Entity或Component,只声明它需要哪些Component类型(如“处理所有同时拥有Position和Velocity的Entity”),运行时由World调度匹配的实体集合。
- 每个System重载void update(float dt),内部用迭代器或索引批量访问对应组件数组
- World负责维护组件容器(如std::vector
)、建立Entity→Component索引(如稀疏数组或哈希表) - 避免在System里做动态内存分配;更新顺序由开发者显式控制(如先VelocitySystem,再PositionSystem)
World:协调者,不参与业务逻辑
World是ECS的中枢,管理Entity生命周期、组件存储、System注册与执行。它本身不包含游戏规则,只提供createEntity()、addComponent
- 组件容器可用std::array<:unique_ptr>, MAX_COMPONENT_TYPES>统一管理不同类型
- Entity到组件的映射可用std::vector<:array max_component_types>>(稀疏索引)或更紧凑的位掩码+间接索引
- System执行时,World按签名(如Signature{has
(), has )筛选出匹配Entity列表,传给对应System()}
基本上就这些。不需要反射、不强求完美解耦,先跑通“添加组件→写System→World驱动”闭环,再逐步优化内存布局和查询性能。ECS的价值不在炫技,而在让数据可见、逻辑可测、变更可控。
# ai
# 闭环
# 而在
# 也能
# 所需
# 不需要
# 游戏开发
# 数据结构
# 只是一个
# c++
# void
# 指针
# 接口
# 架构
# 不带
# 结构体
# 继承
# Struct
# 虚函数
# 遍历
# Float
# Array
# using
# position
# 只提供
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- 如何高效识别并拦截拼接式恶意域名 spam
- Win11怎么设置开机自动连接宽带_Windows
- c++怎么设置线程优先级与cpu亲和性_c++ 多
- Windows10电脑怎么设置防火墙出站规则_Wi
- c# 如何深拷贝和浅拷贝
- Win11怎么设置虚拟内存最佳大小_Windows
- php查询数据怎么导出csv_查询结果转csv文件
- Windows蓝屏错误0x00000023怎么修复
- Win10怎样清理C盘阿里旺旺缓存_Win10清理
- Win10路由器怎么隐藏ssid Win10隐藏w
- Win11如何设置文件权限 Win11 NTFS文
- Python数据挖掘进阶教程_分类回归与聚类案例解
- Win11怎么关闭系统推荐内容_Windows11
- Win11麦克风没声音怎么设置_Win11麦克风权
- php下载安装后swoole扩展怎么安装_异步框架
- Mac电脑进水了怎么办_MacBook进水后紧急处
- 如何使用Golang table-driven f
- 如何使用Golang构建简易投票统计功能_Gola
- Python网络异常模拟_测试说明【指导】
- MAC怎么在照片中添加水印_MAC自带编辑工具文字
- php订单日志权限怎么设_php订单日志文件权限设
- 如何使用Golang捕获测试日志_Golang t
- Win11怎么更改默认打开方式_Win11关联文件
- Windows 10怎么隐藏特定更新补丁_Wind
- php中self::能调用子类重写的方法吗_静态绑
- 企业SEO优化选择网站建设模板的技巧
- php打包exe后无法读取环境变量_变量配置方法【
- Windows10如何重置此电脑_Windows1
- Win11怎么更改任务栏颜色_Windows11个
- 为什么本地php环境运行php脚本卡顿_php执行
- Win11怎么关闭任务栏小组件_Windows11
- Mac怎么进行语音输入_Mac听写功能设置与使用【
- 如何使用Golang实现Web表单数据绑定_自动映
- Win11玩游戏全屏闪退怎么办_Win11全屏优化
- mac怎么安装adb_MAC配置Android A
- 如何使用Golang处理静态文件缓存_提高页面加载
- MAC如何隐藏文件夹及文件_MAC终端命令隐藏与第
- 如何使用Golang实现云原生应用弹性伸缩_自动应
- 如何使用Golang模拟请求超时_Golang c
- 如何使用Golang实现微服务状态监控_Golan
- Windows10系统怎么查看硬盘健康_Win10
- Python字符串操作教程_切片拼接与格式化详解
- Python对象生命周期管理_创建销毁解析【教程】
- Win11怎么设置麦克风权限_允许应用访问Win1
- Win11怎样安装剪映专业版_Win11安装剪映教
- c++如何用AFL++进行模糊测试 c++ Fuz
- php订单日志怎么按状态筛选_php筛选不同状态订
- Go 语言标准库为何不提供泛型切片的 Contai
- php485函数执行慢怎么优化_php485性能提
- 微信企业付款回调PHP怎么接收_处理企业付款异步通

QQ客服