- 游戏开发
【免费下载链接】flecs
A fast entity component system (ECS) for C & C++
Flecs 是一个面向 C 与 C++ 的快速实体组件系统(ECS),在 Bazel 生态中可通过 Bzlmod 以模块化方式直接引入。本文基于仓库中 examples/build/bazel/README.md 这一官方指南,完整讲解如何在你的 Bazel 工程中声明 flecs 依赖、通过git_override锁定源码版本、用bazel run //example跑通一个可用的 ECS 演示程序,并给出结合 CI(如 Renovate)保持依赖自动更新的实践方案。读完本文,你将能独立完成一个基于 Bazel + Flecs 的 C++ 项目搭建,并理解依赖解析与构建目标的底层结构。
一、为什么选择 Bazel 模块方式引入 Flecs
Bazel 自 6.x 起默认启用 Bzlmod 依赖解析机制,第三方库不再依赖传统的WORKSPACE全量拉取,而是通过MODULE.bazel中的bazel_dep声明模块依赖。Flecs 在仓库根目录提供了完整的 Bazel 支持文件(MODULE.bazel、BUILD),这意味着:
- 你不需要手动把 flecs 的源码复制进自己的工程;
- 依赖版本通过模块声明集中管理,可复现、可升级;
- 仓库根目录的 BUILD 文件已经把整个 flecs 编译为一个公开的
cc_library目标(name = "flecs"),你的工程直接依赖它即可,无需自己编写构建规则。
从根目录 BUILD 的源码结构看,该cc_library目标将src/**/*.c、include/**/*.h等全部源码与头文件打包,并把include目录加入编译搜索路径,同时按平台选择 C++17 标准(Windows 下为/std:c++17,其他平台为-std=c++17),这正是 Flecs C++ API 所需的编译环境。
二、在 MODULE.bazel 中声明 Flecs 依赖
在你的项目根目录(即包含MODULE.bazel文件的目录)中,加入以下依赖声明:
bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )这里有两层含义,值得逐一说明:
bazel_dep(name = "flecs"):向 Bazel 声明本工程依赖名为flecs的模块;git_override:由于 flecs 尚未以 registry 形式发布,需要指定 Git 仓库地址(remote)并锁定精确提交(commit),Bazel 会基于该提交构建模块。锁定 commit 是 Bazel 可复现构建的关键——换一台机器或 CI 上,构建结果不会因为上游更新而漂移。
参照仓库中官方示例 examples/build/bazel/MODULE.bazel,一个完整可用的模块文件如下:
"Example for using the flecs as a dependency using bazel" module(name = "flecs_bazel_example", version = "1.0.0") bazel_dep(name = "rules_cc", version = "0.0.9") bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )注意示例中同时声明了rules_cc模块:Bzlmod 模式下 C++ 规则通过rules_cc提供,bazel_dep(name = "rules_cc", version = "0.0.9")显式固定了其版本,避免 Bazel 自动选择兼容版本时产生不确定性。
三、在 BUILD 文件中使用 @flecs
声明模块依赖后,就可以在自己的BUILD文件中引用 flecs 提供的目标。仓库示例 examples/build/bazel/example/BUILD 展示了最直接的方式:
cc_binary( name = "example", srcs = ["main.cpp"], deps = ["@flecs"], copts = select({ "@bazel_tools//src/conditions:windows": ["/std:c++17"], "//conditions:default": ["-std=c++17"], }), )要点解析:
deps = ["@flecs"]:@flecs即通过bazel_dep引入的外部仓库中的默认目标。由于根目录 BUILD 将cc_library命名为flecs并设置了visibility = ["//visibility:public"],它对外部工程完全可见,可直接出现在deps中;copts使用select()按平台指定 C++17:这与 flecs 自身 BUILD 的编译选项保持一致,确保你的二进制与 flecs 使用相同的语言标准;cc_binary会把你编写的main.cpp与 flecs 静态库链接为可执行文件。
四、运行示例:bazel run //example
仓库在 examples/build/bazel/example/main.cpp 提供了一个完整的 C++ ECS 演示程序,验证了依赖链路可用。在你的 Bazel 工程目录(包含上面BUILD文件的目录)执行:
bazel run //exampleBazel 会自动完成依赖解析、从 Git 拉取 flecs 源码、编译并运行。该示例程序本身演示了 Flecs 最核心的用法,可作为你上手 ECS 的模板:
#include "flecs.h" #include <iostream> /* Component types */ struct Position { double x, y; }; struct Velocity { double x, y; }; int main(int argc, char *argv[]) { flecs::world world(argc, argv); world.system<Position, const Velocity>() .each([](flecs::entity e, Position& p, const Velocity& v) { p.x += v.x; p.y += v.y; std::cout << "Moved " << e.name() << " to {" << p.x << ", " << p.y << "}" << std::endl; }); world.entity("MyEntity") .set<Position>({0, 0}) .set<Velocity>({1, 1}); world.set_target_fps(1); std::cout << "Application move_system is running, press CTRL-C to exit..." << std::endl; /* Run systems */ while (world.progress()) { } }运行后可观察到:系统每一帧把MyEntity的Position按Velocity累加并打印坐标。这里值得注意的 C++ API 细节包括:
world.system<Position, const Velocity>()声明了一个订阅Position与只读Velocity的系统,const限定表达了组件只读语义;.each(...)以 lambda 形式对每个匹配实体执行逻辑;world.entity("MyEntity").set<Position>({0, 0}).set<Velocity>({1, 1})创建带命名实体并初始化两个组件;world.set_target_fps(1)将帧率限制为 1 FPS,方便观察逐步移动。
五、通过 Renovate + CI 保持依赖自动更新
使用git_override锁定 commit 后,flecs 版本更新需要手动修改 commit。指南推荐结合 Renovate 与 CI 实现自动化:Renovate 检测到 flecs 上游新提交后自动发起更新 PR,CI 验证构建,必要时自动合并。
第一步:为 bazel_dep 显式声明版本属性。
bazel_dep(name = "flecs") git_override( module_name = "flecs", remote = "https://github.com/SanderMertens/flecs.git", commit = "02c8c2666b22ccce5706c8f16efaf813704fe31e", )虽然 README 中的示例没有为bazel_dep写version,但指南明确指出:为了让 Renovate 能追踪更新,必须在bazel_dep上声明版本属性(例如bazel_dep(name = "flecs", version = "4.0.0"))。Renovate 据此判断当前使用版本并比对上游最新版本,进而自动生成升级 PR。
第二步:配置 Renovate。
在仓库的.github/renovate.json5中加入如下配置:
{ "extends": ["config:recommended"], "dependencyDashboard": true, "automerge": true, }参数含义:
"extends": ["config:recommended"]:采用 Renovate 官方推荐的预设规则;"dependencyDashboard": true:启用 Dependency Dashboard,在仓库 Issue 中集中展示所有待更新依赖及其状态,便于人工介入;"automerge": true:当更新 PR 通过 CI 检查后自动合并。若你的 CI 使用 merge queue 管理合并,Renovate 会配合 merge queue 自动完成 CI 触发与合并流程,实现依赖升级的全自动化。
六、源码级验证:flecs 的 Bazel 目标结构
为了让你对“@flecs 到底构建了什么”有清晰认知,这里把仓库中与 Bazel 相关的核心文件串联起来:
| 文件 | 作用 |
|---|---|
| MODULE.bazel | flecs 自身的 Bazel 模块声明(module(name = "flecs")),同时用git_repository引入构建测试所需的bake依赖 |
| BUILD | 定义对外公开的flecscc_library 目标:源码 glob 覆盖src/**/*.c、src/**/*.h、src/**/*.inl,头文件覆盖include/**/*.h、include/**/*.hpp、include/**/*.inl,includes = ["include"]提供头文件搜索路径,并按平台启用 C++17 |
| examples/build/bazel/MODULE.bazel | 官方示例模块:声明flecs与rules_cc依赖,并通过git_override锁定具体 commit |
| examples/build/bazel/example/BUILD | 官方示例可执行目标:cc_binary,deps = ["@flecs"] |
| examples/build/bazel/example/main.cpp | 示例 ECS 程序:Position/Velocity 组件 + 移动系统 + 实体创建 + 主循环 |
| WORKSPACE.bzlmod | 兼容 Bzlmod 模式的 WORKSPACE 存根 |
从 BUILD 的srcs = glob(["src/**/*.c", ...])可以看出,flecs 的 Bazel 目标采用全量源码编译方式:它与 CMake 构建(见 CMakeLists.txt)使用同一份src/与include/源码树,保证了不同构建系统下行为一致。Flecs 是单一头文件 API(include/flecs.h)与多源文件实现的组合结构,因此 Bazel 目标的hdrs会同时包含include/**/*.hpp与*.inl,确保 C++ API 的内联实现也能被正确打包。
七、常见问题与注意事项
- 必须使用 Bzlmod 模式:本方案基于
MODULE.bazel的 Bzlmod 依赖解析,请确认 Bazel 版本支持 Bzlmod(Bazel 6+ 默认启用,Bazel 7 完全默认)。老式WORKSPACE工程可参考 WORKSPACE.bzlmod 迁移; git_override的 commit 必须存在:commit 值02c8c2666b22ccce5706c8f16efaf813704fe31e是示例锁定的版本,实际使用时应替换为你验证过的最新提交,以保证可复现构建;- C++17 一致性:示例
BUILD与 flecs 自身BUILD都通过select()设置 C++17,若你的目标使用更低标准,需自行调整copts,但应知晓 Flecs 的 C++ API 依赖 C++17 特性; - 版本属性与 Renovate:若计划接入 Renovate 自动升级,务必在
bazel_dep上声明version属性,否则 Renovate 无法识别当前版本、无法生成升级 PR; - include 路径:示例源码中直接
#include "flecs.h",因为 flecs 目标的includes = ["include"]已把 flecs 的 include 目录加入搜索路径,flecs.h实际位于 include/flecs.h。
结语
通过 Bzlmod 的bazel_dep+git_override组合,Flecs ECS 可以被干净、可复现地接入任何 Bazel 工程:依赖声明只需数行配置,@flecs目标直接提供完整 C/C++ API,配合bazel run //example即可快速验证。在此基础上叠加 Renovate 与 CI,还能实现依赖的持续自动升级,让 ECS 基础设施的维护成本降到最低。仓库中 examples/build/bazel 目录提供了可直接参考的完整示例,是上手 Bazel + Flecs 的最佳起点。
- 游戏开发
【免费下载链接】flecs
A fast entity component system (ECS) for C & C++
相关推荐
Bazel Bzlmod 迁移完全指南:从 WORKSPACE 平滑切换到 MODULE.bazel
Bazel Bzlmod 迁移完全指南:从 WORKSPACE 平滑切换到 MODULE.bazel 本文是 Bazel 官方 Bzlmod 迁移指南(对应仓库
构建工具Bazel Bzlmod 迁移工具(migrate_to_bzlmod)实战指南:从 WORKSPACE 平滑迁移到 MODULE.bazel
Bazel Bzlmod 迁移工具(migrate_to_bzlmod)实战指南:从 WORKSPACE 平滑迁移到 MODULE.bazel 在 Bazel
构建工具FTXUI 的 Bazel 集成指南:使用 Bzlmod 构建 C++ 终端 UI 应用
FTXUI 的 Bazel 集成指南:使用 Bzlmod 构建 C++ 终端 UI 应用 本篇技术指南聚焦于在 Bazel 构建系统中通过 Bzlmod(Baz
UI组件
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考